ftrace: Add use of synchronize_rcu_tasks() with dynamic trampolines
authorSteven Rostedt (VMware) <rostedt@goodmis.org>
Thu, 6 Apr 2017 14:28:12 +0000 (10:28 -0400)
committerSteven Rostedt (VMware) <rostedt@goodmis.org>
Fri, 7 Apr 2017 13:41:51 +0000 (09:41 -0400)
The function tracer needs to be more careful than other subsystems when it
comes to freeing data. Especially if that data is actually executable code.
When a single function is traced, a trampoline can be dynamically allocated
which is called to jump to the function trace callback. When the callback is
no longer needed, the dynamic allocated trampoline needs to be freed. This
is where the issues arise. The dynamically allocated trampoline must not be
used again. As function tracing can trace all subsystems, including
subsystems that are used to serialize aspects of freeing (namely RCU), it
must take extra care when doing the freeing.

Before synchronize_rcu_tasks() was around, there was no way for the function
tracer to know that nothing was using the dynamically allocated trampoline
when CONFIG_PREEMPT was enabled. That's because a task could be indefinitely
preempted while sitting on the trampoline. Now with synchronize_rcu_tasks(),
it will wait till all tasks have either voluntarily scheduled (not on the
trampoline) or goes into userspace (not on the trampoline). Then it is safe
to free the trampoline even with CONFIG_PREEMPT set.

Acked-by: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
kernel/trace/Kconfig
kernel/trace/ftrace.c

index d4a06e714645df56f75db97ba6bb052534a4bb41..67b463b4f169eff1222ac658c4a88a784f9a044d 100644 (file)
@@ -134,7 +134,8 @@ config FUNCTION_TRACER
        select KALLSYMS
        select GENERIC_TRACER
        select CONTEXT_SWITCH_TRACER
-        select GLOB
+       select GLOB
+       select TASKS_RCU if PREEMPT
        help
          Enable the kernel to trace every kernel function. This is done
          by using a compiler feature to insert a small, 5-byte No-Operation
index 8efd9fe7aec0fd093350acb2421c7f0a36a488bb..34f63e78d6614d636416e7f97a063875f7373d88 100644 (file)
@@ -2808,18 +2808,28 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
         * callers are done before leaving this function.
         * The same goes for freeing the per_cpu data of the per_cpu
         * ops.
-        *
-        * Again, normal synchronize_sched() is not good enough.
-        * We need to do a hard force of sched synchronization.
-        * This is because we use preempt_disable() to do RCU, but
-        * the function tracers can be called where RCU is not watching
-        * (like before user_exit()). We can not rely on the RCU
-        * infrastructure to do the synchronization, thus we must do it
-        * ourselves.
         */
        if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
+               /*
+                * We need to do a hard force of sched synchronization.
+                * This is because we use preempt_disable() to do RCU, but
+                * the function tracers can be called where RCU is not watching
+                * (like before user_exit()). We can not rely on the RCU
+                * infrastructure to do the synchronization, thus we must do it
+                * ourselves.
+                */
                schedule_on_each_cpu(ftrace_sync);
 
+               /*
+                * When the kernel is preeptive, tasks can be preempted
+                * while on a ftrace trampoline. Just scheduling a task on
+                * a CPU is not good enough to flush them. Calling
+                * synchornize_rcu_tasks() will wait for those tasks to
+                * execute and either schedule voluntarily or enter user space.
+                */
+               if (IS_ENABLED(CONFIG_PREEMPT))
+                       synchronize_rcu_tasks();
+
                arch_ftrace_trampoline_free(ops);
 
                if (ops->flags & FTRACE_OPS_FL_PER_CPU)
@@ -5366,22 +5376,6 @@ void __weak arch_ftrace_update_trampoline(struct ftrace_ops *ops)
 
 static void ftrace_update_trampoline(struct ftrace_ops *ops)
 {
-
-/*
- * Currently there's no safe way to free a trampoline when the kernel
- * is configured with PREEMPT. That is because a task could be preempted
- * when it jumped to the trampoline, it may be preempted for a long time
- * depending on the system load, and currently there's no way to know
- * when it will be off the trampoline. If the trampoline is freed
- * too early, when the task runs again, it will be executing on freed
- * memory and crash.
- */
-#ifdef CONFIG_PREEMPT
-       /* Currently, only non dynamic ops can have a trampoline */
-       if (ops->flags & FTRACE_OPS_FL_DYNAMIC)
-               return;
-#endif
-
        arch_ftrace_update_trampoline(ops);
 }