]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86_64/mm/fault.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild
[linux-2.6-omap-h63xx.git] / arch / x86_64 / mm / fault.c
index 49e8cf2e06f8eca9d016386d8e91ec95c3de26b4..84f11728fc76e8438907be59f187ffb7fef96d72 100644 (file)
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
-#include <linux/smp_lock.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>             /* For unblank_screen() */
 #include <linux/compiler.h>
+#include <linux/vmalloc.h>
 #include <linux/module.h>
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
+#include <linux/kdebug.h>
 
 #include <asm/system.h>
 #include <asm/pgalloc.h>
 #include <asm/smp.h>
 #include <asm/tlbflush.h>
 #include <asm/proto.h>
-#include <asm/kdebug.h>
 #include <asm-generic/sections.h>
 
 /* Page fault error code bits */
@@ -56,17 +56,17 @@ int unregister_page_fault_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(unregister_page_fault_notifier);
 
-static inline int notify_page_fault(enum die_val val, const char *str,
-                       struct pt_regs *regs, long err, int trap, int sig)
+static inline int notify_page_fault(struct pt_regs *regs, long err)
 {
        struct die_args args = {
                .regs = regs,
-               .str = str,
+               .str = "page fault",
                .err = err,
-               .trapnr = trap,
-               .signr = sig
+               .trapnr = 14,
+               .signr = SIGSEGV
        };
-       return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
+       return atomic_notifier_call_chain(&notify_page_fault_chain,
+                                         DIE_PAGE_FAULT, &args);
 }
 
 /* Sometimes the CPU reports invalid exceptions on prefetch.
@@ -317,7 +317,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
        struct vm_area_struct * vma;
        unsigned long address;
        const struct exception_table_entry *fixup;
-       int write;
+       int write, fault;
        unsigned long flags;
        siginfo_t info;
 
@@ -355,8 +355,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
                        if (vmalloc_fault(address) >= 0)
                                return;
                }
-               if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
-                                               SIGSEGV) == NOTIFY_STOP)
+               if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
                        return;
                /*
                 * Don't take the mm semaphore here. If we fixup a prefetch
@@ -365,8 +364,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
                goto bad_area_nosemaphore;
        }
 
-       if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
-                                       SIGSEGV) == NOTIFY_STOP)
+       if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
                return;
 
        if (likely(regs->eflags & X86_EFLAGS_IF))
@@ -452,19 +450,18 @@ good_area:
         * make sure we exit gracefully rather than endlessly redo
         * the fault.
         */
-       switch (handle_mm_fault(mm, vma, address, write)) {
-       case VM_FAULT_MINOR:
-               tsk->min_flt++;
-               break;
-       case VM_FAULT_MAJOR:
-               tsk->maj_flt++;
-               break;
-       case VM_FAULT_SIGBUS:
-               goto do_sigbus;
-       default:
-               goto out_of_memory;
+       fault = handle_mm_fault(mm, vma, address, write);
+       if (unlikely(fault & VM_FAULT_ERROR)) {
+               if (fault & VM_FAULT_OOM)
+                       goto out_of_memory;
+               else if (fault & VM_FAULT_SIGBUS)
+                       goto do_sigbus;
+               BUG();
        }
-
+       if (fault & VM_FAULT_MAJOR)
+               tsk->maj_flt++;
+       else
+               tsk->min_flt++;
        up_read(&mm->mmap_sem);
        return;
 
@@ -478,6 +475,12 @@ bad_area:
 bad_area_nosemaphore:
        /* User mode accesses just cause a SIGSEGV */
        if (error_code & PF_USER) {
+
+               /*
+                * It's possible to have interrupts off here.
+                */
+               local_irq_enable();
+
                if (is_prefetch(regs, address, error_code))
                        return;
 
@@ -587,7 +590,7 @@ do_sigbus:
 }
 
 DEFINE_SPINLOCK(pgd_lock);
-struct page *pgd_list;
+LIST_HEAD(pgd_list);
 
 void vmalloc_sync_all(void)
 {
@@ -607,8 +610,7 @@ void vmalloc_sync_all(void)
                        if (pgd_none(*pgd_ref))
                                continue;
                        spin_lock(&pgd_lock);
-                       for (page = pgd_list; page;
-                            page = (struct page *)page->index) {
+                       list_for_each_entry(page, &pgd_list, lru) {
                                pgd_t *pgd;
                                pgd = (pgd_t *)page_address(page) + pgd_index(address);
                                if (pgd_none(*pgd))