]> Pileus Git - ~andy/linux/blobdiff - arch/powerpc/mm/fault.c
Merge tag 'tty-3.12-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[~andy/linux] / arch / powerpc / mm / fault.c
index 76d8e7cc7805348098f622e0043a4364c9a7f1a1..51ab9e7e6c391b9497a08730a7b5e4c625c96304 100644 (file)
@@ -206,7 +206,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
        int trap = TRAP(regs);
        int is_exec = trap == 0x400;
        int fault;
-       int rc = 0;
+       int rc = 0, store_update_sp = 0;
 
 #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
        /*
@@ -223,9 +223,6 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
        is_write = error_code & ESR_DST;
 #endif /* CONFIG_4xx || CONFIG_BOOKE */
 
-       if (is_write)
-               flags |= FAULT_FLAG_WRITE;
-
 #ifdef CONFIG_PPC_ICSWX
        /*
         * we need to do this early because this "data storage
@@ -280,6 +277,17 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
 
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
 
+       /*
+        * We want to do this outside mmap_sem, because reading code around nip
+        * can result in fault, which will cause a deadlock when called with
+        * mmap_sem held
+        */
+       if (user_mode(regs))
+               store_update_sp = store_updates_sp(regs);
+
+       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
@@ -345,8 +353,7 @@ retry:
                 * between the last mapped region and the stack will
                 * expand the stack rather than segfaulting.
                 */
-               if (address + 2048 < uregs->gpr[1]
-                   && (!user_mode(regs) || !store_updates_sp(regs)))
+               if (address + 2048 < uregs->gpr[1] && !store_update_sp)
                        goto bad_area;
        }
        if (expand_stack(vma, address))
@@ -408,6 +415,7 @@ good_area:
        } else if (is_write) {
                if (!(vma->vm_flags & VM_WRITE))
                        goto bad_area;
+               flags |= FAULT_FLAG_WRITE;
        /* a read */
        } else {
                /* protection fault */