Commit 4184e5ec authored by Pratyush Anand's avatar Pratyush Anand Committed by Sasha Levin

ftrace: Fix en(dis)able graph caller when en(dis)abling record via sysctl

[ Upstream commit 1619dc3f ]

When ftrace is enabled globally through the proc interface, we must check if
ftrace_graph_active is set. If it is set, then we should also pass the
FTRACE_START_FUNC_RET command to ftrace_run_update_code(). Similarly, when
ftrace is disabled globally through the proc interface, we must check if
ftrace_graph_active is set. If it is set, then we should also pass the
FTRACE_STOP_FUNC_RET command to ftrace_run_update_code().

Consider the following situation.

 # echo 0 > /proc/sys/kernel/ftrace_enabled

After this ftrace_enabled = 0.

 # echo function_graph > /sys/kernel/debug/tracing/current_tracer

Since ftrace_enabled = 0, ftrace_enable_ftrace_graph_caller() is never
called.

 # echo 1 > /proc/sys/kernel/ftrace_enabled

Now ftrace_enabled will be set to true, but still
ftrace_enable_ftrace_graph_caller() will not be called, which is not
desired.

Further if we execute the following after this:
  # echo nop > /sys/kernel/debug/tracing/current_tracer

Now since ftrace_enabled is set it will call
ftrace_disable_ftrace_graph_caller(), which causes a kernel warning on
the ARM platform.

On the ARM platform, when ftrace_enable_ftrace_graph_caller() is called,
it checks whether the old instruction is a nop or not. If it's not a nop,
then it returns an error. If it is a nop then it replaces instruction at
that address with a branch to ftrace_graph_caller.
ftrace_disable_ftrace_graph_caller() behaves just the opposite. Therefore,
if generic ftrace code ever calls either ftrace_enable_ftrace_graph_caller()
or ftrace_disable_ftrace_graph_caller() consecutively two times in a row,
then it will return an error, which will cause the generic ftrace code to
raise a warning.

Note, x86 does not have an issue with this because the architecture
specific code for ftrace_enable_ftrace_graph_caller() and
ftrace_disable_ftrace_graph_caller() does not check the previous state,
and calling either of these functions twice in a row has no ill effect.

Link: http://lkml.kernel.org/r/e4fbe64cdac0dd0e86a3bf914b0f83c0b419f146.1425666454.git.panand@redhat.com

Cc: stable@vger.kernel.org # 2.6.31+
Signed-off-by: default avatarPratyush Anand <panand@redhat.com>
[
  removed extra if (ftrace_start_up) and defined ftrace_graph_active as 0
  if CONFIG_FUNCTION_GRAPH_TRACER is not set.
]
Signed-off-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent b4521c3d
...@@ -1053,6 +1053,12 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer) ...@@ -1053,6 +1053,12 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer)
static struct pid * const ftrace_swapper_pid = &init_struct_pid; static struct pid * const ftrace_swapper_pid = &init_struct_pid;
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static int ftrace_graph_active;
#else
# define ftrace_graph_active 0
#endif
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
static struct ftrace_ops *removed_ops; static struct ftrace_ops *removed_ops;
...@@ -2486,24 +2492,36 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) ...@@ -2486,24 +2492,36 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
static void ftrace_startup_sysctl(void) static void ftrace_startup_sysctl(void)
{ {
int command;
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return; return;
/* Force update next time */ /* Force update next time */
saved_ftrace_func = NULL; saved_ftrace_func = NULL;
/* ftrace_start_up is true if we want ftrace running */ /* ftrace_start_up is true if we want ftrace running */
if (ftrace_start_up) if (ftrace_start_up) {
ftrace_run_update_code(FTRACE_UPDATE_CALLS); command = FTRACE_UPDATE_CALLS;
if (ftrace_graph_active)
command |= FTRACE_START_FUNC_RET;
ftrace_run_update_code(command);
}
} }
static void ftrace_shutdown_sysctl(void) static void ftrace_shutdown_sysctl(void)
{ {
int command;
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return; return;
/* ftrace_start_up is true if ftrace is running */ /* ftrace_start_up is true if ftrace is running */
if (ftrace_start_up) if (ftrace_start_up) {
ftrace_run_update_code(FTRACE_DISABLE_CALLS); command = FTRACE_DISABLE_CALLS;
if (ftrace_graph_active)
command |= FTRACE_STOP_FUNC_RET;
ftrace_run_update_code(command);
}
} }
static cycle_t ftrace_update_time; static cycle_t ftrace_update_time;
...@@ -5338,8 +5356,6 @@ static struct ftrace_ops graph_ops = { ...@@ -5338,8 +5356,6 @@ static struct ftrace_ops graph_ops = {
ASSIGN_OPS_HASH(graph_ops, &global_ops.local_hash) ASSIGN_OPS_HASH(graph_ops, &global_ops.local_hash)
}; };
static int ftrace_graph_active;
int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace) int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
{ {
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment