Turning on function-graph tracing, or attaching a kprobe_multi return probe, hands every thread a shadow stack. alloc_retstack_tasklist() does that 32 tasks at a time, and since for_each_process_thread() has no cursor, every sweep restarts from init_task and re-walks the tasks already served. Total work is quadratic O(N^2) on thread count.
On a 60-core Sapphire Rapids machine with 400000 idle threads the 0 -> 1 transition takes 227 s, inside a single bpf() syscall for the kprobe_multi case. On Meta fleet this showed up as RCU stalls and softlockup panics. Patch 1 raises the batch to 1024, dividing the sweeps by 32: 227 s -> 7.2 s at 400000 threads. It helps on every preemption model. Patch 2 adds a cond_resched() between sweeps. It is supplementary and separable: a no-op on current x86 and arm64, but on !CONFIG_PREEMPTION builds it takes soft lockups from 3-of-3 runs to 0-of-3. Dropping it leaves patch 1 intact. Neither changes the O(N^2) shape; a cursor-based walk would, but task_struct lifetime makes that considerably more involved. Vineet Gupta (2): tracing: fgraph: Raise FTRACE_RETSTACK_ALLOC_SIZE to 1024 tracing: fgraph: Add a cond_resched() to the shadow stack retry loop include/linux/ftrace.h | 7 ++++++- kernel/trace/fgraph.c | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) -- 2.53.0-Meta
