Commit 6331c28c962561aee59e5a493b7556a4bb585957

Authored by Steven Rostedt
Committed by Steven Rostedt
1 parent 072126f452

ftrace: Fix dynamic selftest failure on some archs

Archs that do not implement CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST, will
fail the dynamic ftrace selftest.

The function tracer has a quick 'off' variable that will prevent
the call back functions from being called. This variable is called
function_trace_stop. In x86, this is implemented directly in the mcount
assembly, but for other archs, an intermediate function is used called
ftrace_test_stop_func().

In dynamic ftrace, the function pointer variable ftrace_trace_function is
used to update the caller code in the mcount caller. But for archs that
do not have CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST set, it only calls
ftrace_test_stop_func() instead, which in turn calls __ftrace_trace_function.

When more than one ftrace_ops is registered, the function it calls is
ftrace_ops_list_func(), which will iterate over all registered ftrace_ops
and call the callbacks that have their hash matching.

The issue happens when two ftrace_ops are registered for different functions
and one is then unregistered. The __ftrace_trace_function is then pointed
to the remaining ftrace_ops callback function directly. This mean it will
be called for all functions that were registered to trace by both ftrace_ops
that were registered.

This is not an issue for archs with CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST,
because the update of ftrace_trace_function doesn't happen until after all
functions have been updated, and then the mcount caller is updated. But
for those archs that do use the ftrace_test_stop_func(), the update is
immediate.

The dynamic selftest fails because it hits this situation, and the
ftrace_ops that it registers fails to only trace what it was suppose to
and instead traces all other functions.

The solution is to delay the setting of __ftrace_trace_function until
after all the functions have been updated according to the registered
ftrace_ops. Also, function_trace_stop is set during the update to prevent
function tracing from calling code that is caused by the function tracer
itself.

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>

Showing 1 changed file with 26 additions and 0 deletions Side-by-side Diff

kernel/trace/ftrace.c
... ... @@ -88,6 +88,7 @@
88 88 static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;
89 89 static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
90 90 ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
  91 +static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;
91 92 ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
92 93 ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
93 94 static struct ftrace_ops global_ops;
94 95  
... ... @@ -146,9 +147,11 @@
146 147 {
147 148 ftrace_trace_function = ftrace_stub;
148 149 __ftrace_trace_function = ftrace_stub;
  150 + __ftrace_trace_function_delay = ftrace_stub;
149 151 ftrace_pid_function = ftrace_stub;
150 152 }
151 153  
  154 +#undef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
152 155 #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
153 156 /*
154 157 * For those archs that do not test ftrace_trace_stop in their
155 158  
... ... @@ -208,7 +211,12 @@
208 211 #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
209 212 ftrace_trace_function = func;
210 213 #else
  214 +#ifdef CONFIG_DYNAMIC_FTRACE
  215 + /* do not update till all functions have been modified */
  216 + __ftrace_trace_function_delay = func;
  217 +#else
211 218 __ftrace_trace_function = func;
  219 +#endif
212 220 ftrace_trace_function = ftrace_test_stop_func;
213 221 #endif
214 222 }
... ... @@ -1607,6 +1615,12 @@
1607 1615 {
1608 1616 int *command = data;
1609 1617  
  1618 + /*
  1619 + * Do not call function tracer while we update the code.
  1620 + * We are in stop machine, no worrying about races.
  1621 + */
  1622 + function_trace_stop++;
  1623 +
1610 1624 if (*command & FTRACE_ENABLE_CALLS)
1611 1625 ftrace_replace_code(1);
1612 1626 else if (*command & FTRACE_DISABLE_CALLS)
... ... @@ -1619,6 +1633,18 @@
1619 1633 ftrace_enable_ftrace_graph_caller();
1620 1634 else if (*command & FTRACE_STOP_FUNC_RET)
1621 1635 ftrace_disable_ftrace_graph_caller();
  1636 +
  1637 +#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
  1638 + /*
  1639 + * For archs that call ftrace_test_stop_func(), we must
  1640 + * wait till after we update all the function callers
  1641 + * before we update the callback. This keeps different
  1642 + * ops that record different functions from corrupting
  1643 + * each other.
  1644 + */
  1645 + __ftrace_trace_function = __ftrace_trace_function_delay;
  1646 +#endif
  1647 + function_trace_stop--;
1622 1648  
1623 1649 return 0;
1624 1650 }