]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: prevent Oops caused by preemption issues with probes
authorKris Van Hees <kris.van.hees@oracle.com>
Fri, 13 Sep 2013 14:36:07 +0000 (10:36 -0400)
committerKris Van Hees <kris.van.hees@oracle.com>
Tue, 17 Sep 2013 11:26:43 +0000 (07:26 -0400)
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 <kris.van.hees@oracle.com>
dtrace/dtrace_probe.c
dtrace/include/dtrace/dtrace_impl_defines.h

index b1c6edf05fc110ec138ff289de84ee56a77cfa29..30323781c399f20284ac44889ccb4c70c797f0cb 100644 (file)
@@ -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) {
index b493e97e58852bbe9b84fab900d550559c178b9b..64d89a1e013d54ee7f08f02a27f372b6959b3819 100644 (file)
@@ -33,6 +33,7 @@
  */
 
 #include <linux/dtrace/universal.h>
+#include <linux/preempt.h>
 #include <asm/ptrace.h>
 
 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 */