Skip to content

Commit 2c02f73

Browse files
committed
fgraph: Use CPU hotplug mechanism to initialize idle shadow stacks
The function graph infrastructure allocates a shadow stack for every task when enabled. This includes the idle tasks. The first time the function graph is invoked, the shadow stacks are created and never freed until the task exits. This includes the idle tasks. Only the idle tasks that were for online CPUs had their shadow stacks created when function graph tracing started. If function graph tracing is enabled and a CPU comes online, the idle task representing that CPU will not have its shadow stack created, and all function graph tracing for that idle task will be silently dropped. Instead, use the CPU hotplug mechanism to allocate the idle shadow stacks. This will include idle tasks for CPUs that come online during tracing. This issue can be reproduced by: # cd /sys/kernel/tracing # echo 0 > /sys/devices/system/cpu/cpu1/online # echo 0 > set_ftrace_pid # echo function_graph > current_tracer # echo 1 > options/funcgraph-proc # echo 1 > /sys/devices/system/cpu/cpu1 # grep '<idle>' per_cpu/cpu1/trace | head Before, nothing would show up. After: 1) <idle>-0 | 0.811 us | __enqueue_entity(); 1) <idle>-0 | 5.626 us | } /* enqueue_entity */ 1) <idle>-0 | | dl_server_update_idle_time() { 1) <idle>-0 | | dl_scaled_delta_exec() { 1) <idle>-0 | 0.450 us | arch_scale_cpu_capacity(); 1) <idle>-0 | 1.242 us | } 1) <idle>-0 | 1.908 us | } 1) <idle>-0 | | dl_server_start() { 1) <idle>-0 | | enqueue_dl_entity() { 1) <idle>-0 | | task_contending() { Note, if tracing stops and restarts, the old way would then initialize the onlined CPUs. Cc: stable@vger.kernel.org Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/20241018214300.6df82178@rorschach Fixes: 868baf0 ("ftrace: Fix memory leak with function graph and cpu hotplug") Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent 9852d85 commit 2c02f73

File tree

1 file changed

+21
-7
lines changed

1 file changed

+21
-7
lines changed

kernel/trace/fgraph.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,19 +1160,13 @@ void fgraph_update_pid_func(void)
11601160
static int start_graph_tracing(void)
11611161
{
11621162
unsigned long **ret_stack_list;
1163-
int ret, cpu;
1163+
int ret;
11641164

11651165
ret_stack_list = kmalloc(SHADOW_STACK_SIZE, GFP_KERNEL);
11661166

11671167
if (!ret_stack_list)
11681168
return -ENOMEM;
11691169

1170-
/* The cpu_boot init_task->ret_stack will never be freed */
1171-
for_each_online_cpu(cpu) {
1172-
if (!idle_task(cpu)->ret_stack)
1173-
ftrace_graph_init_idle_task(idle_task(cpu), cpu);
1174-
}
1175-
11761170
do {
11771171
ret = alloc_retstack_tasklist(ret_stack_list);
11781172
} while (ret == -EAGAIN);
@@ -1242,14 +1236,34 @@ static void ftrace_graph_disable_direct(bool disable_branch)
12421236
fgraph_direct_gops = &fgraph_stub;
12431237
}
12441238

1239+
/* The cpu_boot init_task->ret_stack will never be freed */
1240+
static int fgraph_cpu_init(unsigned int cpu)
1241+
{
1242+
if (!idle_task(cpu)->ret_stack)
1243+
ftrace_graph_init_idle_task(idle_task(cpu), cpu);
1244+
return 0;
1245+
}
1246+
12451247
int register_ftrace_graph(struct fgraph_ops *gops)
12461248
{
1249+
static bool fgraph_initialized;
12471250
int command = 0;
12481251
int ret = 0;
12491252
int i = -1;
12501253

12511254
mutex_lock(&ftrace_lock);
12521255

1256+
if (!fgraph_initialized) {
1257+
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "fgraph_idle_init",
1258+
fgraph_cpu_init, NULL);
1259+
if (ret < 0) {
1260+
pr_warn("fgraph: Error to init cpu hotplug support\n");
1261+
return ret;
1262+
}
1263+
fgraph_initialized = true;
1264+
ret = 0;
1265+
}
1266+
12531267
if (!fgraph_array[0]) {
12541268
/* The array must always have real data on it */
12551269
for (i = 0; i < FGRAPH_ARRAY_SIZE; i++)

0 commit comments

Comments
 (0)