function_graph: Use static_call and branch to optimize entry function

In most cases function graph is used by a single user. Instead of calling
a loop to call function graph callbacks in this case, call the function
entry callback directly.

Add a static_key that will be used to set the function graph logic to
either do the loop (when more than one callback is registered) or to call
the callback directly if there is only one registered callback.

Link: https://lore.kernel.org/linux-trace-kernel/20240603190824.766858241@goodmis.org

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Florent Revest <revest@chromium.org>
Cc: Martin KaFai Lau <martin.lau@linux.dev>
Cc: bpf <bpf@vger.kernel.org>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Guo Ren <guoren@kernel.org>
Reviewed-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent a5b6d4da
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/jump_label.h> #include <linux/jump_label.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/static_call.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <trace/events/sched.h> #include <trace/events/sched.h>
...@@ -511,6 +512,10 @@ static struct fgraph_ops fgraph_stub = { ...@@ -511,6 +512,10 @@ static struct fgraph_ops fgraph_stub = {
.retfunc = ftrace_graph_ret_stub, .retfunc = ftrace_graph_ret_stub,
}; };
static struct fgraph_ops *fgraph_direct_gops = &fgraph_stub;
DEFINE_STATIC_CALL(fgraph_func, ftrace_graph_entry_stub);
DEFINE_STATIC_KEY_TRUE(fgraph_do_direct);
/** /**
* ftrace_graph_stop - set to permanently disable function graph tracing * ftrace_graph_stop - set to permanently disable function graph tracing
* *
...@@ -636,21 +641,34 @@ int function_graph_enter(unsigned long ret, unsigned long func, ...@@ -636,21 +641,34 @@ int function_graph_enter(unsigned long ret, unsigned long func,
if (offset < 0) if (offset < 0)
goto out; goto out;
for_each_set_bit(i, &fgraph_array_bitmask, #ifdef CONFIG_HAVE_STATIC_CALL
sizeof(fgraph_array_bitmask) * BITS_PER_BYTE) { if (static_branch_likely(&fgraph_do_direct)) {
struct fgraph_ops *gops = fgraph_array[i]; int save_curr_ret_stack = current->curr_ret_stack;
int save_curr_ret_stack;
if (gops == &fgraph_stub)
continue;
save_curr_ret_stack = current->curr_ret_stack; if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
if (ftrace_ops_test(&gops->ops, func, NULL) && bitmap |= BIT(fgraph_direct_gops->idx);
gops->entryfunc(&trace, gops))
bitmap |= BIT(i);
else else
/* Clear out any saved storage */ /* Clear out any saved storage */
current->curr_ret_stack = save_curr_ret_stack; current->curr_ret_stack = save_curr_ret_stack;
} else
#endif
{
for_each_set_bit(i, &fgraph_array_bitmask,
sizeof(fgraph_array_bitmask) * BITS_PER_BYTE) {
struct fgraph_ops *gops = fgraph_array[i];
int save_curr_ret_stack;
if (gops == &fgraph_stub)
continue;
save_curr_ret_stack = current->curr_ret_stack;
if (ftrace_ops_test(&gops->ops, func, NULL) &&
gops->entryfunc(&trace, gops))
bitmap |= BIT(i);
else
/* Clear out any saved storage */
current->curr_ret_stack = save_curr_ret_stack;
}
} }
if (!bitmap) if (!bitmap)
...@@ -1155,6 +1173,8 @@ void fgraph_update_pid_func(void) ...@@ -1155,6 +1173,8 @@ void fgraph_update_pid_func(void)
gops = container_of(op, struct fgraph_ops, ops); gops = container_of(op, struct fgraph_ops, ops);
gops->entryfunc = ftrace_pids_enabled(op) ? gops->entryfunc = ftrace_pids_enabled(op) ?
fgraph_pid_func : gops->saved_func; fgraph_pid_func : gops->saved_func;
if (ftrace_graph_active == 1)
static_call_update(fgraph_func, gops->entryfunc);
} }
} }
} }
...@@ -1209,6 +1229,32 @@ static void init_task_vars(int idx) ...@@ -1209,6 +1229,32 @@ static void init_task_vars(int idx)
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
static void ftrace_graph_enable_direct(bool enable_branch)
{
trace_func_graph_ent_t func = NULL;
int i;
for_each_set_bit(i, &fgraph_array_bitmask,
sizeof(fgraph_array_bitmask) * BITS_PER_BYTE) {
func = fgraph_array[i]->entryfunc;
fgraph_direct_gops = fgraph_array[i];
}
if (WARN_ON_ONCE(!func))
return;
static_call_update(fgraph_func, func);
if (enable_branch)
static_branch_disable(&fgraph_do_direct);
}
static void ftrace_graph_disable_direct(bool disable_branch)
{
if (disable_branch)
static_branch_disable(&fgraph_do_direct);
static_call_update(fgraph_func, ftrace_graph_entry_stub);
fgraph_direct_gops = &fgraph_stub;
}
int register_ftrace_graph(struct fgraph_ops *gops) int register_ftrace_graph(struct fgraph_ops *gops)
{ {
int command = 0; int command = 0;
...@@ -1235,7 +1281,11 @@ int register_ftrace_graph(struct fgraph_ops *gops) ...@@ -1235,7 +1281,11 @@ int register_ftrace_graph(struct fgraph_ops *gops)
ftrace_graph_active++; ftrace_graph_active++;
if (ftrace_graph_active == 2)
ftrace_graph_disable_direct(true);
if (ftrace_graph_active == 1) { if (ftrace_graph_active == 1) {
ftrace_graph_enable_direct(false);
register_pm_notifier(&ftrace_suspend_notifier); register_pm_notifier(&ftrace_suspend_notifier);
ret = start_graph_tracing(); ret = start_graph_tracing();
if (ret) if (ret)
...@@ -1292,6 +1342,11 @@ void unregister_ftrace_graph(struct fgraph_ops *gops) ...@@ -1292,6 +1342,11 @@ void unregister_ftrace_graph(struct fgraph_ops *gops)
ftrace_shutdown_subops(&graph_ops, &gops->ops, command); ftrace_shutdown_subops(&graph_ops, &gops->ops, command);
if (ftrace_graph_active == 1)
ftrace_graph_enable_direct(true);
else if (!ftrace_graph_active)
ftrace_graph_disable_direct(false);
if (!ftrace_graph_active) { if (!ftrace_graph_active) {
ftrace_graph_return = ftrace_stub_graph; ftrace_graph_return = ftrace_stub_graph;
ftrace_graph_entry = ftrace_graph_entry_stub; ftrace_graph_entry = ftrace_graph_entry_stub;
......
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