tracing: Make sure rcu_irq_enter() can work for trace_*_rcuidle() trace events
authorSteven Rostedt (VMware) <rostedt@goodmis.org>
Fri, 7 Apr 2017 16:40:49 +0000 (12:40 -0400)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Mon, 10 Apr 2017 19:22:17 +0000 (15:22 -0400)
Stack tracing discovered that there's a small location inside the RCU
infrastructure where calling rcu_irq_enter() does not work. As trace events
use rcu_irq_enter() it must make sure that it is functionable. A check
against rcu_irq_enter_disabled() is added with a WARN_ON_ONCE() as no trace
event should ever be used in that part of RCU. If the warning is triggered,
then the trace event is ignored.

Restructure the __DO_TRACE() a bit to get rid of the prercu and postrcu,
and just have an rcucheck that does the work from within the _DO_TRACE()
macro. gcc optimization will compile out the rcucheck=0 case.

Link: http://lkml.kernel.org/r/20170405093207.404f8deb@gandalf.local.home
Acked-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
include/linux/tracepoint.h

index f72fcfe0e66a80a80ae59a175e21abfba5b5ca42..cc48cb2ce20965a412037bfe3a8565991fcb9611 100644 (file)
@@ -128,7 +128,7 @@ extern void syscall_unregfunc(void);
  * as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just
  * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto".
  */
-#define __DO_TRACE(tp, proto, args, cond, prercu, postrcu)             \
+#define __DO_TRACE(tp, proto, args, cond, rcucheck)                    \
        do {                                                            \
                struct tracepoint_func *it_func_ptr;                    \
                void *it_func;                                          \
@@ -136,7 +136,11 @@ extern void syscall_unregfunc(void);
                                                                        \
                if (!(cond))                                            \
                        return;                                         \
-               prercu;                                                 \
+               if (rcucheck) {                                         \
+                       if (WARN_ON_ONCE(rcu_irq_enter_disabled()))     \
+                               return;                                 \
+                       rcu_irq_enter_irqson();                         \
+               }                                                       \
                rcu_read_lock_sched_notrace();                          \
                it_func_ptr = rcu_dereference_sched((tp)->funcs);       \
                if (it_func_ptr) {                                      \
@@ -147,20 +151,19 @@ extern void syscall_unregfunc(void);
                        } while ((++it_func_ptr)->func);                \
                }                                                       \
                rcu_read_unlock_sched_notrace();                        \
-               postrcu;                                                \
+               if (rcucheck)                                           \
+                       rcu_irq_exit_irqson();                          \
        } while (0)
 
 #ifndef MODULE
-#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args)    \
+#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args) \
        static inline void trace_##name##_rcuidle(proto)                \
        {                                                               \
                if (static_key_false(&__tracepoint_##name.key))         \
                        __DO_TRACE(&__tracepoint_##name,                \
                                TP_PROTO(data_proto),                   \
                                TP_ARGS(data_args),                     \
-                               TP_CONDITION(cond),                     \
-                               rcu_irq_enter_irqson(),                 \
-                               rcu_irq_exit_irqson());                 \
+                               TP_CONDITION(cond), 1);                 \
        }
 #else
 #define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args)
@@ -186,7 +189,7 @@ extern void syscall_unregfunc(void);
                        __DO_TRACE(&__tracepoint_##name,                \
                                TP_PROTO(data_proto),                   \
                                TP_ARGS(data_args),                     \
-                               TP_CONDITION(cond),,);                  \
+                               TP_CONDITION(cond), 0);                 \
                if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) {             \
                        rcu_read_lock_sched_notrace();                  \
                        rcu_dereference_sched(__tracepoint_##name.funcs);\