* [PATCH v2 sched_ext/for-7.3-fixes] sched_ext: Reenqueue IMMED tasks when their CPU picks idle
@ 2026-10-17 22:19 Andrea Righi
2026-10-19 23:16 ` Tejun Heo
0 siblings, 1 reply; 2+ messages in thread
From: Andrea Righi @ 2026-10-17 22:19 UTC (permalink / raw)
To: Tejun Heo, David Vernet, Changwoo Min
Cc: Emil Tsalapatis, sched-ext, linux-kernel
Core scheduling can force a CPU idle while tasks are runnable on it:
when the CPU's highest priority task doesn't match the core cookie, or
when an IMMED task's own cookie doesn't match it, e.g. a tagged IMMED
task while the SMT sibling runs an untagged RT task. In both cases
rq->next_class is idle, so rq_is_open() reports the CPU as open and
IMMED tasks inserted into its local DSQ are parked there, instead of
going back to the BPF scheduler.
This was reproduced on an SMT pair, with the ext server runtime set to 0
on both siblings and an untagged SCHED_FIFO task spinning on one of
them, forcing the other idle in two ways: with a tagged SCHED_FIFO task
runnable on it, and with no higher class task, but a tagged IMMED task.
In both cases, the IMMED task inserted into the local DSQ of the
forced-idle sibling was never reenqueued and stayed parked for the
whole test time window.
Instead of predicting the core scheduling pick, react to it: a CPU
picking idle with IMMED tasks on its local DSQ means that they didn't
run immediately, whatever the reason. __scx_update_idle() is called on
every idle pick, both on a task to idle switch and on an idle to idle
re-pick, so schedule a local DSQ reenqueue from there, with a new
internal SCX_REENQ_IDLE_PICK flag that tells reenq_local() to skip
rq_is_open(), which would report the idle rq as open and keep the IMMED
task at the head. With this change the IMMED task is reenqueued right
away and the BPF scheduler runs it on another CPU.
Fixes: 98d709cba319 ("sched_ext: Implement SCX_ENQ_IMMED")
Cc: stable@vger.kernel.org # v7.1+
Suggested-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Andrea Righi <arighi@nvidia.com>
---
Changes in v2:
- React to the idle pick instead of predicting it from rq_is_open(),
which also covers an IMMED task whose own cookie forces the CPU idle
(Tejun Heo)
- Link to v1: https://lore.kernel.org/r/20261017124749.3608468-1-arighi@nvidia.com
kernel/sched/ext/ext.c | 2 +-
kernel/sched/ext/idle.c | 10 ++++++++++
kernel/sched/ext/internal.h | 6 ++++++
3 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 32b248a6d1777..7fc306ff54ac1 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -4591,7 +4591,7 @@ static u32 reenq_local(struct scx_sched *sch, struct rq *rq, u64 reenq_flags)
if (WARN_ON_ONCE(reenq_flags & __SCX_REENQ_TSR_MASK))
reenq_flags &= ~__SCX_REENQ_TSR_MASK;
- if (rq_is_open(rq, 0))
+ if (!(reenq_flags & SCX_REENQ_IDLE_PICK) && rq_is_open(rq, 0))
reenq_flags |= SCX_REENQ_TSR_RQ_OPEN;
/*
diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c
index 28ed9e3270854..3d8d75ffbf9e1 100644
--- a/kernel/sched/ext/idle.c
+++ b/kernel/sched/ext/idle.c
@@ -804,6 +804,16 @@ void __scx_update_idle(struct rq *rq, bool idle, bool do_notify)
lockdep_assert_rq_held(rq);
+ /*
+ * Picking idle with IMMED tasks on the local DSQ means that they didn't
+ * run immediately, e.g., core scheduling forced the CPU idle. Outside
+ * of that, pick_task_scx() would have taken the local DSQ head. As
+ * next_class is idle, the rq looks open to rq_is_open(), tell
+ * reenq_local() to reenqueue the IMMED tasks anyway.
+ */
+ if (idle && unlikely(rq->scx.nr_immed))
+ scx_schedule_reenq_local(rq, SCX_REENQ_IDLE_PICK);
+
/*
* pick_task_idle() calls here only on an idle-to-idle re-pick and the
* transitions call with @do_notify, so every reaching call updates the
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 03f03204d1582..df8b013fcd09c 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -1819,6 +1819,12 @@ enum scx_reenq_flags {
/* internal: kernel-issued on cap revoke, not accepted from BPF */
SCX_REENQ_CAP_REVOKE = 1LLU << 1,
+ /*
+ * internal: kernel-issued when the rq picks idle with IMMED tasks on its
+ * local DSQ, the rq isn't open for them regardless of rq_is_open().
+ */
+ SCX_REENQ_IDLE_PICK = 1LLU << 2,
+
__SCX_REENQ_FILTER_MASK = 0xffffLLU,
__SCX_REENQ_USER_MASK = SCX_REENQ_ANY,
--
2.56.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v2 sched_ext/for-7.3-fixes] sched_ext: Reenqueue IMMED tasks when their CPU picks idle
2026-10-17 22:19 [PATCH v2 sched_ext/for-7.3-fixes] sched_ext: Reenqueue IMMED tasks when their CPU picks idle Andrea Righi
@ 2026-10-19 23:16 ` Tejun Heo
0 siblings, 0 replies; 2+ messages in thread
From: Tejun Heo @ 2026-10-19 23:16 UTC (permalink / raw)
To: Andrea Righi
Cc: David Vernet, Changwoo Min, Emil Tsalapatis, sched-ext, linux-kernel
Hello, Andrea.
The following is a Claude-generated review.
On Sun, Oct 18, 2026 at 12:19:24AM +0200, Andrea Righi wrote:
> + /*
> + * Picking idle with IMMED tasks on the local DSQ means that they didn't
> + * run immediately, e.g., core scheduling forced the CPU idle. Outside
> + * of that, pick_task_scx() would have taken the local DSQ head. As
> + * next_class is idle, the rq looks open to rq_is_open(), tell
> + * reenq_local() to reenqueue the IMMED tasks anyway.
> + */
> + if (idle && unlikely(rq->scx.nr_immed))
> + scx_schedule_reenq_local(rq, SCX_REENQ_IDLE_PICK);
The forced-idle CPU is still advertised as idle: update_builtin_idle()
marks it idle right below, and the bounced task's task_cpu() is still
that CPU. A scheduler that trusts the idle state can put the task
straight back, e.g. scx_cosmos for a per-CPU task, which checks
test_cpu_idle(prev_cpu) and inserts into SCX_DSQ_LOCAL_ON | cpu with
SCX_ENQ_IMMED:
1. The IMMED insert calls wakeup_preempt() and reschedules the CPU.
2. The core-wide selection forces it idle again, and pick_task_idle()
queues another IDLE_PICK reenq.
3. reenq_local() bounces the task again. p->scx.reenq_cnt is reset only
when the task runs, so after SCX_REENQ_MAX_REPEAT rounds the
scheduler exits with SCX_EXIT_ERROR_REENQ.
Before this patch, the task waited until the cookie allowed it to run.
With SCX_OPS_ALWAYS_ENQ_IMMED, scx_bpf_dsq_move_to_local() also inserts
with IMMED, so a dispatch can close the same loop.
"Outside of that, pick_task_scx() would have taken the local DSQ head"
doesn't hold for SCX_ENQ_LAST, with or without core scheduling, and the
same assumption was in the v1 suggestion. put_prev_task_scx() runs
ops.enqueue(SCX_ENQ_LAST) before set_next_task_idle(). If that inserts
the task into the local DSQ with IMMED, nr_immed is 1 here and the task
is bounced, although set_tsk_need_resched(next) was about to run it.
ENQ_LAST schedulers using IMMED would take an extra reenq every time a
lone task's slice expires. Would triggering from pick_task_idle(),
regardless of rq->curr, work? It runs before put_prev_task_scx(), and
the core-wide selection calls it for every forced-idle sibling.
Also, next_class isn't idle yet when this runs. On the re-pick after an
insert, it's still ext. It's idle by the time the deferred reenq_local()
runs.
> - if (rq_is_open(rq, 0))
> + if (!(reenq_flags & SCX_REENQ_IDLE_PICK) && rq_is_open(rq, 0))
> reenq_flags |= SCX_REENQ_TSR_RQ_OPEN;
Maybe update the %SCX_REENQ_TSR_RQ_OPEN description above
local_task_should_reenq() to mention SCX_REENQ_IDLE_PICK?
Separately, a case that neither this patch nor the existing triggers
cover: when the IMMED task at the head of the local DSQ doesn't match
the core cookie but sched_core_find() finds a matching SCX task outside
the local DSQ, the CPU runs that task instead of going idle, and the
IMMED task stays parked.
None of the above has been reproduced. It all comes from reading the
code.
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-19 23:16 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-17 22:19 [PATCH v2 sched_ext/for-7.3-fixes] sched_ext: Reenqueue IMMED tasks when their CPU picks idle Andrea Righi
2026-10-19 23:16 ` Tejun Heo
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®