summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorJunbin Zhang <sprooc69@gmail.com>2026-10-27 13:16:34 +0800
committerTejun Heo <tj@kernel.org>2026-10-27 07:39:56 -1000
commitb07a7450f6ef98c79ebd450a92fc4bb6b8f1e02e (patch)
tree576f07c687774bbf317a4eabb25b9c289be679f0 /include/linux
parent93b1512ca69fbe3da66bbd2bab76e9ed74277679 (diff)
downloadlinux-b07a7450f6ef98c79ebd450a92fc4bb6b8f1e02e.tar.gz
linux-b07a7450f6ef98c79ebd450a92fc4bb6b8f1e02e.zip
sched_ext: Reject rq-lock-taking kfuncs in tracing programs
scx_bpf_cpuperf_set() and scx_bpf_cidperf_set() are members of scx_kfunc_ids_any, which is registered for BPF_PROG_TYPE_TRACING. A tracing program can run in a context which already holds the target rq lock, in which case scx_cpuperf_set() attempts to acquire the same lock again: __schedule() rq_lock(rq); /* rq->__lock is held */ trace_sched_switch(); BPF tracing program scx_bpf_cpuperf_set(smp_processor_id(), 1024); scx_cpuperf_set(); locked_rq = scx_locked_rq(); /* NULL */ rq_lock_irqsave(rq, &rf); /* same lock */ scx_locked_rq() only tracks rq locks held during SCX callback invocations, so it cannot detect an rq lock held by the surrounding kernel code. The deadlock can be reproduced with two BPF programs. The first is a minimal SCX scheduler, which only needs to be registered and enabled: SEC(".struct_ops.link") struct sched_ext_ops minimal_ops = { .name = "minimal", }; The second is a tracing program attached to tp_btf/sched_switch, which fires from __schedule() with rq->__lock already held: SEC("tp_btf/sched_switch") int BPF_PROG(on_sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next, unsigned int prev_state) { scx_bpf_cpuperf_set(bpf_get_smp_processor_id(), 1024); return 0; } With both programs attached, the next context switch on any CPU deadlocks that CPU. Lockdep reports: WARNING: possible recursive locking detected 7.3.0-rc7 #3 Not tainted swapper/3/0 is trying to acquire lock: (&rq->__lock){-.-.}-{2:2}, at: raw_spin_rq_lock_nested+0x1e/0x30 but task is already holding lock: (&rq->__lock){-.-.}-{2:2}, at: __schedule+0xeb/0x1430 Possible unsafe locking scenario: CPU0 ---- lock(&rq->__lock); lock(&rq->__lock); *** DEADLOCK *** Call Trace: <TASK> print_deadlock_bug.cold+0xc0/0xd0 __lock_acquire+0x12fa/0x1b80 lock_acquire+0xd1/0x2f0 _raw_spin_lock_nested+0x32/0x40 raw_spin_rq_lock_nested+0x1e/0x30 scx_cpuperf_set+0x1e9/0x310 scx_bpf_cpuperf_set+0x9b/0x230 bpf_prog_..._on_sched_switch+0x3d/0x41 bpf_trace_run4+0x125/0x450 __traceiter_sched_switch+0x4b/0x70 __schedule+0x94a/0x1430 schedule_idle+0x26/0x50 cpu_startup_entry+0x2d/0x30 start_secondary+0xfc/0x100 </TASK> The CPU remains locked up with interrupts disabled. scx_bpf_cidperf_set() is similarly affected, as it resolves the CID through scx_cid_to_cpu() before reaching the same rq-locking path. There is no reliable way for these kfuncs to determine whether the surrounding tracing context already holds an rq lock. Reject calls to both kfuncs from tracing programs at verification time, while preserving their availability to SCX struct_ops and syscall programs. Fixes: a11d6784d731 ("sched_ext: Fix missing rq lock in scx_bpf_cpuperf_set()") Signed-off-by: Junbin Zhang <sprooc69@gmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'include/linux')
0 files changed, 0 insertions, 0 deletions