diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-02-20 16:50:24 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-20 18:09:43 -0500 |
commit | f2f13a8535174dbb813a0607a9d4737cfba98f6c (patch) | |
tree | a7e2d70cef8dbc6fc5ac2e878e1fc7435a6f6203 /arch/x86/mm/fault.c | |
parent | b18018126f422f5b706fd750373425e10e84b486 (diff) |
x86, mm: fault.c, reorder functions
Impact: cleanup
Avoid a couple more #ifdefs by moving fundamentally non-unifiable
functions into a single #ifdef 32-bit / #else / #endif block in
fault.c: vmalloc*(), dump_pagetable(), check_vm8086_mode().
No code changed:
text data bss dec hex filename
4618 32 24 4674 1242 fault.o.before
4618 32 24 4674 1242 fault.o.after
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/mm/fault.c')
-rw-r--r-- | arch/x86/mm/fault.c | 474 |
1 files changed, 239 insertions, 235 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 379beaec6caa..4ce62fb80da7 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -191,18 +191,124 @@ force_sig_info_fault(int si_signo, int si_code, unsigned long address, | |||
191 | force_sig_info(si_signo, &info, tsk); | 191 | force_sig_info(si_signo, &info, tsk); |
192 | } | 192 | } |
193 | 193 | ||
194 | #ifdef CONFIG_X86_64 | 194 | DEFINE_SPINLOCK(pgd_lock); |
195 | static int bad_address(void *p) | 195 | LIST_HEAD(pgd_list); |
196 | |||
197 | #ifdef CONFIG_X86_32 | ||
198 | static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address) | ||
196 | { | 199 | { |
197 | unsigned long dummy; | 200 | unsigned index = pgd_index(address); |
201 | pgd_t *pgd_k; | ||
202 | pud_t *pud, *pud_k; | ||
203 | pmd_t *pmd, *pmd_k; | ||
198 | 204 | ||
199 | return probe_kernel_address((unsigned long *)p, dummy); | 205 | pgd += index; |
206 | pgd_k = init_mm.pgd + index; | ||
207 | |||
208 | if (!pgd_present(*pgd_k)) | ||
209 | return NULL; | ||
210 | |||
211 | /* | ||
212 | * set_pgd(pgd, *pgd_k); here would be useless on PAE | ||
213 | * and redundant with the set_pmd() on non-PAE. As would | ||
214 | * set_pud. | ||
215 | */ | ||
216 | pud = pud_offset(pgd, address); | ||
217 | pud_k = pud_offset(pgd_k, address); | ||
218 | if (!pud_present(*pud_k)) | ||
219 | return NULL; | ||
220 | |||
221 | pmd = pmd_offset(pud, address); | ||
222 | pmd_k = pmd_offset(pud_k, address); | ||
223 | if (!pmd_present(*pmd_k)) | ||
224 | return NULL; | ||
225 | |||
226 | if (!pmd_present(*pmd)) { | ||
227 | set_pmd(pmd, *pmd_k); | ||
228 | arch_flush_lazy_mmu_mode(); | ||
229 | } else { | ||
230 | BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k)); | ||
231 | } | ||
232 | |||
233 | return pmd_k; | ||
234 | } | ||
235 | |||
236 | void vmalloc_sync_all(void) | ||
237 | { | ||
238 | unsigned long address; | ||
239 | |||
240 | if (SHARED_KERNEL_PMD) | ||
241 | return; | ||
242 | |||
243 | for (address = VMALLOC_START & PMD_MASK; | ||
244 | address >= TASK_SIZE && address < FIXADDR_TOP; | ||
245 | address += PMD_SIZE) { | ||
246 | |||
247 | unsigned long flags; | ||
248 | struct page *page; | ||
249 | |||
250 | spin_lock_irqsave(&pgd_lock, flags); | ||
251 | list_for_each_entry(page, &pgd_list, lru) { | ||
252 | if (!vmalloc_sync_one(page_address(page), address)) | ||
253 | break; | ||
254 | } | ||
255 | spin_unlock_irqrestore(&pgd_lock, flags); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * 32-bit: | ||
261 | * | ||
262 | * Handle a fault on the vmalloc or module mapping area | ||
263 | */ | ||
264 | static noinline int vmalloc_fault(unsigned long address) | ||
265 | { | ||
266 | unsigned long pgd_paddr; | ||
267 | pmd_t *pmd_k; | ||
268 | pte_t *pte_k; | ||
269 | |||
270 | /* Make sure we are in vmalloc area: */ | ||
271 | if (!(address >= VMALLOC_START && address < VMALLOC_END)) | ||
272 | return -1; | ||
273 | |||
274 | /* | ||
275 | * Synchronize this task's top level page-table | ||
276 | * with the 'reference' page table. | ||
277 | * | ||
278 | * Do _not_ use "current" here. We might be inside | ||
279 | * an interrupt in the middle of a task switch.. | ||
280 | */ | ||
281 | pgd_paddr = read_cr3(); | ||
282 | pmd_k = vmalloc_sync_one(__va(pgd_paddr), address); | ||
283 | if (!pmd_k) | ||
284 | return -1; | ||
285 | |||
286 | pte_k = pte_offset_kernel(pmd_k, address); | ||
287 | if (!pte_present(*pte_k)) | ||
288 | return -1; | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Did it hit the DOS screen memory VA from vm86 mode? | ||
295 | */ | ||
296 | static inline void | ||
297 | check_v8086_mode(struct pt_regs *regs, unsigned long address, | ||
298 | struct task_struct *tsk) | ||
299 | { | ||
300 | unsigned long bit; | ||
301 | |||
302 | if (!v8086_mode(regs)) | ||
303 | return; | ||
304 | |||
305 | bit = (address - 0xA0000) >> PAGE_SHIFT; | ||
306 | if (bit < 32) | ||
307 | tsk->thread.screen_bitmap |= 1 << bit; | ||
200 | } | 308 | } |
201 | #endif | ||
202 | 309 | ||
203 | static void dump_pagetable(unsigned long address) | 310 | static void dump_pagetable(unsigned long address) |
204 | { | 311 | { |
205 | #ifdef CONFIG_X86_32 | ||
206 | __typeof__(pte_val(__pte(0))) page; | 312 | __typeof__(pte_val(__pte(0))) page; |
207 | 313 | ||
208 | page = read_cr3(); | 314 | page = read_cr3(); |
@@ -239,7 +345,132 @@ static void dump_pagetable(unsigned long address) | |||
239 | } | 345 | } |
240 | 346 | ||
241 | printk("\n"); | 347 | printk("\n"); |
242 | #else /* CONFIG_X86_64 */ | 348 | } |
349 | |||
350 | #else /* CONFIG_X86_64: */ | ||
351 | |||
352 | void vmalloc_sync_all(void) | ||
353 | { | ||
354 | unsigned long address; | ||
355 | |||
356 | for (address = VMALLOC_START & PGDIR_MASK; address <= VMALLOC_END; | ||
357 | address += PGDIR_SIZE) { | ||
358 | |||
359 | const pgd_t *pgd_ref = pgd_offset_k(address); | ||
360 | unsigned long flags; | ||
361 | struct page *page; | ||
362 | |||
363 | if (pgd_none(*pgd_ref)) | ||
364 | continue; | ||
365 | |||
366 | spin_lock_irqsave(&pgd_lock, flags); | ||
367 | list_for_each_entry(page, &pgd_list, lru) { | ||
368 | pgd_t *pgd; | ||
369 | pgd = (pgd_t *)page_address(page) + pgd_index(address); | ||
370 | if (pgd_none(*pgd)) | ||
371 | set_pgd(pgd, *pgd_ref); | ||
372 | else | ||
373 | BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref)); | ||
374 | } | ||
375 | spin_unlock_irqrestore(&pgd_lock, flags); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * 64-bit: | ||
381 | * | ||
382 | * Handle a fault on the vmalloc area | ||
383 | * | ||
384 | * This assumes no large pages in there. | ||
385 | */ | ||
386 | static noinline int vmalloc_fault(unsigned long address) | ||
387 | { | ||
388 | pgd_t *pgd, *pgd_ref; | ||
389 | pud_t *pud, *pud_ref; | ||
390 | pmd_t *pmd, *pmd_ref; | ||
391 | pte_t *pte, *pte_ref; | ||
392 | |||
393 | /* Make sure we are in vmalloc area: */ | ||
394 | if (!(address >= VMALLOC_START && address < VMALLOC_END)) | ||
395 | return -1; | ||
396 | |||
397 | /* | ||
398 | * Copy kernel mappings over when needed. This can also | ||
399 | * happen within a race in page table update. In the later | ||
400 | * case just flush: | ||
401 | */ | ||
402 | pgd = pgd_offset(current->active_mm, address); | ||
403 | pgd_ref = pgd_offset_k(address); | ||
404 | if (pgd_none(*pgd_ref)) | ||
405 | return -1; | ||
406 | |||
407 | if (pgd_none(*pgd)) | ||
408 | set_pgd(pgd, *pgd_ref); | ||
409 | else | ||
410 | BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref)); | ||
411 | |||
412 | /* | ||
413 | * Below here mismatches are bugs because these lower tables | ||
414 | * are shared: | ||
415 | */ | ||
416 | |||
417 | pud = pud_offset(pgd, address); | ||
418 | pud_ref = pud_offset(pgd_ref, address); | ||
419 | if (pud_none(*pud_ref)) | ||
420 | return -1; | ||
421 | |||
422 | if (pud_none(*pud) || pud_page_vaddr(*pud) != pud_page_vaddr(*pud_ref)) | ||
423 | BUG(); | ||
424 | |||
425 | pmd = pmd_offset(pud, address); | ||
426 | pmd_ref = pmd_offset(pud_ref, address); | ||
427 | if (pmd_none(*pmd_ref)) | ||
428 | return -1; | ||
429 | |||
430 | if (pmd_none(*pmd) || pmd_page(*pmd) != pmd_page(*pmd_ref)) | ||
431 | BUG(); | ||
432 | |||
433 | pte_ref = pte_offset_kernel(pmd_ref, address); | ||
434 | if (!pte_present(*pte_ref)) | ||
435 | return -1; | ||
436 | |||
437 | pte = pte_offset_kernel(pmd, address); | ||
438 | |||
439 | /* | ||
440 | * Don't use pte_page here, because the mappings can point | ||
441 | * outside mem_map, and the NUMA hash lookup cannot handle | ||
442 | * that: | ||
443 | */ | ||
444 | if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref)) | ||
445 | BUG(); | ||
446 | |||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static const char errata93_warning[] = | ||
451 | KERN_ERR "******* Your BIOS seems to not contain a fix for K8 errata #93\n" | ||
452 | KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n" | ||
453 | KERN_ERR "******* Please consider a BIOS update.\n" | ||
454 | KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n"; | ||
455 | |||
456 | /* | ||
457 | * No vm86 mode in 64-bit mode: | ||
458 | */ | ||
459 | static inline void | ||
460 | check_v8086_mode(struct pt_regs *regs, unsigned long address, | ||
461 | struct task_struct *tsk) | ||
462 | { | ||
463 | } | ||
464 | |||
465 | static int bad_address(void *p) | ||
466 | { | ||
467 | unsigned long dummy; | ||
468 | |||
469 | return probe_kernel_address((unsigned long *)p, dummy); | ||
470 | } | ||
471 | |||
472 | static void dump_pagetable(unsigned long address) | ||
473 | { | ||
243 | pgd_t *pgd; | 474 | pgd_t *pgd; |
244 | pud_t *pud; | 475 | pud_t *pud; |
245 | pmd_t *pmd; | 476 | pmd_t *pmd; |
@@ -284,83 +515,9 @@ out: | |||
284 | return; | 515 | return; |
285 | bad: | 516 | bad: |
286 | printk("BAD\n"); | 517 | printk("BAD\n"); |
287 | #endif | ||
288 | } | ||
289 | |||
290 | #ifdef CONFIG_X86_32 | ||
291 | static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address) | ||
292 | { | ||
293 | unsigned index = pgd_index(address); | ||
294 | pgd_t *pgd_k; | ||
295 | pud_t *pud, *pud_k; | ||
296 | pmd_t *pmd, *pmd_k; | ||
297 | |||
298 | pgd += index; | ||
299 | pgd_k = init_mm.pgd + index; | ||
300 | |||
301 | if (!pgd_present(*pgd_k)) | ||
302 | return NULL; | ||
303 | |||
304 | /* | ||
305 | * set_pgd(pgd, *pgd_k); here would be useless on PAE | ||
306 | * and redundant with the set_pmd() on non-PAE. As would | ||
307 | * set_pud. | ||
308 | */ | ||
309 | pud = pud_offset(pgd, address); | ||
310 | pud_k = pud_offset(pgd_k, address); | ||
311 | if (!pud_present(*pud_k)) | ||
312 | return NULL; | ||
313 | |||
314 | pmd = pmd_offset(pud, address); | ||
315 | pmd_k = pmd_offset(pud_k, address); | ||
316 | if (!pmd_present(*pmd_k)) | ||
317 | return NULL; | ||
318 | |||
319 | if (!pmd_present(*pmd)) { | ||
320 | set_pmd(pmd, *pmd_k); | ||
321 | arch_flush_lazy_mmu_mode(); | ||
322 | } else { | ||
323 | BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k)); | ||
324 | } | ||
325 | |||
326 | return pmd_k; | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * Did it hit the DOS screen memory VA from vm86 mode? | ||
331 | */ | ||
332 | static inline void | ||
333 | check_v8086_mode(struct pt_regs *regs, unsigned long address, | ||
334 | struct task_struct *tsk) | ||
335 | { | ||
336 | unsigned long bit; | ||
337 | |||
338 | if (!v8086_mode(regs)) | ||
339 | return; | ||
340 | |||
341 | bit = (address - 0xA0000) >> PAGE_SHIFT; | ||
342 | if (bit < 32) | ||
343 | tsk->thread.screen_bitmap |= 1 << bit; | ||
344 | } | ||
345 | |||
346 | #else /* CONFIG_X86_64: */ | ||
347 | |||
348 | static const char errata93_warning[] = | ||
349 | KERN_ERR "******* Your BIOS seems to not contain a fix for K8 errata #93\n" | ||
350 | KERN_ERR "******* Working around it, but it may cause SEGVs or burn power.\n" | ||
351 | KERN_ERR "******* Please consider a BIOS update.\n" | ||
352 | KERN_ERR "******* Disabling USB legacy in the BIOS may also help.\n"; | ||
353 | |||
354 | /* | ||
355 | * No vm86 mode in 64-bit mode: | ||
356 | */ | ||
357 | static inline void | ||
358 | check_v8086_mode(struct pt_regs *regs, unsigned long address, | ||
359 | struct task_struct *tsk) | ||
360 | { | ||
361 | } | 518 | } |
362 | 519 | ||
363 | #endif | 520 | #endif /* CONFIG_X86_64 */ |
364 | 521 | ||
365 | /* | 522 | /* |
366 | * Workaround for K8 erratum #93 & buggy BIOS. | 523 | * Workaround for K8 erratum #93 & buggy BIOS. |
@@ -795,109 +952,6 @@ spurious_fault(unsigned long error_code, unsigned long address) | |||
795 | return ret; | 952 | return ret; |
796 | } | 953 | } |
797 | 954 | ||
798 | /* | ||
799 | * 32-bit: | ||
800 | * | ||
801 | * Handle a fault on the vmalloc or module mapping area | ||
802 | * | ||
803 | * 64-bit: | ||
804 | * | ||
805 | * Handle a fault on the vmalloc area | ||
806 | * | ||
807 | * This assumes no large pages in there. | ||
808 | */ | ||
809 | static noinline int vmalloc_fault(unsigned long address) | ||
810 | { | ||
811 | #ifdef CONFIG_X86_32 | ||
812 | unsigned long pgd_paddr; | ||
813 | pmd_t *pmd_k; | ||
814 | pte_t *pte_k; | ||
815 | |||
816 | /* Make sure we are in vmalloc area: */ | ||
817 | if (!(address >= VMALLOC_START && address < VMALLOC_END)) | ||
818 | return -1; | ||
819 | |||
820 | /* | ||
821 | * Synchronize this task's top level page-table | ||
822 | * with the 'reference' page table. | ||
823 | * | ||
824 | * Do _not_ use "current" here. We might be inside | ||
825 | * an interrupt in the middle of a task switch.. | ||
826 | */ | ||
827 | pgd_paddr = read_cr3(); | ||
828 | pmd_k = vmalloc_sync_one(__va(pgd_paddr), address); | ||
829 | if (!pmd_k) | ||
830 | return -1; | ||
831 | |||
832 | pte_k = pte_offset_kernel(pmd_k, address); | ||
833 | if (!pte_present(*pte_k)) | ||
834 | return -1; | ||
835 | |||
836 | return 0; | ||
837 | #else | ||
838 | pgd_t *pgd, *pgd_ref; | ||
839 | pud_t *pud, *pud_ref; | ||
840 | pmd_t *pmd, *pmd_ref; | ||
841 | pte_t *pte, *pte_ref; | ||
842 | |||
843 | /* Make sure we are in vmalloc area: */ | ||
844 | if (!(address >= VMALLOC_START && address < VMALLOC_END)) | ||
845 | return -1; | ||
846 | |||
847 | /* | ||
848 | * Copy kernel mappings over when needed. This can also | ||
849 | * happen within a race in page table update. In the later | ||
850 | * case just flush: | ||
851 | */ | ||
852 | pgd = pgd_offset(current->active_mm, address); | ||
853 | pgd_ref = pgd_offset_k(address); | ||
854 | if (pgd_none(*pgd_ref)) | ||
855 | return -1; | ||
856 | |||
857 | if (pgd_none(*pgd)) | ||
858 | set_pgd(pgd, *pgd_ref); | ||
859 | else | ||
860 | BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref)); | ||
861 | |||
862 | /* | ||
863 | * Below here mismatches are bugs because these lower tables | ||
864 | * are shared: | ||
865 | */ | ||
866 | |||
867 | pud = pud_offset(pgd, address); | ||
868 | pud_ref = pud_offset(pgd_ref, address); | ||
869 | if (pud_none(*pud_ref)) | ||
870 | return -1; | ||
871 | |||
872 | if (pud_none(*pud) || pud_page_vaddr(*pud) != pud_page_vaddr(*pud_ref)) | ||
873 | BUG(); | ||
874 | |||
875 | pmd = pmd_offset(pud, address); | ||
876 | pmd_ref = pmd_offset(pud_ref, address); | ||
877 | if (pmd_none(*pmd_ref)) | ||
878 | return -1; | ||
879 | |||
880 | if (pmd_none(*pmd) || pmd_page(*pmd) != pmd_page(*pmd_ref)) | ||
881 | BUG(); | ||
882 | |||
883 | pte_ref = pte_offset_kernel(pmd_ref, address); | ||
884 | if (!pte_present(*pte_ref)) | ||
885 | return -1; | ||
886 | |||
887 | pte = pte_offset_kernel(pmd, address); | ||
888 | |||
889 | /* | ||
890 | * Don't use pte_page here, because the mappings can point | ||
891 | * outside mem_map, and the NUMA hash lookup cannot handle | ||
892 | * that: | ||
893 | */ | ||
894 | if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref)) | ||
895 | BUG(); | ||
896 | |||
897 | return 0; | ||
898 | #endif | ||
899 | } | ||
900 | |||
901 | int show_unhandled_signals = 1; | 955 | int show_unhandled_signals = 1; |
902 | 956 | ||
903 | static inline int | 957 | static inline int |
@@ -1115,53 +1169,3 @@ good_area: | |||
1115 | 1169 | ||
1116 | up_read(&mm->mmap_sem); | 1170 | up_read(&mm->mmap_sem); |
1117 | } | 1171 | } |
1118 | |||
1119 | DEFINE_SPINLOCK(pgd_lock); | ||
1120 | LIST_HEAD(pgd_list); | ||
1121 | |||
1122 | void vmalloc_sync_all(void) | ||
1123 | { | ||
1124 | unsigned long address; | ||
1125 | |||
1126 | #ifdef CONFIG_X86_32 | ||
1127 | if (SHARED_KERNEL_PMD) | ||
1128 | return; | ||
1129 | |||
1130 | for (address = VMALLOC_START & PMD_MASK; | ||
1131 | address >= TASK_SIZE && address < FIXADDR_TOP; | ||
1132 | address += PMD_SIZE) { | ||
1133 | |||
1134 | unsigned long flags; | ||
1135 | struct page *page; | ||
1136 | |||
1137 | spin_lock_irqsave(&pgd_lock, flags); | ||
1138 | list_for_each_entry(page, &pgd_list, lru) { | ||
1139 | if (!vmalloc_sync_one(page_address(page), address)) | ||
1140 | break; | ||
1141 | } | ||
1142 | spin_unlock_irqrestore(&pgd_lock, flags); | ||
1143 | } | ||
1144 | #else /* CONFIG_X86_64 */ | ||
1145 | for (address = VMALLOC_START & PGDIR_MASK; address <= VMALLOC_END; | ||
1146 | address += PGDIR_SIZE) { | ||
1147 | |||
1148 | const pgd_t *pgd_ref = pgd_offset_k(address); | ||
1149 | unsigned long flags; | ||
1150 | struct page *page; | ||
1151 | |||
1152 | if (pgd_none(*pgd_ref)) | ||
1153 | continue; | ||
1154 | |||
1155 | spin_lock_irqsave(&pgd_lock, flags); | ||
1156 | list_for_each_entry(page, &pgd_list, lru) { | ||
1157 | pgd_t *pgd; | ||
1158 | pgd = (pgd_t *)page_address(page) + pgd_index(address); | ||
1159 | if (pgd_none(*pgd)) | ||
1160 | set_pgd(pgd, *pgd_ref); | ||
1161 | else | ||
1162 | BUG_ON(pgd_page_vaddr(*pgd) != pgd_page_vaddr(*pgd_ref)); | ||
1163 | } | ||
1164 | spin_unlock_irqrestore(&pgd_lock, flags); | ||
1165 | } | ||
1166 | #endif | ||
1167 | } | ||