Commit 6331c28c962561aee59e5a493b7556a4bb585957
Committed by
Steven Rostedt
1 parent
072126f452
Exists in
master
and in
4 other branches
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 | } |
-
mentioned in commit c7c6ec