]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: install the die notifier hook whenever DTrace is enabled
authorKris Van Hees <kris.van.hees@oracle.com>
Wed, 11 Jan 2012 06:18:41 +0000 (01:18 -0500)
committerNick Alcock <nick.alcock@oracle.com>
Mon, 29 Jun 2015 21:40:23 +0000 (22:40 +0100)
Page fault and general protection fault handling depends on it, and that
is needed for safe memory access support in DTrace.

Work around an apparent bug in Xen where an invalid opcode fault is delivered
as a general protection failure instead.

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
include/linux/dtrace_os.h
kernel/dtrace/dtrace_os.c

index d9436ec513a65b5f494b09b0268179e8647a0da1..87797613e360090bc55d3c78f7ffae0c068215e9 100644 (file)
@@ -18,6 +18,9 @@ typedef uint32_t dtrace_id_t;
 #define SCE_RT_SIGRETURN       6
 #define SCE_nr_stubs           7
 
+extern void dtrace_enable(void);
+extern void dtrace_disable(void);
+
 extern void dtrace_invop_add(int (*func)(struct pt_regs *));
 extern void dtrace_invop_remove(int (*func)(struct pt_regs *));
 
index 821a6f610512fa3cd8561a19e1874ba83c555d81..3a8caf87ec6ef51a32e62f19a101e57a12c359b2 100644 (file)
@@ -246,12 +246,11 @@ static int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
                               void *args)
 {
        struct die_args         *dargs = args;
-       dtrace_invop_hdlr_t     *hdlr;
-       int                     rval = 0;
+       struct insn             insn;
 
-       if (val == DIE_PAGE_FAULT) {
+       switch (val) {
+       case DIE_PAGE_FAULT: {
                unsigned long   addr = read_cr2();
-               struct insn     insn;
 
                if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
                        return NOTIFY_DONE;
@@ -266,44 +265,80 @@ static int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
 
                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);
-
+       case DIE_GPF: {
                kernel_insn_init(&insn, (void *)dargs->regs->ip);
                insn_get_length(&insn);
 
-               dargs->regs->ip += insn.length;
+               /*
+                * It would seem that the invalid opcode generated by the LOCK
+                * prefix (0xF0) used for SDT probe points may get delivered as
+                * a general protection failure on Xen.  We need to ignore them
+                * as general protection failures...
+                */
+               if (insn.length != 5 || insn.prefixes.bytes[0] != 0xf0 ||
+                   insn.opcode.bytes[0] != 0x90) {
+                       if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+                               return NOTIFY_DONE;
 
-               return NOTIFY_DONE;
-       }
+                       DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
 
-       if (val != DIE_TRAP || dargs->trapnr != 6)
-               return NOTIFY_DONE;
+                       dargs->regs->ip += insn.length;
 
+                       return NOTIFY_OK | NOTIFY_STOP_MASK;
+               }
 
-       for (hdlr = dtrace_invop_hdlrs; hdlr != NULL; hdlr = hdlr->dtih_next) {
-               if ((rval = hdlr->dtih_func(dargs->regs)) != 0)
-                       break;
+               /*
+                * ... and instead treat them as the SDT probe point traps that
+                * they are.
+                */
+               dargs->trapnr = 6;
        }
+       case DIE_TRAP: {
+               dtrace_invop_hdlr_t     *hdlr;
+               int                     rval = 0;
 
-       if (rval != 0) {
-               dargs->regs->ip++;
+               if (dargs->trapnr != 6)
+                       return NOTIFY_DONE;
 
-               return NOTIFY_OK | NOTIFY_STOP_MASK;
-       }
+               for (hdlr = dtrace_invop_hdlrs; hdlr != NULL;
+                    hdlr = hdlr->dtih_next) {
+                       if ((rval = hdlr->dtih_func(dargs->regs)) != 0)
+                               break;
+               }
+
+               if (rval != 0) {
+                       dargs->regs->ip += insn.length;
 
-       return NOTIFY_DONE;
+                       return NOTIFY_OK | NOTIFY_STOP_MASK;
+               }
+       }
+       default:
+               return NOTIFY_DONE;
+       }
 }
 
 static struct notifier_block   dtrace_die = {
        .notifier_call = dtrace_die_notifier,
 };
 
+static int     dtrace_enabled = 0;
+
+void dtrace_enable(void)
+{
+       if (!dtrace_enabled)
+               register_die_notifier(&dtrace_die);
+}
+EXPORT_SYMBOL(dtrace_enable);
+
+void dtrace_disable(void)
+{
+       if (!dtrace_enabled)
+               return;
+
+       unregister_die_notifier(&dtrace_die);
+}
+EXPORT_SYMBOL(dtrace_disable);
+
 void dtrace_invop_add(int (*func)(struct pt_regs *))
 {
        dtrace_invop_hdlr_t     *hdlr;
@@ -312,13 +347,6 @@ void dtrace_invop_add(int (*func)(struct pt_regs *))
        hdlr->dtih_func = func;
        hdlr->dtih_next = dtrace_invop_hdlrs;
        dtrace_invop_hdlrs = hdlr;
-
-       /*
-        * If this is the first DTrace invalid opcode handling, register the
-        * die notifier with the kernel notifier core.
-        */
-       if (hdlr->dtih_next == NULL)
-               register_die_notifier(&dtrace_die);
 }
 EXPORT_SYMBOL(dtrace_invop_add);
 
@@ -339,13 +367,6 @@ void dtrace_invop_remove(int (*func)(struct pt_regs *))
 
        if (prev == NULL) {
                dtrace_invop_hdlrs = hdlr->dtih_next;
-
-               /*
-                * If there are no invalid opcode handlers left, unregister
-                * from the kernel notifier core.
-                */
-               if (dtrace_invop_hdlrs == NULL)
-                       unregister_die_notifier(&dtrace_die);
        } else
                prev->dtih_next = hdlr->dtih_next;