diff options
Diffstat (limited to 'arch/sh/mm')
-rw-r--r-- | arch/sh/mm/fault_32.c | 137 | ||||
-rw-r--r-- | arch/sh/mm/tlbflush_64.c | 114 |
2 files changed, 139 insertions, 112 deletions
diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index 324eef9..8ce856d 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -35,6 +35,74 @@ static inline int notify_page_fault(struct pt_regs *regs, int trap) return ret; } +/* + * This is useful to dump out the page tables associated with + * 'addr' in mm 'mm'. + */ +static void show_pte(struct mm_struct *mm, unsigned long addr) +{ + pgd_t *pgd; + + if (mm) + pgd = mm->pgd; + else + pgd = get_TTB(); + + printk(KERN_ALERT "pgd = %p\n", pgd); + pgd += pgd_index(addr); + printk(KERN_ALERT "[%08lx] *pgd=%0*Lx", addr, + sizeof(*pgd) * 2, (u64)pgd_val(*pgd)); + + do { + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + if (pgd_none(*pgd)) + break; + + if (pgd_bad(*pgd)) { + printk("(bad)"); + break; + } + + pud = pud_offset(pgd, addr); + if (PTRS_PER_PUD != 1) + printk(", *pud=%0*Lx", sizeof(*pud) * 2, + (u64)pud_val(*pud)); + + if (pud_none(*pud)) + break; + + if (pud_bad(*pud)) { + printk("(bad)"); + break; + } + + pmd = pmd_offset(pud, addr); + if (PTRS_PER_PMD != 1) + printk(", *pmd=%0*Lx", sizeof(*pmd) * 2, + (u64)pmd_val(*pmd)); + + if (pmd_none(*pmd)) + break; + + if (pmd_bad(*pmd)) { + printk("(bad)"); + break; + } + + /* We must not map this if we have highmem enabled */ + if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT))) + break; + + pte = pte_offset_kernel(pmd, addr); + printk(", *pte=%0*Lx", sizeof(*pte) * 2, (u64)pte_val(*pte)); + } while (0); + + printk("\n"); +} + static inline pmd_t *vmalloc_sync_one(pgd_t *pgd, unsigned long address) { unsigned index = pgd_index(address); @@ -86,7 +154,7 @@ static noinline int vmalloc_fault(unsigned long address) pte_t *pte_k; /* Make sure we are in vmalloc/module/P3 area: */ - if (!(address >= VMALLOC_START && address < P3_ADDR_MAX)) + if (!(address >= P3SEG && address < P3_ADDR_MAX)) return -1; /* @@ -129,6 +197,8 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, int si_code; int fault; siginfo_t info; + unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + (writeaccess ? FAULT_FLAG_WRITE : 0)); tsk = current; mm = tsk->mm; @@ -169,6 +239,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, if (in_atomic() || !mm) goto no_context; +retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); @@ -200,7 +271,11 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -208,14 +283,27 @@ good_area: goto do_sigbus; BUG(); } - if (fault & VM_FAULT_MAJOR) { - tsk->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, - regs, address); - } else { - tsk->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, - regs, address); + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + tsk->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, + regs, address); + } else { + tsk->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, + regs, address); + } + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + goto retry; + } } up_read(&mm->mmap_sem); @@ -254,29 +342,12 @@ no_context: bust_spinlocks(1); if (oops_may_print()) { - unsigned long page; - - if (address < PAGE_SIZE) - printk(KERN_ALERT "Unable to handle kernel NULL " - "pointer dereference"); - else - printk(KERN_ALERT "Unable to handle kernel paging " - "request"); - printk(" at virtual address %08lx\n", address); - printk(KERN_ALERT "pc = %08lx\n", regs->pc); - page = (unsigned long)get_TTB(); - if (page) { - page = ((__typeof__(page) *)page)[address >> PGDIR_SHIFT]; - printk(KERN_ALERT "*pde = %08lx\n", page); - if (page & _PAGE_PRESENT) { - page &= PAGE_MASK; - address &= 0x003ff000; - page = ((__typeof__(page) *) - __va(page))[address >> - PAGE_SHIFT]; - printk(KERN_ALERT "*pte = %08lx\n", page); - } - } + printk(KERN_ALERT + "Unable to handle kernel %s at virtual address %08lx\n", + (address < PAGE_SIZE) ? "NULL pointer dereference" : + "paging request", address); + + show_pte(mm, address); } die("Oops", regs, writeaccess); diff --git a/arch/sh/mm/tlbflush_64.c b/arch/sh/mm/tlbflush_64.c index 11c5a18..605dc65 100644 --- a/arch/sh/mm/tlbflush_64.c +++ b/arch/sh/mm/tlbflush_64.c @@ -3,7 +3,7 @@ * * Copyright (C) 2000, 2001 Paolo Alberelli * Copyright (C) 2003 Richard Curnow (/proc/tlb, bug fixes) - * Copyright (C) 2003 - 2009 Paul Mundt + * Copyright (C) 2003 - 2012 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -28,33 +28,6 @@ #include <asm/pgalloc.h> #include <asm/mmu_context.h> -extern void die(const char *,struct pt_regs *,long); - -#define PFLAG(val,flag) (( (val) & (flag) ) ? #flag : "" ) -#define PPROT(flag) PFLAG(pgprot_val(prot),flag) - -static inline void print_prots(pgprot_t prot) -{ - printk("prot is 0x%016llx\n",pgprot_val(prot)); - - printk("%s %s %s %s %s\n",PPROT(_PAGE_SHARED),PPROT(_PAGE_READ), - PPROT(_PAGE_EXECUTE),PPROT(_PAGE_WRITE),PPROT(_PAGE_USER)); -} - -static inline void print_vma(struct vm_area_struct *vma) -{ - printk("vma start 0x%08lx\n", vma->vm_start); - printk("vma end 0x%08lx\n", vma->vm_end); - - print_prots(vma->vm_page_prot); - printk("vm_flags 0x%08lx\n", vma->vm_flags); -} - -static inline void print_task(struct task_struct *tsk) -{ - printk("Task pid %d\n", task_pid_nr(tsk)); -} - static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address) { pgd_t *dir; @@ -95,6 +68,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, struct mm_struct *mm; struct vm_area_struct * vma; const struct exception_table_entry *fixup; + unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + (writeaccess ? FAULT_FLAG_WRITE : 0)); pte_t *pte; int fault; @@ -124,47 +99,20 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, if (in_atomic() || !mm) goto no_context; +retry: /* TLB misses upon some cache flushes get done under cli() */ down_read(&mm->mmap_sem); vma = find_vma(mm, address); - - if (!vma) { -#ifdef DEBUG_FAULT - print_task(tsk); - printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", - __func__, __LINE__, - address,regs->pc,textaccess,writeaccess); - show_regs(regs); -#endif + if (!vma) goto bad_area; - } - if (vma->vm_start <= address) { + if (vma->vm_start <= address) goto good_area; - } - - if (!(vma->vm_flags & VM_GROWSDOWN)) { -#ifdef DEBUG_FAULT - print_task(tsk); - printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", - __func__, __LINE__, - address,regs->pc,textaccess,writeaccess); - show_regs(regs); - - print_vma(vma); -#endif + if (!(vma->vm_flags & VM_GROWSDOWN)) goto bad_area; - } - if (expand_stack(vma, address)) { -#ifdef DEBUG_FAULT - print_task(tsk); - printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", - __func__, __LINE__, - address,regs->pc,textaccess,writeaccess); - show_regs(regs); -#endif + if (expand_stack(vma, address)) goto bad_area; - } + /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. @@ -188,7 +136,11 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -197,14 +149,27 @@ good_area: BUG(); } - if (fault & VM_FAULT_MAJOR) { - tsk->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, - regs, address); - } else { - tsk->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, - regs, address); + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + tsk->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, + regs, address); + } else { + tsk->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, + regs, address); + } + + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + goto retry; + } } /* If we get here, the page fault has been handled. Do the TLB refill @@ -231,9 +196,6 @@ no_pte: * Fix it, but check if it's kernel or user first.. */ bad_area: -#ifdef DEBUG_FAULT - printk("fault:bad area\n"); -#endif up_read(&mm->mmap_sem); if (user_mode(regs)) { @@ -246,9 +208,6 @@ bad_area: printk("user mode bad_area address=%08lx pid=%d (%s) pc=%08lx\n", address, task_pid_nr(current), current->comm, (unsigned long) regs->pc); -#if 0 - show_regs(regs); -#endif } if (is_global_init(tsk)) { panic("INIT had user mode bad_area\n"); @@ -263,9 +222,6 @@ bad_area: } no_context: -#ifdef DEBUG_FAULT - printk("fault:No context\n"); -#endif /* Are we prepared to handle this kernel fault? */ fixup = search_exception_tables(regs->pc); if (fixup) { |