Commit 0b4e273fb83bce5dd8e166a4defb16ebdd215abf
Committed by
Linus Torvalds
1 parent
909e90d3c4
Exists in
master
and in
7 other branches
uml: customize tlb.h
Customize the hooks in tlb.h to optimize TLB flushing some more. Add start and end fields to tlb_gather_mmu, which are used to limit the address space range scanned when a region is unmapped. The interfaces which just free page tables, without actually changing mappings, don't need to cause a TLB flush. Signed-off-by: Jeff Dike <jdike@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 2 changed files with 139 additions and 8 deletions Inline Diff
arch/um/kernel/tlb.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | 2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
3 | * Licensed under the GPL | 3 | * Licensed under the GPL |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <linux/mm.h> | 6 | #include <linux/mm.h> |
7 | #include <linux/sched.h> | 7 | #include <linux/sched.h> |
8 | #include <asm/pgtable.h> | 8 | #include <asm/pgtable.h> |
9 | #include <asm/tlbflush.h> | 9 | #include <asm/tlbflush.h> |
10 | #include "as-layout.h" | 10 | #include "as-layout.h" |
11 | #include "mem_user.h" | 11 | #include "mem_user.h" |
12 | #include "os.h" | 12 | #include "os.h" |
13 | #include "skas.h" | 13 | #include "skas.h" |
14 | #include "tlb.h" | 14 | #include "tlb.h" |
15 | 15 | ||
16 | struct host_vm_change { | 16 | struct host_vm_change { |
17 | struct host_vm_op { | 17 | struct host_vm_op { |
18 | enum { NONE, MMAP, MUNMAP, MPROTECT } type; | 18 | enum { NONE, MMAP, MUNMAP, MPROTECT } type; |
19 | union { | 19 | union { |
20 | struct { | 20 | struct { |
21 | unsigned long addr; | 21 | unsigned long addr; |
22 | unsigned long len; | 22 | unsigned long len; |
23 | unsigned int prot; | 23 | unsigned int prot; |
24 | int fd; | 24 | int fd; |
25 | __u64 offset; | 25 | __u64 offset; |
26 | } mmap; | 26 | } mmap; |
27 | struct { | 27 | struct { |
28 | unsigned long addr; | 28 | unsigned long addr; |
29 | unsigned long len; | 29 | unsigned long len; |
30 | } munmap; | 30 | } munmap; |
31 | struct { | 31 | struct { |
32 | unsigned long addr; | 32 | unsigned long addr; |
33 | unsigned long len; | 33 | unsigned long len; |
34 | unsigned int prot; | 34 | unsigned int prot; |
35 | } mprotect; | 35 | } mprotect; |
36 | } u; | 36 | } u; |
37 | } ops[1]; | 37 | } ops[1]; |
38 | int index; | 38 | int index; |
39 | struct mm_id *id; | 39 | struct mm_id *id; |
40 | void *data; | 40 | void *data; |
41 | int force; | 41 | int force; |
42 | }; | 42 | }; |
43 | 43 | ||
44 | #define INIT_HVC(mm, force) \ | 44 | #define INIT_HVC(mm, force) \ |
45 | ((struct host_vm_change) \ | 45 | ((struct host_vm_change) \ |
46 | { .ops = { { .type = NONE } }, \ | 46 | { .ops = { { .type = NONE } }, \ |
47 | .id = &mm->context.id, \ | 47 | .id = &mm->context.id, \ |
48 | .data = NULL, \ | 48 | .data = NULL, \ |
49 | .index = 0, \ | 49 | .index = 0, \ |
50 | .force = force }) | 50 | .force = force }) |
51 | 51 | ||
52 | static int do_ops(struct host_vm_change *hvc, int end, | 52 | static int do_ops(struct host_vm_change *hvc, int end, |
53 | int finished) | 53 | int finished) |
54 | { | 54 | { |
55 | struct host_vm_op *op; | 55 | struct host_vm_op *op; |
56 | int i, ret = 0; | 56 | int i, ret = 0; |
57 | 57 | ||
58 | for (i = 0; i < end && !ret; i++) { | 58 | for (i = 0; i < end && !ret; i++) { |
59 | op = &hvc->ops[i]; | 59 | op = &hvc->ops[i]; |
60 | switch(op->type) { | 60 | switch(op->type) { |
61 | case MMAP: | 61 | case MMAP: |
62 | ret = map(hvc->id, op->u.mmap.addr, op->u.mmap.len, | 62 | ret = map(hvc->id, op->u.mmap.addr, op->u.mmap.len, |
63 | op->u.mmap.prot, op->u.mmap.fd, | 63 | op->u.mmap.prot, op->u.mmap.fd, |
64 | op->u.mmap.offset, finished, &hvc->data); | 64 | op->u.mmap.offset, finished, &hvc->data); |
65 | break; | 65 | break; |
66 | case MUNMAP: | 66 | case MUNMAP: |
67 | ret = unmap(hvc->id, op->u.munmap.addr, | 67 | ret = unmap(hvc->id, op->u.munmap.addr, |
68 | op->u.munmap.len, finished, &hvc->data); | 68 | op->u.munmap.len, finished, &hvc->data); |
69 | break; | 69 | break; |
70 | case MPROTECT: | 70 | case MPROTECT: |
71 | ret = protect(hvc->id, op->u.mprotect.addr, | 71 | ret = protect(hvc->id, op->u.mprotect.addr, |
72 | op->u.mprotect.len, op->u.mprotect.prot, | 72 | op->u.mprotect.len, op->u.mprotect.prot, |
73 | finished, &hvc->data); | 73 | finished, &hvc->data); |
74 | break; | 74 | break; |
75 | default: | 75 | default: |
76 | printk(KERN_ERR "Unknown op type %d in do_ops\n", | 76 | printk(KERN_ERR "Unknown op type %d in do_ops\n", |
77 | op->type); | 77 | op->type); |
78 | break; | 78 | break; |
79 | } | 79 | } |
80 | } | 80 | } |
81 | 81 | ||
82 | return ret; | 82 | return ret; |
83 | } | 83 | } |
84 | 84 | ||
85 | static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len, | 85 | static int add_mmap(unsigned long virt, unsigned long phys, unsigned long len, |
86 | unsigned int prot, struct host_vm_change *hvc) | 86 | unsigned int prot, struct host_vm_change *hvc) |
87 | { | 87 | { |
88 | __u64 offset; | 88 | __u64 offset; |
89 | struct host_vm_op *last; | 89 | struct host_vm_op *last; |
90 | int fd, ret = 0; | 90 | int fd, ret = 0; |
91 | 91 | ||
92 | fd = phys_mapping(phys, &offset); | 92 | fd = phys_mapping(phys, &offset); |
93 | if (hvc->index != 0) { | 93 | if (hvc->index != 0) { |
94 | last = &hvc->ops[hvc->index - 1]; | 94 | last = &hvc->ops[hvc->index - 1]; |
95 | if ((last->type == MMAP) && | 95 | if ((last->type == MMAP) && |
96 | (last->u.mmap.addr + last->u.mmap.len == virt) && | 96 | (last->u.mmap.addr + last->u.mmap.len == virt) && |
97 | (last->u.mmap.prot == prot) && (last->u.mmap.fd == fd) && | 97 | (last->u.mmap.prot == prot) && (last->u.mmap.fd == fd) && |
98 | (last->u.mmap.offset + last->u.mmap.len == offset)) { | 98 | (last->u.mmap.offset + last->u.mmap.len == offset)) { |
99 | last->u.mmap.len += len; | 99 | last->u.mmap.len += len; |
100 | return 0; | 100 | return 0; |
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
104 | if (hvc->index == ARRAY_SIZE(hvc->ops)) { | 104 | if (hvc->index == ARRAY_SIZE(hvc->ops)) { |
105 | ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0); | 105 | ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0); |
106 | hvc->index = 0; | 106 | hvc->index = 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | hvc->ops[hvc->index++] = ((struct host_vm_op) | 109 | hvc->ops[hvc->index++] = ((struct host_vm_op) |
110 | { .type = MMAP, | 110 | { .type = MMAP, |
111 | .u = { .mmap = { .addr = virt, | 111 | .u = { .mmap = { .addr = virt, |
112 | .len = len, | 112 | .len = len, |
113 | .prot = prot, | 113 | .prot = prot, |
114 | .fd = fd, | 114 | .fd = fd, |
115 | .offset = offset } | 115 | .offset = offset } |
116 | } }); | 116 | } }); |
117 | return ret; | 117 | return ret; |
118 | } | 118 | } |
119 | 119 | ||
120 | static int add_munmap(unsigned long addr, unsigned long len, | 120 | static int add_munmap(unsigned long addr, unsigned long len, |
121 | struct host_vm_change *hvc) | 121 | struct host_vm_change *hvc) |
122 | { | 122 | { |
123 | struct host_vm_op *last; | 123 | struct host_vm_op *last; |
124 | int ret = 0; | 124 | int ret = 0; |
125 | 125 | ||
126 | if (hvc->index != 0) { | 126 | if (hvc->index != 0) { |
127 | last = &hvc->ops[hvc->index - 1]; | 127 | last = &hvc->ops[hvc->index - 1]; |
128 | if ((last->type == MUNMAP) && | 128 | if ((last->type == MUNMAP) && |
129 | (last->u.munmap.addr + last->u.mmap.len == addr)) { | 129 | (last->u.munmap.addr + last->u.mmap.len == addr)) { |
130 | last->u.munmap.len += len; | 130 | last->u.munmap.len += len; |
131 | return 0; | 131 | return 0; |
132 | } | 132 | } |
133 | } | 133 | } |
134 | 134 | ||
135 | if (hvc->index == ARRAY_SIZE(hvc->ops)) { | 135 | if (hvc->index == ARRAY_SIZE(hvc->ops)) { |
136 | ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0); | 136 | ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0); |
137 | hvc->index = 0; | 137 | hvc->index = 0; |
138 | } | 138 | } |
139 | 139 | ||
140 | hvc->ops[hvc->index++] = ((struct host_vm_op) | 140 | hvc->ops[hvc->index++] = ((struct host_vm_op) |
141 | { .type = MUNMAP, | 141 | { .type = MUNMAP, |
142 | .u = { .munmap = { .addr = addr, | 142 | .u = { .munmap = { .addr = addr, |
143 | .len = len } } }); | 143 | .len = len } } }); |
144 | return ret; | 144 | return ret; |
145 | } | 145 | } |
146 | 146 | ||
147 | static int add_mprotect(unsigned long addr, unsigned long len, | 147 | static int add_mprotect(unsigned long addr, unsigned long len, |
148 | unsigned int prot, struct host_vm_change *hvc) | 148 | unsigned int prot, struct host_vm_change *hvc) |
149 | { | 149 | { |
150 | struct host_vm_op *last; | 150 | struct host_vm_op *last; |
151 | int ret = 0; | 151 | int ret = 0; |
152 | 152 | ||
153 | if (hvc->index != 0) { | 153 | if (hvc->index != 0) { |
154 | last = &hvc->ops[hvc->index - 1]; | 154 | last = &hvc->ops[hvc->index - 1]; |
155 | if ((last->type == MPROTECT) && | 155 | if ((last->type == MPROTECT) && |
156 | (last->u.mprotect.addr + last->u.mprotect.len == addr) && | 156 | (last->u.mprotect.addr + last->u.mprotect.len == addr) && |
157 | (last->u.mprotect.prot == prot)) { | 157 | (last->u.mprotect.prot == prot)) { |
158 | last->u.mprotect.len += len; | 158 | last->u.mprotect.len += len; |
159 | return 0; | 159 | return 0; |
160 | } | 160 | } |
161 | } | 161 | } |
162 | 162 | ||
163 | if (hvc->index == ARRAY_SIZE(hvc->ops)) { | 163 | if (hvc->index == ARRAY_SIZE(hvc->ops)) { |
164 | ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0); | 164 | ret = do_ops(hvc, ARRAY_SIZE(hvc->ops), 0); |
165 | hvc->index = 0; | 165 | hvc->index = 0; |
166 | } | 166 | } |
167 | 167 | ||
168 | hvc->ops[hvc->index++] = ((struct host_vm_op) | 168 | hvc->ops[hvc->index++] = ((struct host_vm_op) |
169 | { .type = MPROTECT, | 169 | { .type = MPROTECT, |
170 | .u = { .mprotect = { .addr = addr, | 170 | .u = { .mprotect = { .addr = addr, |
171 | .len = len, | 171 | .len = len, |
172 | .prot = prot } } }); | 172 | .prot = prot } } }); |
173 | return ret; | 173 | return ret; |
174 | } | 174 | } |
175 | 175 | ||
176 | #define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1)) | 176 | #define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1)) |
177 | 177 | ||
178 | static inline int update_pte_range(pmd_t *pmd, unsigned long addr, | 178 | static inline int update_pte_range(pmd_t *pmd, unsigned long addr, |
179 | unsigned long end, | 179 | unsigned long end, |
180 | struct host_vm_change *hvc) | 180 | struct host_vm_change *hvc) |
181 | { | 181 | { |
182 | pte_t *pte; | 182 | pte_t *pte; |
183 | int r, w, x, prot, ret = 0; | 183 | int r, w, x, prot, ret = 0; |
184 | 184 | ||
185 | pte = pte_offset_kernel(pmd, addr); | 185 | pte = pte_offset_kernel(pmd, addr); |
186 | do { | 186 | do { |
187 | if ((addr >= STUB_START) && (addr < STUB_END)) | 187 | if ((addr >= STUB_START) && (addr < STUB_END)) |
188 | continue; | 188 | continue; |
189 | 189 | ||
190 | r = pte_read(*pte); | 190 | r = pte_read(*pte); |
191 | w = pte_write(*pte); | 191 | w = pte_write(*pte); |
192 | x = pte_exec(*pte); | 192 | x = pte_exec(*pte); |
193 | if (!pte_young(*pte)) { | 193 | if (!pte_young(*pte)) { |
194 | r = 0; | 194 | r = 0; |
195 | w = 0; | 195 | w = 0; |
196 | } else if (!pte_dirty(*pte)) { | 196 | } else if (!pte_dirty(*pte)) |
197 | w = 0; | 197 | w = 0; |
198 | } | 198 | |
199 | prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) | | 199 | prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) | |
200 | (x ? UM_PROT_EXEC : 0)); | 200 | (x ? UM_PROT_EXEC : 0)); |
201 | if (hvc->force || pte_newpage(*pte)) { | 201 | if (hvc->force || pte_newpage(*pte)) { |
202 | if (pte_present(*pte)) | 202 | if (pte_present(*pte)) |
203 | ret = add_mmap(addr, pte_val(*pte) & PAGE_MASK, | 203 | ret = add_mmap(addr, pte_val(*pte) & PAGE_MASK, |
204 | PAGE_SIZE, prot, hvc); | 204 | PAGE_SIZE, prot, hvc); |
205 | else ret = add_munmap(addr, PAGE_SIZE, hvc); | 205 | else |
206 | } | 206 | ret = add_munmap(addr, PAGE_SIZE, hvc); |
207 | else if (pte_newprot(*pte)) | 207 | } else if (pte_newprot(*pte)) |
208 | ret = add_mprotect(addr, PAGE_SIZE, prot, hvc); | 208 | ret = add_mprotect(addr, PAGE_SIZE, prot, hvc); |
209 | *pte = pte_mkuptodate(*pte); | 209 | *pte = pte_mkuptodate(*pte); |
210 | } while (pte++, addr += PAGE_SIZE, ((addr < end) && !ret)); | 210 | } while (pte++, addr += PAGE_SIZE, ((addr < end) && !ret)); |
211 | return ret; | 211 | return ret; |
212 | } | 212 | } |
213 | 213 | ||
214 | static inline int update_pmd_range(pud_t *pud, unsigned long addr, | 214 | static inline int update_pmd_range(pud_t *pud, unsigned long addr, |
215 | unsigned long end, | 215 | unsigned long end, |
216 | struct host_vm_change *hvc) | 216 | struct host_vm_change *hvc) |
217 | { | 217 | { |
218 | pmd_t *pmd; | 218 | pmd_t *pmd; |
219 | unsigned long next; | 219 | unsigned long next; |
220 | int ret = 0; | 220 | int ret = 0; |
221 | 221 | ||
222 | pmd = pmd_offset(pud, addr); | 222 | pmd = pmd_offset(pud, addr); |
223 | do { | 223 | do { |
224 | next = pmd_addr_end(addr, end); | 224 | next = pmd_addr_end(addr, end); |
225 | if (!pmd_present(*pmd)) { | 225 | if (!pmd_present(*pmd)) { |
226 | if (hvc->force || pmd_newpage(*pmd)) { | 226 | if (hvc->force || pmd_newpage(*pmd)) { |
227 | ret = add_munmap(addr, next - addr, hvc); | 227 | ret = add_munmap(addr, next - addr, hvc); |
228 | pmd_mkuptodate(*pmd); | 228 | pmd_mkuptodate(*pmd); |
229 | } | 229 | } |
230 | } | 230 | } |
231 | else ret = update_pte_range(pmd, addr, next, hvc); | 231 | else ret = update_pte_range(pmd, addr, next, hvc); |
232 | } while (pmd++, addr = next, ((addr < end) && !ret)); | 232 | } while (pmd++, addr = next, ((addr < end) && !ret)); |
233 | return ret; | 233 | return ret; |
234 | } | 234 | } |
235 | 235 | ||
236 | static inline int update_pud_range(pgd_t *pgd, unsigned long addr, | 236 | static inline int update_pud_range(pgd_t *pgd, unsigned long addr, |
237 | unsigned long end, | 237 | unsigned long end, |
238 | struct host_vm_change *hvc) | 238 | struct host_vm_change *hvc) |
239 | { | 239 | { |
240 | pud_t *pud; | 240 | pud_t *pud; |
241 | unsigned long next; | 241 | unsigned long next; |
242 | int ret = 0; | 242 | int ret = 0; |
243 | 243 | ||
244 | pud = pud_offset(pgd, addr); | 244 | pud = pud_offset(pgd, addr); |
245 | do { | 245 | do { |
246 | next = pud_addr_end(addr, end); | 246 | next = pud_addr_end(addr, end); |
247 | if (!pud_present(*pud)) { | 247 | if (!pud_present(*pud)) { |
248 | if (hvc->force || pud_newpage(*pud)) { | 248 | if (hvc->force || pud_newpage(*pud)) { |
249 | ret = add_munmap(addr, next - addr, hvc); | 249 | ret = add_munmap(addr, next - addr, hvc); |
250 | pud_mkuptodate(*pud); | 250 | pud_mkuptodate(*pud); |
251 | } | 251 | } |
252 | } | 252 | } |
253 | else ret = update_pmd_range(pud, addr, next, hvc); | 253 | else ret = update_pmd_range(pud, addr, next, hvc); |
254 | } while (pud++, addr = next, ((addr < end) && !ret)); | 254 | } while (pud++, addr = next, ((addr < end) && !ret)); |
255 | return ret; | 255 | return ret; |
256 | } | 256 | } |
257 | 257 | ||
258 | void fix_range_common(struct mm_struct *mm, unsigned long start_addr, | 258 | void fix_range_common(struct mm_struct *mm, unsigned long start_addr, |
259 | unsigned long end_addr, int force) | 259 | unsigned long end_addr, int force) |
260 | { | 260 | { |
261 | pgd_t *pgd; | 261 | pgd_t *pgd; |
262 | struct host_vm_change hvc; | 262 | struct host_vm_change hvc; |
263 | unsigned long addr = start_addr, next; | 263 | unsigned long addr = start_addr, next; |
264 | int ret = 0; | 264 | int ret = 0; |
265 | 265 | ||
266 | hvc = INIT_HVC(mm, force); | 266 | hvc = INIT_HVC(mm, force); |
267 | pgd = pgd_offset(mm, addr); | 267 | pgd = pgd_offset(mm, addr); |
268 | do { | 268 | do { |
269 | next = pgd_addr_end(addr, end_addr); | 269 | next = pgd_addr_end(addr, end_addr); |
270 | if (!pgd_present(*pgd)) { | 270 | if (!pgd_present(*pgd)) { |
271 | if (force || pgd_newpage(*pgd)) { | 271 | if (force || pgd_newpage(*pgd)) { |
272 | ret = add_munmap(addr, next - addr, &hvc); | 272 | ret = add_munmap(addr, next - addr, &hvc); |
273 | pgd_mkuptodate(*pgd); | 273 | pgd_mkuptodate(*pgd); |
274 | } | 274 | } |
275 | } | 275 | } |
276 | else ret = update_pud_range(pgd, addr, next, &hvc); | 276 | else ret = update_pud_range(pgd, addr, next, &hvc); |
277 | } while (pgd++, addr = next, ((addr < end_addr) && !ret)); | 277 | } while (pgd++, addr = next, ((addr < end_addr) && !ret)); |
278 | 278 | ||
279 | if (!ret) | 279 | if (!ret) |
280 | ret = do_ops(&hvc, hvc.index, 1); | 280 | ret = do_ops(&hvc, hvc.index, 1); |
281 | 281 | ||
282 | /* This is not an else because ret is modified above */ | 282 | /* This is not an else because ret is modified above */ |
283 | if (ret) { | 283 | if (ret) { |
284 | printk(KERN_ERR "fix_range_common: failed, killing current " | 284 | printk(KERN_ERR "fix_range_common: failed, killing current " |
285 | "process\n"); | 285 | "process\n"); |
286 | force_sig(SIGKILL, current); | 286 | force_sig(SIGKILL, current); |
287 | } | 287 | } |
288 | } | 288 | } |
289 | 289 | ||
290 | int flush_tlb_kernel_range_common(unsigned long start, unsigned long end) | 290 | int flush_tlb_kernel_range_common(unsigned long start, unsigned long end) |
291 | { | 291 | { |
292 | struct mm_struct *mm; | 292 | struct mm_struct *mm; |
293 | pgd_t *pgd; | 293 | pgd_t *pgd; |
294 | pud_t *pud; | 294 | pud_t *pud; |
295 | pmd_t *pmd; | 295 | pmd_t *pmd; |
296 | pte_t *pte; | 296 | pte_t *pte; |
297 | unsigned long addr, last; | 297 | unsigned long addr, last; |
298 | int updated = 0, err; | 298 | int updated = 0, err; |
299 | 299 | ||
300 | mm = &init_mm; | 300 | mm = &init_mm; |
301 | for (addr = start; addr < end;) { | 301 | for (addr = start; addr < end;) { |
302 | pgd = pgd_offset(mm, addr); | 302 | pgd = pgd_offset(mm, addr); |
303 | if (!pgd_present(*pgd)) { | 303 | if (!pgd_present(*pgd)) { |
304 | last = ADD_ROUND(addr, PGDIR_SIZE); | 304 | last = ADD_ROUND(addr, PGDIR_SIZE); |
305 | if (last > end) | 305 | if (last > end) |
306 | last = end; | 306 | last = end; |
307 | if (pgd_newpage(*pgd)) { | 307 | if (pgd_newpage(*pgd)) { |
308 | updated = 1; | 308 | updated = 1; |
309 | err = os_unmap_memory((void *) addr, | 309 | err = os_unmap_memory((void *) addr, |
310 | last - addr); | 310 | last - addr); |
311 | if (err < 0) | 311 | if (err < 0) |
312 | panic("munmap failed, errno = %d\n", | 312 | panic("munmap failed, errno = %d\n", |
313 | -err); | 313 | -err); |
314 | } | 314 | } |
315 | addr = last; | 315 | addr = last; |
316 | continue; | 316 | continue; |
317 | } | 317 | } |
318 | 318 | ||
319 | pud = pud_offset(pgd, addr); | 319 | pud = pud_offset(pgd, addr); |
320 | if (!pud_present(*pud)) { | 320 | if (!pud_present(*pud)) { |
321 | last = ADD_ROUND(addr, PUD_SIZE); | 321 | last = ADD_ROUND(addr, PUD_SIZE); |
322 | if (last > end) | 322 | if (last > end) |
323 | last = end; | 323 | last = end; |
324 | if (pud_newpage(*pud)) { | 324 | if (pud_newpage(*pud)) { |
325 | updated = 1; | 325 | updated = 1; |
326 | err = os_unmap_memory((void *) addr, | 326 | err = os_unmap_memory((void *) addr, |
327 | last - addr); | 327 | last - addr); |
328 | if (err < 0) | 328 | if (err < 0) |
329 | panic("munmap failed, errno = %d\n", | 329 | panic("munmap failed, errno = %d\n", |
330 | -err); | 330 | -err); |
331 | } | 331 | } |
332 | addr = last; | 332 | addr = last; |
333 | continue; | 333 | continue; |
334 | } | 334 | } |
335 | 335 | ||
336 | pmd = pmd_offset(pud, addr); | 336 | pmd = pmd_offset(pud, addr); |
337 | if (!pmd_present(*pmd)) { | 337 | if (!pmd_present(*pmd)) { |
338 | last = ADD_ROUND(addr, PMD_SIZE); | 338 | last = ADD_ROUND(addr, PMD_SIZE); |
339 | if (last > end) | 339 | if (last > end) |
340 | last = end; | 340 | last = end; |
341 | if (pmd_newpage(*pmd)) { | 341 | if (pmd_newpage(*pmd)) { |
342 | updated = 1; | 342 | updated = 1; |
343 | err = os_unmap_memory((void *) addr, | 343 | err = os_unmap_memory((void *) addr, |
344 | last - addr); | 344 | last - addr); |
345 | if (err < 0) | 345 | if (err < 0) |
346 | panic("munmap failed, errno = %d\n", | 346 | panic("munmap failed, errno = %d\n", |
347 | -err); | 347 | -err); |
348 | } | 348 | } |
349 | addr = last; | 349 | addr = last; |
350 | continue; | 350 | continue; |
351 | } | 351 | } |
352 | 352 | ||
353 | pte = pte_offset_kernel(pmd, addr); | 353 | pte = pte_offset_kernel(pmd, addr); |
354 | if (!pte_present(*pte) || pte_newpage(*pte)) { | 354 | if (!pte_present(*pte) || pte_newpage(*pte)) { |
355 | updated = 1; | 355 | updated = 1; |
356 | err = os_unmap_memory((void *) addr, | 356 | err = os_unmap_memory((void *) addr, |
357 | PAGE_SIZE); | 357 | PAGE_SIZE); |
358 | if (err < 0) | 358 | if (err < 0) |
359 | panic("munmap failed, errno = %d\n", | 359 | panic("munmap failed, errno = %d\n", |
360 | -err); | 360 | -err); |
361 | if (pte_present(*pte)) | 361 | if (pte_present(*pte)) |
362 | map_memory(addr, | 362 | map_memory(addr, |
363 | pte_val(*pte) & PAGE_MASK, | 363 | pte_val(*pte) & PAGE_MASK, |
364 | PAGE_SIZE, 1, 1, 1); | 364 | PAGE_SIZE, 1, 1, 1); |
365 | } | 365 | } |
366 | else if (pte_newprot(*pte)) { | 366 | else if (pte_newprot(*pte)) { |
367 | updated = 1; | 367 | updated = 1; |
368 | os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1); | 368 | os_protect_memory((void *) addr, PAGE_SIZE, 1, 1, 1); |
369 | } | 369 | } |
370 | addr += PAGE_SIZE; | 370 | addr += PAGE_SIZE; |
371 | } | 371 | } |
372 | return updated; | 372 | return updated; |
373 | } | 373 | } |
374 | 374 | ||
375 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) | 375 | void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) |
376 | { | 376 | { |
377 | pgd_t *pgd; | 377 | pgd_t *pgd; |
378 | pud_t *pud; | 378 | pud_t *pud; |
379 | pmd_t *pmd; | 379 | pmd_t *pmd; |
380 | pte_t *pte; | 380 | pte_t *pte; |
381 | struct mm_struct *mm = vma->vm_mm; | 381 | struct mm_struct *mm = vma->vm_mm; |
382 | void *flush = NULL; | 382 | void *flush = NULL; |
383 | int r, w, x, prot, err = 0; | 383 | int r, w, x, prot, err = 0; |
384 | struct mm_id *mm_id; | 384 | struct mm_id *mm_id; |
385 | 385 | ||
386 | address &= PAGE_MASK; | 386 | address &= PAGE_MASK; |
387 | pgd = pgd_offset(mm, address); | 387 | pgd = pgd_offset(mm, address); |
388 | if (!pgd_present(*pgd)) | 388 | if (!pgd_present(*pgd)) |
389 | goto kill; | 389 | goto kill; |
390 | 390 | ||
391 | pud = pud_offset(pgd, address); | 391 | pud = pud_offset(pgd, address); |
392 | if (!pud_present(*pud)) | 392 | if (!pud_present(*pud)) |
393 | goto kill; | 393 | goto kill; |
394 | 394 | ||
395 | pmd = pmd_offset(pud, address); | 395 | pmd = pmd_offset(pud, address); |
396 | if (!pmd_present(*pmd)) | 396 | if (!pmd_present(*pmd)) |
397 | goto kill; | 397 | goto kill; |
398 | 398 | ||
399 | pte = pte_offset_kernel(pmd, address); | 399 | pte = pte_offset_kernel(pmd, address); |
400 | 400 | ||
401 | r = pte_read(*pte); | 401 | r = pte_read(*pte); |
402 | w = pte_write(*pte); | 402 | w = pte_write(*pte); |
403 | x = pte_exec(*pte); | 403 | x = pte_exec(*pte); |
404 | if (!pte_young(*pte)) { | 404 | if (!pte_young(*pte)) { |
405 | r = 0; | 405 | r = 0; |
406 | w = 0; | 406 | w = 0; |
407 | } else if (!pte_dirty(*pte)) { | 407 | } else if (!pte_dirty(*pte)) { |
408 | w = 0; | 408 | w = 0; |
409 | } | 409 | } |
410 | 410 | ||
411 | mm_id = &mm->context.id; | 411 | mm_id = &mm->context.id; |
412 | prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) | | 412 | prot = ((r ? UM_PROT_READ : 0) | (w ? UM_PROT_WRITE : 0) | |
413 | (x ? UM_PROT_EXEC : 0)); | 413 | (x ? UM_PROT_EXEC : 0)); |
414 | if (pte_newpage(*pte)) { | 414 | if (pte_newpage(*pte)) { |
415 | if (pte_present(*pte)) { | 415 | if (pte_present(*pte)) { |
416 | unsigned long long offset; | 416 | unsigned long long offset; |
417 | int fd; | 417 | int fd; |
418 | 418 | ||
419 | fd = phys_mapping(pte_val(*pte) & PAGE_MASK, &offset); | 419 | fd = phys_mapping(pte_val(*pte) & PAGE_MASK, &offset); |
420 | err = map(mm_id, address, PAGE_SIZE, prot, fd, offset, | 420 | err = map(mm_id, address, PAGE_SIZE, prot, fd, offset, |
421 | 1, &flush); | 421 | 1, &flush); |
422 | } | 422 | } |
423 | else err = unmap(mm_id, address, PAGE_SIZE, 1, &flush); | 423 | else err = unmap(mm_id, address, PAGE_SIZE, 1, &flush); |
424 | } | 424 | } |
425 | else if (pte_newprot(*pte)) | 425 | else if (pte_newprot(*pte)) |
426 | err = protect(mm_id, address, PAGE_SIZE, prot, 1, &flush); | 426 | err = protect(mm_id, address, PAGE_SIZE, prot, 1, &flush); |
427 | 427 | ||
428 | if (err) | 428 | if (err) |
429 | goto kill; | 429 | goto kill; |
430 | 430 | ||
431 | *pte = pte_mkuptodate(*pte); | 431 | *pte = pte_mkuptodate(*pte); |
432 | 432 | ||
433 | return; | 433 | return; |
434 | 434 | ||
435 | kill: | 435 | kill: |
436 | printk(KERN_ERR "Failed to flush page for address 0x%lx\n", address); | 436 | printk(KERN_ERR "Failed to flush page for address 0x%lx\n", address); |
437 | force_sig(SIGKILL, current); | 437 | force_sig(SIGKILL, current); |
438 | } | 438 | } |
439 | 439 | ||
440 | pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) | 440 | pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) |
441 | { | 441 | { |
442 | return pgd_offset(mm, address); | 442 | return pgd_offset(mm, address); |
443 | } | 443 | } |
444 | 444 | ||
445 | pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address) | 445 | pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address) |
446 | { | 446 | { |
447 | return pud_offset(pgd, address); | 447 | return pud_offset(pgd, address); |
448 | } | 448 | } |
449 | 449 | ||
450 | pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address) | 450 | pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address) |
451 | { | 451 | { |
452 | return pmd_offset(pud, address); | 452 | return pmd_offset(pud, address); |
453 | } | 453 | } |
454 | 454 | ||
455 | pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) | 455 | pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) |
456 | { | 456 | { |
457 | return pte_offset_kernel(pmd, address); | 457 | return pte_offset_kernel(pmd, address); |
458 | } | 458 | } |
459 | 459 | ||
460 | pte_t *addr_pte(struct task_struct *task, unsigned long addr) | 460 | pte_t *addr_pte(struct task_struct *task, unsigned long addr) |
461 | { | 461 | { |
462 | pgd_t *pgd = pgd_offset(task->mm, addr); | 462 | pgd_t *pgd = pgd_offset(task->mm, addr); |
463 | pud_t *pud = pud_offset(pgd, addr); | 463 | pud_t *pud = pud_offset(pgd, addr); |
464 | pmd_t *pmd = pmd_offset(pud, addr); | 464 | pmd_t *pmd = pmd_offset(pud, addr); |
465 | 465 | ||
466 | return pte_offset_map(pmd, addr); | 466 | return pte_offset_map(pmd, addr); |
467 | } | 467 | } |
468 | 468 | ||
469 | void flush_tlb_all(void) | 469 | void flush_tlb_all(void) |
470 | { | 470 | { |
471 | flush_tlb_mm(current->mm); | 471 | flush_tlb_mm(current->mm); |
472 | } | 472 | } |
473 | 473 | ||
474 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) | 474 | void flush_tlb_kernel_range(unsigned long start, unsigned long end) |
475 | { | 475 | { |
476 | flush_tlb_kernel_range_common(start, end); | 476 | flush_tlb_kernel_range_common(start, end); |
477 | } | 477 | } |
478 | 478 | ||
479 | void flush_tlb_kernel_vm(void) | 479 | void flush_tlb_kernel_vm(void) |
480 | { | 480 | { |
481 | flush_tlb_kernel_range_common(start_vm, end_vm); | 481 | flush_tlb_kernel_range_common(start_vm, end_vm); |
482 | } | 482 | } |
483 | 483 | ||
484 | void __flush_tlb_one(unsigned long addr) | 484 | void __flush_tlb_one(unsigned long addr) |
485 | { | 485 | { |
486 | flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE); | 486 | flush_tlb_kernel_range_common(addr, addr + PAGE_SIZE); |
487 | } | 487 | } |
488 | 488 | ||
489 | static void fix_range(struct mm_struct *mm, unsigned long start_addr, | 489 | static void fix_range(struct mm_struct *mm, unsigned long start_addr, |
490 | unsigned long end_addr, int force) | 490 | unsigned long end_addr, int force) |
491 | { | 491 | { |
492 | fix_range_common(mm, start_addr, end_addr, force); | 492 | fix_range_common(mm, start_addr, end_addr, force); |
493 | } | 493 | } |
494 | 494 | ||
495 | void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, | 495 | void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, |
496 | unsigned long end) | 496 | unsigned long end) |
497 | { | 497 | { |
498 | if (vma->vm_mm == NULL) | 498 | if (vma->vm_mm == NULL) |
499 | flush_tlb_kernel_range_common(start, end); | 499 | flush_tlb_kernel_range_common(start, end); |
500 | else fix_range(vma->vm_mm, start, end, 0); | 500 | else fix_range(vma->vm_mm, start, end, 0); |
501 | } | 501 | } |
502 | 502 | ||
503 | void flush_tlb_mm(struct mm_struct *mm) | 503 | void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, |
504 | unsigned long end) | ||
504 | { | 505 | { |
505 | /* | 506 | /* |
506 | * Don't bother flushing if this address space is about to be | 507 | * Don't bother flushing if this address space is about to be |
507 | * destroyed. | 508 | * destroyed. |
508 | */ | 509 | */ |
509 | if (atomic_read(&mm->mm_users) == 0) | 510 | if (atomic_read(&mm->mm_users) == 0) |
510 | return; | 511 | return; |
511 | 512 | ||
512 | fix_range(mm, 0, TASK_SIZE, 0); | 513 | fix_range(mm, start, end, 0); |
514 | } | ||
515 | |||
516 | void flush_tlb_mm(struct mm_struct *mm) | ||
517 | { | ||
518 | struct vm_area_struct *vma = mm->mmap; | ||
519 | |||
520 | while (vma != NULL) { | ||
521 | fix_range(mm, vma->vm_start, vma->vm_end, 0); | ||
522 | vma = vma->vm_next; | ||
523 | } | ||
513 | } | 524 | } |
514 | 525 | ||
515 | void force_flush_all(void) | 526 | void force_flush_all(void) |
516 | { | 527 | { |
517 | struct mm_struct *mm = current->mm; | 528 | struct mm_struct *mm = current->mm; |
518 | struct vm_area_struct *vma = mm->mmap; | 529 | struct vm_area_struct *vma = mm->mmap; |
519 | 530 | ||
520 | while (vma != NULL) { | 531 | while (vma != NULL) { |
521 | fix_range(mm, vma->vm_start, vma->vm_end, 1); | 532 | fix_range(mm, vma->vm_start, vma->vm_end, 1); |
522 | vma = vma->vm_next; | 533 | vma = vma->vm_next; |
523 | } | 534 | } |
524 | } | 535 | } |
525 | 536 |
include/asm-um/tlb.h
1 | #ifndef __UM_TLB_H | 1 | #ifndef __UM_TLB_H |
2 | #define __UM_TLB_H | 2 | #define __UM_TLB_H |
3 | 3 | ||
4 | #include <asm/arch/tlb.h> | 4 | #include <linux/swap.h> |
5 | #include <asm/percpu.h> | ||
6 | #include <asm/pgalloc.h> | ||
7 | #include <asm/tlbflush.h> | ||
8 | |||
9 | #define tlb_start_vma(tlb, vma) do { } while (0) | ||
10 | #define tlb_end_vma(tlb, vma) do { } while (0) | ||
11 | #define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) | ||
12 | |||
13 | /* struct mmu_gather is an opaque type used by the mm code for passing around | ||
14 | * any data needed by arch specific code for tlb_remove_page. | ||
15 | */ | ||
16 | struct mmu_gather { | ||
17 | struct mm_struct *mm; | ||
18 | unsigned int need_flush; /* Really unmapped some ptes? */ | ||
19 | unsigned long start; | ||
20 | unsigned long end; | ||
21 | unsigned int fullmm; /* non-zero means full mm flush */ | ||
22 | }; | ||
23 | |||
24 | /* Users of the generic TLB shootdown code must declare this storage space. */ | ||
25 | DECLARE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
26 | |||
27 | static inline void __tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, | ||
28 | unsigned long address) | ||
29 | { | ||
30 | if (tlb->start > address) | ||
31 | tlb->start = address; | ||
32 | if (tlb->end < address + PAGE_SIZE) | ||
33 | tlb->end = address + PAGE_SIZE; | ||
34 | } | ||
35 | |||
36 | static inline void init_tlb_gather(struct mmu_gather *tlb) | ||
37 | { | ||
38 | tlb->need_flush = 0; | ||
39 | |||
40 | tlb->start = TASK_SIZE; | ||
41 | tlb->end = 0; | ||
42 | |||
43 | if (tlb->fullmm) { | ||
44 | tlb->start = 0; | ||
45 | tlb->end = TASK_SIZE; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | /* tlb_gather_mmu | ||
50 | * Return a pointer to an initialized struct mmu_gather. | ||
51 | */ | ||
52 | static inline struct mmu_gather * | ||
53 | tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush) | ||
54 | { | ||
55 | struct mmu_gather *tlb = &get_cpu_var(mmu_gathers); | ||
56 | |||
57 | tlb->mm = mm; | ||
58 | tlb->fullmm = full_mm_flush; | ||
59 | |||
60 | init_tlb_gather(tlb); | ||
61 | |||
62 | return tlb; | ||
63 | } | ||
64 | |||
65 | extern void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start, | ||
66 | unsigned long end); | ||
67 | |||
68 | static inline void | ||
69 | tlb_flush_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) | ||
70 | { | ||
71 | if (!tlb->need_flush) | ||
72 | return; | ||
73 | |||
74 | flush_tlb_mm_range(tlb->mm, tlb->start, tlb->end); | ||
75 | init_tlb_gather(tlb); | ||
76 | } | ||
77 | |||
78 | /* tlb_finish_mmu | ||
79 | * Called at the end of the shootdown operation to free up any resources | ||
80 | * that were required. | ||
81 | */ | ||
82 | static inline void | ||
83 | tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) | ||
84 | { | ||
85 | tlb_flush_mmu(tlb, start, end); | ||
86 | |||
87 | /* keep the page table cache within bounds */ | ||
88 | check_pgt_cache(); | ||
89 | |||
90 | put_cpu_var(mmu_gathers); | ||
91 | } | ||
92 | |||
93 | /* tlb_remove_page | ||
94 | * Must perform the equivalent to __free_pte(pte_get_and_clear(ptep)), | ||
95 | * while handling the additional races in SMP caused by other CPUs | ||
96 | * caching valid mappings in their TLBs. | ||
97 | */ | ||
98 | static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) | ||
99 | { | ||
100 | tlb->need_flush = 1; | ||
101 | free_page_and_swap_cache(page); | ||
102 | return; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * tlb_remove_tlb_entry - remember a pte unmapping for later tlb invalidation. | ||
107 | * | ||
108 | * Record the fact that pte's were really umapped in ->need_flush, so we can | ||
109 | * later optimise away the tlb invalidate. This helps when userspace is | ||
110 | * unmapping already-unmapped pages, which happens quite a lot. | ||
111 | */ | ||
112 | #define tlb_remove_tlb_entry(tlb, ptep, address) \ | ||
113 | do { \ | ||
114 | tlb->need_flush = 1; \ | ||
115 | __tlb_remove_tlb_entry(tlb, ptep, address); \ | ||
116 | } while (0) | ||
117 | |||
118 | #define pte_free_tlb(tlb, ptep) __pte_free_tlb(tlb, ptep) | ||
119 | |||
120 | #define pud_free_tlb(tlb, pudp) __pud_free_tlb(tlb, pudp) | ||
121 | |||
122 | #define pmd_free_tlb(tlb, pmdp) __pmd_free_tlb(tlb, pmdp) | ||
123 | |||
124 | #define tlb_migrate_finish(mm) do {} while (0) | ||
5 | 125 | ||
6 | #endif | 126 | #endif |
7 | 127 |