Merge branch 'linux-3.10.61' into dev-kernel-3.10
[linux-3.10.git] / arch / arm64 / mm / fault.c
index b5d4587..cdc3aac 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/sched.h>
 #include <linux/highmem.h>
 #include <linux/perf_event.h>
+#include <linux/fs.h>
 
 #include <asm/exception.h>
 #include <asm/debug-monitors.h>
@@ -36,6 +37,9 @@
 #include <asm/pgtable.h>
 #include <asm/tlbflush.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/pagefault.h>
+
 static const char *fault_name(unsigned int esr);
 
 /*
@@ -103,6 +107,25 @@ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
        do_exit(SIGKILL);
 }
 
+static void show_map(struct task_struct *tsk, u64 addr)
+{
+       struct vm_area_struct *vma;
+       struct file *file;
+       struct mm_struct *mm = tsk->mm;
+       char path[64];
+       char *p;
+
+       vma = find_vma(mm, addr);
+       if (!vma)
+               return;
+       file = vma->vm_file;
+       if (!file)
+               return;
+       p = d_path(&file->f_path, &path[0], 64);
+       if (IS_ERR(p))
+               return;
+       pr_alert("Library at 0x%llx: 0x%lx %s\n", addr, vma->vm_start, p);
+}
 /*
  * Something tried to access memory that isn't in our memory map. User mode
  * accesses just cause a SIGSEGV
@@ -120,6 +143,12 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
                        addr, esr);
                show_pte(tsk->mm, addr);
                show_regs(regs);
+               show_map(tsk, instruction_pointer(regs));
+               if (compat_user_mode(regs))
+                       show_map(tsk, regs->compat_lr);
+               else
+                       show_map(tsk, regs->regs[30]);
+               pr_alert("vdso base = 0x%lx\n", (unsigned long)tsk->mm->context.vdso);
        }
 
        tsk->thread.fault_address = addr;
@@ -130,7 +159,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
        force_sig_info(sig, &si, tsk);
 }
 
-void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
 {
        struct task_struct *tsk = current;
        struct mm_struct *mm = tsk->active_mm;
@@ -199,6 +228,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
        unsigned long vm_flags = VM_READ | VM_WRITE | VM_EXEC;
        unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
 
+       trace_pagefault_entry(addr);
+
        tsk = current;
        mm  = tsk->mm;
 
@@ -253,7 +284,7 @@ retry:
         * would already be released in __lock_page_or_retry in mm/filemap.c.
         */
        if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
-               return 0;
+               goto return0;
 
        /*
         * Major/minor page fault accounting is only done on the initial
@@ -289,7 +320,14 @@ retry:
         */
        if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP |
                              VM_FAULT_BADACCESS))))
-               return 0;
+               goto return0;
+
+       /*
+        * If we are in kernel mode at this point, we have no context to
+        * handle this fault with.
+        */
+       if (!user_mode(regs))
+               goto no_context;
 
        /*
         * If we are in kernel mode at this point, we have no context to
@@ -305,7 +343,7 @@ retry:
                 * oom-killed).
                 */
                pagefault_out_of_memory();
-               return 0;
+               goto return0;
        }
 
        if (fault & VM_FAULT_SIGBUS) {
@@ -326,10 +364,12 @@ retry:
        }
 
        __do_user_fault(tsk, addr, esr, sig, code, regs);
-       return 0;
+       goto return0;
 
 no_context:
        __do_kernel_fault(mm, addr, esr, regs);
+return0:
+       trace_pagefault_exit(addr);
        return 0;
 }