From 146130c4dc9c061ca088e7e15825879b97792425 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Wed, 11 Jan 2012 01:18:41 -0500 Subject: [PATCH] dtrace: install the die notifier hook whenever DTrace is enabled 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 --- include/linux/dtrace_os.h | 3 ++ kernel/dtrace/dtrace_os.c | 99 ++++++++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/include/linux/dtrace_os.h b/include/linux/dtrace_os.h index d9436ec513a6..87797613e360 100644 --- a/include/linux/dtrace_os.h +++ b/include/linux/dtrace_os.h @@ -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 *)); diff --git a/kernel/dtrace/dtrace_os.c b/kernel/dtrace/dtrace_os.c index 821a6f610512..3a8caf87ec6e 100644 --- a/kernel/dtrace/dtrace_os.c +++ b/kernel/dtrace/dtrace_os.c @@ -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; -- 2.50.1