aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2014-02-07 14:42:01 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-13 16:48:03 -0500
commitb6c5a8d32c2e2fea5a574365cef91bbfe75d861c (patch)
tree670e73b402865f56e09bc659b8f3f3b0a85a712c /kernel/trace
parenta0d0a2a5a36361f7695bb243bf7ea499f0c82fd2 (diff)
ftrace: Fix synchronization location disabling and freeing ftrace_ops
commit a4c35ed241129dd142be4cadb1e5a474a56d5464 upstream. The synchronization needed after ftrace_ops are unregistered must happen after the callback is disabled from becing called by functions. The current location happens after the function is being removed from the internal lists, but not after the function callbacks were disabled, leaving the functions susceptible of being called after their callbacks are freed. This affects perf and any externel users of function tracing (LTTng and SystemTap). Fixes: cdbe61bfe704 "ftrace: Allow dynamically allocated function tracers" Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/ftrace.c50
1 files changed, 32 insertions, 18 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index fc21312dad6d..17b95a492948 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -490,16 +490,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
490 } else if (ops->flags & FTRACE_OPS_FL_CONTROL) { 490 } else if (ops->flags & FTRACE_OPS_FL_CONTROL) {
491 ret = remove_ftrace_list_ops(&ftrace_control_list, 491 ret = remove_ftrace_list_ops(&ftrace_control_list,
492 &control_ops, ops); 492 &control_ops, ops);
493 if (!ret) {
494 /*
495 * The ftrace_ops is now removed from the list,
496 * so there'll be no new users. We must ensure
497 * all current users are done before we free
498 * the control data.
499 */
500 synchronize_sched();
501 control_ops_free(ops);
502 }
503 } else 493 } else
504 ret = remove_ftrace_ops(&ftrace_ops_list, ops); 494 ret = remove_ftrace_ops(&ftrace_ops_list, ops);
505 495
@@ -509,13 +499,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
509 if (ftrace_enabled) 499 if (ftrace_enabled)
510 update_ftrace_function(); 500 update_ftrace_function();
511 501
512 /*
513 * Dynamic ops may be freed, we must make sure that all
514 * callers are done before leaving this function.
515 */
516 if (ops->flags & FTRACE_OPS_FL_DYNAMIC)
517 synchronize_sched();
518
519 return 0; 502 return 0;
520} 503}
521 504
@@ -2184,10 +2167,41 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
2184 command |= FTRACE_UPDATE_TRACE_FUNC; 2167 command |= FTRACE_UPDATE_TRACE_FUNC;
2185 } 2168 }
2186 2169
2187 if (!command || !ftrace_enabled) 2170 if (!command || !ftrace_enabled) {
2171 /*
2172 * If these are control ops, they still need their
2173 * per_cpu field freed. Since, function tracing is
2174 * not currently active, we can just free them
2175 * without synchronizing all CPUs.
2176 */
2177 if (ops->flags & FTRACE_OPS_FL_CONTROL)
2178 control_ops_free(ops);
2188 return 0; 2179 return 0;
2180 }
2189 2181
2190 ftrace_run_update_code(command); 2182 ftrace_run_update_code(command);
2183
2184 /*
2185 * Dynamic ops may be freed, we must make sure that all
2186 * callers are done before leaving this function.
2187 * The same goes for freeing the per_cpu data of the control
2188 * ops.
2189 *
2190 * Again, normal synchronize_sched() is not good enough.
2191 * We need to do a hard force of sched synchronization.
2192 * This is because we use preempt_disable() to do RCU, but
2193 * the function tracers can be called where RCU is not watching
2194 * (like before user_exit()). We can not rely on the RCU
2195 * infrastructure to do the synchronization, thus we must do it
2196 * ourselves.
2197 */
2198 if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_CONTROL)) {
2199 schedule_on_each_cpu(ftrace_sync);
2200
2201 if (ops->flags & FTRACE_OPS_FL_CONTROL)
2202 control_ops_free(ops);
2203 }
2204
2191 return 0; 2205 return 0;
2192} 2206}
2193 2207