AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
tlb.c
Go to the documentation of this file.
1
29
30#include <core/system.h>
31#include <core/arch.h>
32#include <cpu/cpu.h>
33#include <mm/mm.h>
34#include <mm/pmap.h>
35#include <mm/vm.h>
36#include <platform/misc.h>
37#include <sys/cpu.h>
38#include <sys/lock.h>
39
40/* above this many pages, the whole TLB is flushed instead */
41#define FLUSH_PAGES_MAX 32
42
43#define ICR_ASSERT 0x4000
44
45/* how many times the kernel's mappings changed; each CPU's TLB has
46 * the kernel's mappings of one of these generations
47 * (cpu->arch.kernel_gen) */
49
50/* the request to the other CPUs: flush [sva, eva) of pmap, or leave pmap
51 * for the kernel's page tables */
52static struct {
53 struct pmap *pmap;
55 int leave;
56 volatile uint32_t pending; /* the CPUs that haven't done it yet */
58
60static void flush(vaddr_t sva, vaddr_t eva)
61{
62 if ((eva - sva) / PAGE_SIZE > FLUSH_PAGES_MAX) {
63 tlb_flush();
64 return;
65 }
66
67 for (vaddr_t va = sva; va < eva; va += PAGE_SIZE)
68 asm volatile("invlpg (%0)" :: "r"(va) : "memory");
69}
70
73static void request_do(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
74{
75 struct pmap *loaded = curcpu()->arch.pmap;
76
77 if (leave) {
78 if (loaded == pmap)
80 } else if (pmap == kvm_space.pmap || loaded == pmap) {
81 flush(sva, eva);
82 }
83}
84
87void x86_tlb_ipi(void)
88{
89 uint32_t me = 1U << curcpu()->id;
90
91 if (!(__atomic_load_n(&request.pending, __ATOMIC_ACQUIRE) & me))
92 return;
93
94 request_do(request.pmap, request.sva, request.eva, request.leave);
95 curcpu()->arch.tlb_flushes++;
96 __atomic_and_fetch(&request.pending, ~me, __ATOMIC_RELEASE);
97}
98
99/* Runs in the kernel lock's wait loop: does a pending TLB request, so
100 * that the CPU holding the lock isn't kept waiting for this one. */
102{
103 x86_tlb_ipi();
104 asm volatile("pause");
105}
106
107/* Runs when the running CPU takes the kernel lock: flushes its TLB if
108 * the kernel's mappings changed since it last held the lock. */
110{
111 struct cpu *cpu = curcpu();
112
113 if (cpu->arch.kernel_gen != kernel_gen) {
114 tlb_flush();
116 }
117}
118
119/*
120 * Flushes a range of an address space from every TLB that may have it,
121 * after its mappings changed, and waits until the other CPUs have done
122 * it. The caller holds the kernel lock.
123 */
124void tlb_shootdown(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
125{
126 uint32_t me = 1U << curcpu()->id;
127
128 request_do(pmap, sva, eva, leave);
129
130 /* the kernel's mappings: the other CPUs flush when they take the
131 * lock */
132 if (pmap == kvm_space.pmap) {
133 curcpu()->arch.kernel_gen = ++kernel_gen;
134 return;
135 }
136
137 uint32_t others = __atomic_load_n(&pmap->cpus, __ATOMIC_ACQUIRE) & ~me;
138
139 if (!others)
140 return;
141
142 request.pmap = pmap;
143 request.sva = sva;
144 request.eva = eva;
145 request.leave = leave;
146 __atomic_store_n(&request.pending, others, __ATOMIC_RELEASE);
147
148 for (int i = 0; i < cpus_count; ++i)
149 if (others & (1U << i))
151
152 while (__atomic_load_n(&request.pending, __ATOMIC_ACQUIRE))
153 asm volatile("pause");
154}
155
156/*
157 * Records that the running CPU switched page tables, so that TLB requests
158 * go to the CPUs that may have a mapping cached.
159 */
160void pmap_loaded(struct pmap *from, struct pmap *to)
161{
162 uint32_t me = 1U << curcpu()->id;
163
164 if (from)
165 __atomic_and_fetch(&from->cpus, ~me, __ATOMIC_RELEASE);
166
167 __atomic_or_fetch(&to->cpus, me, __ATOMIC_RELEASE);
168}
void arch_lock_spin(void)
Runs in the kernel lock's wait loop, on each turn.
Definition smp.c:20
void arch_lock_taken(void)
Runs once this CPU has taken the kernel lock.
Definition smp.c:25
#define PAGE_SIZE
Definition system.h:7
static void tlb_flush(void)
Flushes every translation from this CPU's TLB.
Definition mm.h:43
uint64_t vaddr_t
Definition mm.h:40
void pmap_loaded(struct pmap *from, struct pmap *to)
Definition pmap.c:157
void tlb_shootdown(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
Definition pmap.c:130
void x86_tlb_ipi(void)
Does the pending request if it is for the running CPU.
Definition tlb.c:87
#define ICR_ASSERT
Definition smp.c:46
struct cpu cpus[CPUS_MAX]
Definition sched.c:28
int cpus_count
Definition sched.c:29
#define X86_TLB_VECTOR
Definition misc.h:15
void x86_lapic_ipi(uint32_t id, uint32_t icr)
Sends an IPI to a CPU, and waits until it is delivered.
Definition apic.c:342
struct pmap * pmap_switch(struct pmap *pmap)
Switches this CPU to an address space's page tables.
Definition pmap.c:164
unsigned int uint32_t
Definition stddef.h:22
unsigned long long int uint64_t
Definition stddef.h:23
struct pmap * pmap
volatile uint32_t pending
uint64_t kernel_gen
Definition cpu.h:135
uint32_t apic_id
Definition cpu.h:125
A processor, and what runs on it.
Definition cpu.h:11
struct arch_cpu arch
Definition cpu.h:17
An address space's page tables, on x86.
Definition pmap.h:9
volatile uint32_t cpus
Definition pmap.h:8
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
static void flush(vaddr_t sva, vaddr_t eva)
Flushes [sva, eva) from the running CPU's TLB.
Definition tlb.c:60
static struct @012362346107137024144356360042152075235052321043 request
static void request_do(struct pmap *pmap, vaddr_t sva, vaddr_t eva, int leave)
Does what a request asks of the running CPU: flush the range, if the CPU may have it,...
Definition tlb.c:73
static uint64_t kernel_gen
Definition tlb.c:48
#define FLUSH_PAGES_MAX
Definition tlb.c:41
struct vm_space kvm_space
Definition vmm.c:17