From: Andrea Righi <arighi@nvidia.com>
To: Tejun Heo <tj@kernel.org>, David Vernet <void@manifault.com>,
Changwoo Min <changwoo@igalia.com>
Cc: Cheng-Yang Chou <yphbchou0911@gmail.com>,
sched-ext@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: [PATCH sched_ext/for-7.3-fixes] sched_ext: Unlink pending local reenqueues before freeing scheduler
Date: Wed, 16 Sep 2026 16:58:07 +0200 [thread overview]
Message-ID: <20260916145807.3250167-1-arighi@nvidia.com> (raw)
A deferred local DSQ reenqueue embeds its list node in struct
scx_sched_pcpu and links it into rq->scx.deferred_reenq_locals.
A scheduler teardown enters bypass before the RCU grace period, which
prevents new requests. However, this does not guarantee that an
already-linked request has been consumed. An RCU grace period waits for
active readers and it does not flush a pending rq deferred request.
scx_sched_free_rcu_work() assumes that each node has been removed. It only
warns when one remains linked and then frees sch->pcpu. The rq list
therefore retains a pointer into freed per-CPU storage. A later
run_deferred() can walk the stale node, derive sch_pcpu and sch from it,
and dereference freed memory.
This was reproduced on a 352-CPU arm64 machine by repeatedly attaching
an SCX scheduler, running hackbench, and detaching it:
for i in $(seq 1 100); do
sudo timeout --signal=INT 15s \
scx_cidland --stats 1 &
sleep 1
hackbench -l 2000 -g 100
wait
done
The scheduler was enabled and disabled twice in close succession. On the
second detach, scx_sched_free_rcu_work() reported the pending node:
WARNING: kernel/sched/ext/ext.c:5351 at
scx_sched_free_rcu_work+0x460/0x5a0
Workqueue: events_unbound scx_sched_free_rcu_work
After scx_cidland was attached again, a hackbench worker hit the stale
entry two seconds later:
Unable to handle kernel paging request at virtual address
000000000010b8bf
CPU: 89 PID: 590569 Comm: hackbench
pc : run_deferred+0x148/0x5a8
lr : run_deferred+0x194/0x5a8
x1 : 000000000010b8bf
Call trace:
run_deferred+0x148/0x5a8
task_woken_scx+0x1c/0x40
wake_up_new_task+0x1d4/0x448
kernel_clone+0x1b8/0x5e8
The oops left interrupts disabled and was followed by persistent RCU
stalls, making the system unusable.
Cancel any pending local reenqueue before freeing sch->pcpu. Take the rq
lock first to wait for any in-flight run_deferred() and prevent another
one from starting, then unlink the request under deferred_reenq_lock
(the request is obsolete once its scheduler is being torn down).
Fixes: 0d8c551dd5de ("sched_ext: Make scx_bpf_reenqueue_local() sub-sched aware")
Cc: stable@vger.kernel.org # v7.1+
Signed-off-by: Andrea Righi <arighi@nvidia.com>
---
kernel/sched/ext/ext.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 70b711c4de6e1..94ee33ec88de6 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -5380,13 +5380,20 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
for_each_possible_cpu(cpu) {
struct scx_sched_pcpu *pcpu = per_cpu_ptr(sch->pcpu, cpu);
+ struct rq *rq = cpu_rq(cpu);
/*
- * $sch would have entered bypass mode before the RCU grace
- * period. As that blocks new deferrals, all
- * deferred_reenq_local_node's must be off-list by now.
+ * Bypass blocks new deferrals, but a request queued before bypass
+ * may still be pending. As run_deferred() runs under the rq lock,
+ * take it to wait for any in-flight processing before unlinking the
+ * now-obsolete request.
*/
- WARN_ON_ONCE(!list_empty(&pcpu->deferred_reenq_local.node));
+ scoped_guard (rq_lock_irqsave, rq) {
+ guard(raw_spinlock)(&rq->scx.deferred_reenq_lock);
+
+ if (!list_empty(&pcpu->deferred_reenq_local.node))
+ list_del_init(&pcpu->deferred_reenq_local.node);
+ }
/* remove the queued ecaps sync so the pcpu can be freed */
scx_discard_ecaps_to_sync(cpu, pcpu);
@@ -5395,7 +5402,7 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
* Bypass blocks new kicks. Flush the kick irq_work so this
* pcpu's to_kick_node is off the list before it is freed.
*/
- irq_work_sync(&cpu_rq(cpu)->scx.kick_cpus_irq_work);
+ irq_work_sync(&rq->scx.kick_cpus_irq_work);
WARN_ON_ONCE(!list_empty(&pcpu->to_kick_node));
free_cpumask_var(pcpu->cpus_to_kick);
free_cpumask_var(pcpu->cpus_to_kick_if_idle);
--
2.55.0
next reply other threads:[~2026-09-16 14:58 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-16 14:58 Andrea Righi [this message]
2026-09-16 16:33 ` Cheng-Yang Chou
2026-09-16 22:00 ` Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260916145807.3250167-1-arighi@nvidia.com \
--to=arighi@nvidia.com \
--cc=changwoo@igalia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=sched-ext@lists.linux.dev \
--cc=tj@kernel.org \
--cc=void@manifault.com \
--cc=yphbchou0911@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®