diff options
Diffstat (limited to 'arch/microblaze/mm/fault.c')
| -rw-r--r-- | arch/microblaze/mm/fault.c | 50 |
1 files changed, 38 insertions, 12 deletions
diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c index 57bd2a09610..fa4cf52aa7a 100644 --- a/arch/microblaze/mm/fault.c +++ b/arch/microblaze/mm/fault.c @@ -32,8 +32,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/mmu.h> -#include <asm/mmu_context.h> -#include <asm/system.h> +#include <linux/mmu_context.h> #include <linux/uaccess.h> #include <asm/exceptions.h> @@ -48,7 +47,7 @@ static int store_updates_sp(struct pt_regs *regs) { unsigned int inst; - if (get_user(inst, (unsigned int *)regs->pc)) + if (get_user(inst, (unsigned int __user *)regs->pc)) return 0; /* check for 1 in the rD field */ if (((inst >> 21) & 0x1f) != 1) @@ -93,13 +92,14 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, int code = SEGV_MAPERR; int is_write = error_code & ESR_S; int fault; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; regs->ear = address; regs->esr = error_code; /* On a kernel SLB miss we can only check for a valid exception entry */ if (unlikely(kernel_mode(regs) && (address >= TASK_SIZE))) { - printk(KERN_WARNING "kernel task_size exceed"); + pr_warn("kernel task_size exceed"); _exception(SIGSEGV, regs, code, address); } @@ -113,13 +113,16 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, /* in_atomic() in user mode is really bad, as is current->mm == NULL. */ - printk(KERN_EMERG "Page fault in user mode with " - "in_atomic(), mm = %p\n", mm); - printk(KERN_EMERG "r15 = %lx MSR = %lx\n", + pr_emerg("Page fault in user mode with in_atomic(), mm = %p\n", + mm); + pr_emerg("r15 = %lx MSR = %lx\n", regs->r15, regs->msr); die("Weird page fault", regs, SIGSEGV); } + if (user_mode(regs)) + flags |= FAULT_FLAG_USER; + /* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the * kernel and should generate an OOPS. Unfortunately, in the case of an @@ -139,6 +142,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, if (kernel_mode(regs) && !search_exception_tables(regs->pc)) goto bad_area_nosemaphore; +retry: down_read(&mm->mmap_sem); } @@ -197,6 +201,7 @@ good_area: if (unlikely(is_write)) { if (unlikely(!(vma->vm_flags & VM_WRITE))) goto bad_area; + flags |= FAULT_FLAG_WRITE; /* a read */ } else { /* protection fault */ @@ -211,7 +216,11 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - fault = handle_mm_fault(mm, vma, address, is_write ? 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; @@ -219,11 +228,28 @@ good_area: goto do_sigbus; BUG(); } - if (unlikely(fault & VM_FAULT_MAJOR)) - current->maj_flt++; - else - current->min_flt++; + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (unlikely(fault & VM_FAULT_MAJOR)) + current->maj_flt++; + else + current->min_flt++; + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + flags |= FAULT_FLAG_TRIED; + + /* + * 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); + /* * keep track of tlb+htab misses that are good addrs but * just need pte's created via handle_mm_fault() |
