]> Pileus Git - ~andy/linux/blobdiff - arch/powerpc/mm/fault.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[~andy/linux] / arch / powerpc / mm / fault.c
index 8726779e1409b5da36c1e1ea852276cf47d4252c..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 */
@@ -443,8 +451,12 @@ good_area:
                                      regs, address);
 #ifdef CONFIG_PPC_SMLPAR
                        if (firmware_has_feature(FW_FEATURE_CMO)) {
+                               u32 page_ins;
+
                                preempt_disable();
-                               get_lppaca()->page_ins += (1 << PAGE_FACTOR);
+                               page_ins = be32_to_cpu(get_lppaca()->page_ins);
+                               page_ins += 1 << PAGE_FACTOR;
+                               get_lppaca()->page_ins = cpu_to_be32(page_ins);
                                preempt_enable();
                        }
 #endif /* CONFIG_PPC_SMLPAR */