From 2c6c9aadeb7b5f8a60e18a4e44438879a7599f4e Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Fri, 13 Sep 2013 10:36:07 -0400 Subject: [PATCH] dtrace: prevent Oops caused by preemption issues with probes It was possible (specifically with direct-call probes) for the execution of actions (using the DIF emulator) to get preempted, causing interesting side effects because dtrace_probe() (and the functions it calls) are designed to run on a specific CPU without any interruption especially not from another call to dtrace_probe()). Since the UEK3 kernel uses voluntary preemption, the behaviour was not as expected and explicit preemption protection had to be added to resolve this. Orabug: 17403196 Signed-off-by: Kris Van Hees --- dtrace/dtrace_probe.c | 17 +++++++++++ dtrace/include/dtrace/dtrace_impl_defines.h | 31 ++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/dtrace/dtrace_probe.c b/dtrace/dtrace_probe.c index b1c6edf05fc1..30323781c399 100644 --- a/dtrace/dtrace_probe.c +++ b/dtrace/dtrace_probe.c @@ -162,7 +162,9 @@ void dtrace_probe_description(const dtrace_probe_t *prp, void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv) { +#ifdef FIXME struct module *mod; +#endif int all = 0; if (prv == NULL) { @@ -548,6 +550,7 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, int vtime, onintr; volatile uint16_t *flags; ktime_t now; + int pflag = 0; #ifdef FIXME /* @@ -559,7 +562,15 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, return; #endif + /* + * If preemption has already been disabled before we get here, we + * accept it as a free gift. We just need to make sure that we don't + * re-enable preemption on the way out... + */ local_irq_save(cookie); + if ((pflag = dtrace_is_preemptive())) + dtrace_preempt_off(); + probe = dtrace_probe_lookup_id(id); cpuid = smp_processor_id(); onintr = in_interrupt(); @@ -570,6 +581,8 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, * We have hit in the predicate cache; we know that * this predicate would evaluate to be false. */ + if (pflag) + dtrace_preempt_on(); local_irq_restore(cookie); return; } @@ -579,6 +592,8 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, /* * We don't trace anything if we're panicking. */ + if (pflag) + dtrace_preempt_on(); local_irq_restore(cookie); return; } @@ -1227,6 +1242,8 @@ void dtrace_probe(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1, if (vtime) current->dtrace_start = dtrace_gethrtime(); + if (pflag) + dtrace_preempt_on(); local_irq_restore(cookie); if (current->dtrace_sig != 0) { diff --git a/dtrace/include/dtrace/dtrace_impl_defines.h b/dtrace/include/dtrace/dtrace_impl_defines.h index b493e97e5885..64d89a1e013d 100644 --- a/dtrace/include/dtrace/dtrace_impl_defines.h +++ b/dtrace/include/dtrace/dtrace_impl_defines.h @@ -33,6 +33,7 @@ */ #include +#include #include typedef typeof(((struct pt_regs *)0)->ip) pc_t; @@ -205,20 +206,42 @@ typedef enum dtrace_vtime_state { # define mutex_lock(x) do { \ printk(KERN_DEBUG \ - "mutex_lock(%s) at %s::%d\n", \ + "mutex_lock(%s) at %s::%d " \ + "for %p(PID %d)\n", \ __stringify(x), \ - __FILE__, __LINE__); \ + __FILE__, __LINE__, current, \ + current ? current->pid : -1); \ _mutex_lock(x); \ } while (0) # define mutex_unlock(x) do { \ printk(KERN_DEBUG \ - "mutex_unlock(%s) at %s::%d\n", \ + "mutex_unlock(%s) at %s::%d" \ + "for %p(PID %d)\n", \ __stringify(x), \ - __FILE__, __LINE__); \ + __FILE__, __LINE__, current, \ + current ? current->pid : -1); \ _mutex_unlock(x); \ } while (0) #endif #define MUTEX_HELD(lock) mutex_owned(lock) +#ifdef CONFIG_PREEMPT_VOLUNTARY +# define dtrace_is_preemptive() (!(preempt_count() & PREEMPT_ACTIVE)) +# define dtrace_preempt_off() do { \ + add_preempt_count(PREEMPT_ACTIVE); \ + barrier(); \ + } while (0) +# define dtrace_preempt_on() do { \ + sub_preempt_count(PREEMPT_ACTIVE); \ + barrier(); \ + } while (0) +#endif + +#ifdef CONFIG_PREEMPT +# define dtrace_is_preemptive() (preempt_count() > 0) +# define dtrace_preempt_off() preempt_disable() +# define dtrace_preempt_on() preempt_enable_no_resched() +#endif + #endif /* _LINUX_DTRACE_IMPL_DEFINES_H */ -- 2.50.1