]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: support for page fault and general protection fault detection
authorKris Van Hees <kris.van.hees@oracle.com>
Wed, 14 Dec 2011 05:09:14 +0000 (00:09 -0500)
committerNick Alcock <nick.alcock@oracle.com>
Mon, 29 Jun 2015 21:40:23 +0000 (22:40 +0100)
This ensures that DTrace memory access faults are non-fatal.

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
arch/x86/mm/fault.c
include/linux/dtrace_cpu.h [new file with mode: 0644]
kernel/dtrace/Makefile
kernel/dtrace/dtrace_cpu.c [new file with mode: 0644]
kernel/dtrace/dtrace_os.c

index 181c53bac3a7ee8881b8844bae66b951d9beecde..0c214252685cf708eed5974cf9c0c43b9fdaea7e 100644 (file)
@@ -701,8 +701,13 @@ no_context(struct pt_regs *regs, unsigned long error_code,
 
        /*
         * Oops. The kernel tried to access some bad page. We'll have to
-        * terminate things with extreme prejudice:
+        * terminate things with extreme prejudice, unless a notifier decides
+        * to let this one slide.
         */
+       if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+                      SIGKILL) == NOTIFY_STOP)
+               return;
+
        flags = oops_begin();
 
        show_fault_oops(regs, error_code, address);
diff --git a/include/linux/dtrace_cpu.h b/include/linux/dtrace_cpu.h
new file mode 100644 (file)
index 0000000..37fd6d3
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef _DTRACE_CPU_H_
+#define _DTRACE_CPU_H_
+
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#define CPUC_SIZE      (sizeof (uint16_t) + sizeof(uint8_t) + \
+                        sizeof(uintptr_t) + sizeof(struct mutex))
+#define CPUC_PADSIZE   (192 - CPUC_SIZE)
+
+typedef struct cpu_core {
+       uint16_t cpuc_dtrace_flags;
+       uint8_t cpuc_dcpc_intr_state;
+       uint8_t cpuc_pad[CPUC_PADSIZE];
+       uintptr_t cpuc_dtrace_illval;
+       struct mutex cpuc_pid_lock;
+
+       uintptr_t cpu_dtrace_caller;
+       ktime_t cpu_dtrace_chillmark;
+       ktime_t cpu_dtrace_chilled;
+} cpu_core_t;
+
+DECLARE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info);
+
+#define per_cpu_core(cpu)      (&per_cpu(dtrace_cpu_info, (cpu)))
+#define this_cpu_core          (&__get_cpu_var(dtrace_cpu_info))
+
+#define DTRACE_CPUFLAG_ISSET(flag) \
+       (this_cpu_core->cpuc_dtrace_flags & (flag))
+
+#define DTRACE_CPUFLAG_SET(flag) \
+       (this_cpu_core->cpuc_dtrace_flags |= (flag))
+
+#define DTRACE_CPUFLAG_CLEAR(flag) \
+       (this_cpu_core->cpuc_dtrace_flags &= ~(flag))
+
+#define CPU_DTRACE_NOFAULT     0x0001
+#define CPU_DTRACE_DROP                0x0002
+#define CPU_DTRACE_BADADDR     0x0004
+#define CPU_DTRACE_BADALIGN    0x0008
+#define CPU_DTRACE_DIVZERO     0x0010
+#define CPU_DTRACE_ILLOP       0x0020
+#define CPU_DTRACE_NOSCRATCH   0x0040
+#define CPU_DTRACE_KPRIV       0x0080
+#define CPU_DTRACE_UPRIV       0x0100
+#define CPU_DTRACE_TUPOFLOW    0x0200
+#define CPU_DTRACE_ENTRY       0x0800
+#define CPU_DTRACE_BADSTACK    0x1000
+
+#define CPU_DTRACE_FAULT       (CPU_DTRACE_BADADDR | CPU_DTRACE_BADALIGN | \
+                                CPU_DTRACE_DIVZERO | CPU_DTRACE_ILLOP | \
+                                CPU_DTRACE_NOSCRATCH | CPU_DTRACE_KPRIV | \
+                                CPU_DTRACE_UPRIV | CPU_DTRACE_TUPOFLOW | \
+                                CPU_DTRACE_BADSTACK)
+#define CPU_DTRACE_ERROR       (CPU_DTRACE_FAULT | CPU_DTRACE_DROP)
+
+#endif /* _DTRACE_CPU_H_ */
index 1713210d969e212c737dab3e7d352ed8affe1c4d..957a01cef24f51728a824a806fc06089ee3ecbfa 100644 (file)
@@ -5,6 +5,6 @@
 GCOV_PROFILE := y
 
 ifdef CONFIG_DT_CORE
