diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-05-23 12:52:58 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-05-23 12:52:58 +0000 |
commit | 5e1f225fc0d4793e1fdc85660b1fdf69095c38f3 (patch) | |
tree | 229660073999b8e43dfee99eff59975bc0e7be88 | |
parent | b02fba343994b22476ff5daf48028d8f2cf7dc1a (diff) |
Fix vmalloc for mips64.
-rw-r--r-- | arch/mips64/mm/fault.c | 51 | ||||
-rw-r--r-- | include/asm-mips64/pgtable.h | 7 |
2 files changed, 51 insertions, 7 deletions
diff --git a/arch/mips64/mm/fault.c b/arch/mips64/mm/fault.c index 42e810176..a070ae50c 100644 --- a/arch/mips64/mm/fault.c +++ b/arch/mips64/mm/fault.c @@ -84,6 +84,18 @@ do_page_fault(struct pt_regs *regs, unsigned long write, unsigned long address) unsigned long fixup; siginfo_t info; + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + */ + if (address >= TASK_SIZE) + goto vmalloc_fault; + info.si_code = SEGV_MAPERR; /* * If we're in an interrupt or have no user @@ -148,13 +160,7 @@ good_area: bad_area: up_read(&mm->mmap_sem); - /* - * Quickly check for vmalloc range faults. - */ - if ((!vma) && (address >= VMALLOC_START) && (address < VMALLOC_END)) { - printk("Fix vmalloc invalidate fault\n"); - while(1); - } +bad_area_nosemaphore: if (user_mode(regs)) { tsk->thread.cp0_badvaddr = address; tsk->thread.error_code = write; @@ -232,4 +238,35 @@ do_sigbus: /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) goto no_context; + + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + */ + int offset = pgd_index(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + + pgd = tsk->active_mm->pgd + offset; + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd)) { + if (!pgd_present(*pgd_k)) + goto bad_area_nosemaphore; + set_pgd(pgd, *pgd_k); + return; + } + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + + if (pmd_present(*pmd) || !pmd_present(*pmd_k)) + goto bad_area_nosemaphore; + set_pmd(pmd, *pmd_k); + return; + } } diff --git a/include/asm-mips64/pgtable.h b/include/asm-mips64/pgtable.h index bd925fd6b..db48fc09e 100644 --- a/include/asm-mips64/pgtable.h +++ b/include/asm-mips64/pgtable.h @@ -314,6 +314,13 @@ extern inline void pte_clear(pte_t *ptep) } /* + * (pmds are folded into pgds so this doesnt get actually called, + * but the define is needed for a generic inline function.) + */ +#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) +#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval) + +/* * Empty pmd entries point to the invalid_pte_table. */ extern inline int pmd_none(pmd_t pmd) |