ftrace: Have ftrace_ops_get_func() handle RCU and PER_CPU flags too
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>
Tue, 1 Dec 2015 18:28:16 +0000 (13:28 -0500)
committerSteven Rostedt <rostedt@goodmis.org>
Wed, 23 Dec 2015 19:27:19 +0000 (14:27 -0500)
Jiri Olsa noted that the change to replace the control_ops did not update
the trampoline for when running perf on a single CPU and with CONFIG_PREEMPT
disabled (where dynamic ops, like perf, can use trampolines directly). The
result was that perf function could be called when RCU is not watching as
well as not handle the ftrace_local_disable().

Modify the ftrace_ops_get_func() to also check the RCU and PER_CPU ops flags
and use the recursive function if they are set. The recursive function is
modified to check those flags and execute the appropriate checks if they are
set.

Link: http://lkml.kernel.org/r/20151201134213.GA14155@krava.brq.redhat.com
Reported-by: Jiri Olsa <jolsa@redhat.com>
Patch-fixed-up-by: Jiri Olsa <jolsa@redhat.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
kernel/trace/ftrace.c

index bc7f4eb..e290a30 100644 (file)
@@ -115,9 +115,6 @@ static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
 ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
 static struct ftrace_ops global_ops;
 
-static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
-                                  struct ftrace_ops *op, struct pt_regs *regs);
-
 #if ARCH_SUPPORTS_FTRACE_OPS
 static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
                                 struct ftrace_ops *op, struct pt_regs *regs);
@@ -5231,20 +5228,29 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip)
 
 /*
  * If there's only one function registered but it does not support
- * recursion, this function will be called by the mcount trampoline.
- * This function will handle recursion protection.
+ * recursion, needs RCU protection and/or requires per cpu handling, then
+ * this function will be called by the mcount trampoline.
  */
-static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
+static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
                                   struct ftrace_ops *op, struct pt_regs *regs)
 {
        int bit;
 
+       if ((op->flags & FTRACE_OPS_FL_RCU) && !rcu_is_watching())
+               return;
+
        bit = trace_test_and_set_recursion(TRACE_LIST_START, TRACE_LIST_MAX);
        if (bit < 0)
                return;
 
-       op->func(ip, parent_ip, op, regs);
+       preempt_disable_notrace();
 
+       if (!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
+           !ftrace_function_local_disabled(op)) {
+               op->func(ip, parent_ip, op, regs);
+       }
+
+       preempt_enable_notrace();
        trace_clear_recursion(bit);
 }
 
@@ -5262,12 +5268,12 @@ static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
 ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops)
 {
        /*
-        * If the func handles its own recursion, call it directly.
-        * Otherwise call the recursion protected function that
-        * will call the ftrace ops function.
+        * If the function does not handle recursion, needs to be RCU safe,
+        * or does per cpu logic, then we need to call the assist handler.
         */
-       if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE))
-               return ftrace_ops_recurs_func;
+       if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE) ||
+           ops->flags & (FTRACE_OPS_FL_RCU | FTRACE_OPS_FL_PER_CPU))
+               return ftrace_ops_assist_func;
 
        return ops->func;
 }