-obj-y                          += dtrace_os.o dtrace_stubs_x86_64.o \
-                                  sdt_register.o
+obj-y                          += dtrace_os.o dtrace_cpu.o \
+                                  dtrace_stubs_x86_64.o sdt_register.o
 endif
diff --git a/kernel/dtrace/dtrace_cpu.c b/kernel/dtrace/dtrace_cpu.c
new file mode 100644 (file)
index 0000000..4b1d249
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * FILE:       dtrace_cpu.c
+ * DESCRIPTION:        Dynamic Tracing: CPU info - part of kernel core
+ *
+ * Copyright (C) 2010, 2011 Oracle Corporation
+ */
+
+#include <linux/dtrace_cpu.h>
+#include <asm/percpu.h>
+
+DEFINE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info);
+EXPORT_PER_CPU_SYMBOL(dtrace_cpu_info);
index 2851e1528337317161cb4fa7de07abd95d77bf38..821a6f610512fa3cd8561a19e1874ba83c555d81 100644 (file)
@@ -6,16 +6,17 @@
  */
 
 #include <linux/cyclic.h>
+#include <linux/dtrace_cpu.h>
 #include <linux/dtrace_os.h>
+#include <linux/fs.h>
 #include <linux/hrtimer.h>
 #include <linux/kdebug.h>
 #include <linux/module.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <asm/insn.h>
 #include <asm/stacktrace.h>
-
-#include <linux/fs.h>
-#include <linux/sched.h>
 #include <asm/syscalls.h>
 
 /*
@@ -248,10 +249,42 @@ static int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
        dtrace_invop_hdlr_t     *hdlr;
        int                     rval = 0;
 
+       if (val == DIE_PAGE_FAULT) {
+               unsigned long   addr = read_cr2();
+               struct insn     insn;
+
+               if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+                       return NOTIFY_DONE;
+
+               DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+               this_cpu_core->cpuc_dtrace_illval = addr;
+
+               kernel_insn_init(&insn, (void *)dargs->regs->ip);
+               insn_get_length(&insn);
+
+               dargs->regs->ip += insn.length;
+
+               return NOTIFY_OK | NOTIFY_STOP_MASK;
+       }
+       if (val == DIE_GPF) {
+               struct insn     insn;
+
+               if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+                       return NOTIFY_DONE;
+
+               DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+
+               kernel_insn_init(&insn, (void *)dargs->regs->ip);
+               insn_get_length(&insn);
+
+               dargs->regs->ip += insn.length;
+
+               return NOTIFY_DONE;
+       }
+
        if (val != DIE_TRAP || dargs->trapnr != 6)
                return NOTIFY_DONE;
 
-printk(KERN_INFO "dtrace_die_notifier: TRAP %d, IP %lx\n", dargs->trapnr, dargs->regs->ip);
 
        for (hdlr = dtrace_invop_hdlrs; hdlr != NULL; hdlr = hdlr->dtih_next) {
                if ((rval = hdlr->dtih_func(dargs->regs)) != 0)
@@ -261,7 +294,6 @@ printk(KERN_INFO "dtrace_die_notifier: TRAP %d, IP %lx\n", dargs->trapnr, dargs-
        if (rval != 0) {
                dargs->regs->ip++;
 
-printk(KERN_INFO "dtrace_die_notifier: TRAP %d, New IP %lx\n", dargs->trapnr, dargs->regs->ip);
                return NOTIFY_OK | NOTIFY_STOP_MASK;
        }