Skip to content

Commit

Permalink
ftrace: Have ftrace_ops_get_func() handle RCU and PER_CPU flags too
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rostedt committed Dec 23, 2015
1 parent ba27f2b commit c68c0fa
Showing 1 changed file with 18 additions and 12 deletions.
30 changes: 18 additions & 12 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand All @@ -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;
}
Expand Down

0 comments on commit c68c0fa

Please sign in to comment.