From: sashiko-bot@kernel.org
To: "Tejun Heo" <tj@kernel.org>, sched-ext@lists.linux.dev
Subject: Re: [PATCH 03/15] sched_ext: Fix bypassed tasks looping on a host without SCX_CAP_ENQ
Date: Fri, 23 Oct 2026 05:48:12 +0000 [thread overview]
Message-ID: <sashiko-outbox-182332@kernel.org> (raw)
In-Reply-To: <20261023052452.2889061-4-tj@kernel.org>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] sched_ext: bypassed task stranding in scx_do_enqueue_task() on SCX_ENQ_IMMED
--
commit 51c04312bb9dbd18b0f365aada46b01365632700
Author: Tejun Heo <tj@kernel.org>
sched_ext: Fix bypassed tasks looping on a host without SCX_CAP_ENQ
This commit updates scx_dispatch_sched() to pass SCX_ENQ_IMMED when consuming
tasks from a scheduler's bypass DSQ so that a host scheduler holding only
SCX_CAP_ENQ_IMMED can dispatch bypassed tasks onto the local DSQ.
> diff --git a/kernel/sched/ext/inlines.h b/kernel/sched/ext/inlines.h
> index 8de9d8fb4b25f..1e39d12678c4d 100644
> --- a/kernel/sched/ext/inlines.h
> +++ b/kernel/sched/ext/inlines.h
> @@ -39,10 +39,18 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
> if (scx_consume_global_dsq(sch, rq))
> return SCX_DSP_LOCAL;
>
> + /*
> + * A bypass consume puts a task on the cpu being dispatched for, to run
> + * right away. That is an IMMED insert and needs only
> + * %SCX_CAP_ENQ_IMMED, the base cap. Dispatch descends into @sch only on
> + * a cpu where @sch holds the base cap, so @sch holds it here. The same
> + * holds for the consume at the end of this function.
> + */
> if (scx_bypass_dsp_enabled(sch)) {
> /* if @sch is bypassing, only the bypass DSQs are active */
> if (scx_bypassing(sch, cpu)) {
> - if (scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu), 0))
> + if (scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu),
> + SCX_ENQ_IMMED))
> return SCX_DSP_LOCAL;
> return SCX_DSP_NONE;
> }
> @@ -63,7 +71,8 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
> struct scx_sched_pcpu *pcpu = per_cpu_ptr(sch->pcpu, cpu);
>
> if (!(pcpu->bypass_host_seq++ % SCX_BYPASS_HOST_NTH) &&
> - scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu), 0)) {
> + scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu),
> + SCX_ENQ_IMMED)) {
> __scx_add_event(sch, SCX_EV_SUB_BYPASS_DISPATCH, 1);
> return SCX_DSP_LOCAL;
> }
> @@ -135,7 +144,7 @@ scx_dispatch_sched(struct scx_sched *sch, struct rq *rq,
> * scheduler's ops.dispatch() doesn't yield any tasks.
> */
> if (scx_bypass_dsp_enabled(sch) &&
> - scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu), 0)) {
> + scx_consume_dispatch_q(sch, rq, scx_bypass_dsq(sch, cpu), SCX_ENQ_IMMED)) {
[Severity: High]
Can passing SCX_ENQ_IMMED to scx_consume_dispatch_q() here cause bypassed
tasks to be permanently stranded off all dispatch queues in
scx_do_enqueue_task() when reenq_local() reenqueues them?
When a bypassed task p is consumed into rq->scx.local_dsq with
SCX_ENQ_IMMED, dsq_inc_nr() marks it with SCX_TASK_IMMED. If p cannot run
right away (for example, because a higher-priority scheduling class preempts
rq, a head insert occurs ahead of p, or a parent scheduler queues its own
task onto rq->scx.local_dsq before calling scx_bpf_sub_dispatch()),
reenq_local() dequeues p from rq->scx.local_dsq and calls
scx_do_enqueue_task(rq, p, SCX_ENQ_REENQ, -1).
In kernel/sched/ext/ext.c:scx_do_enqueue_task(), sch is set to
scx_task_sched(p) (the bypassing scheduler that owns p, rather than the host
scheduler scx_exec_sched(sch, cpu_of(rq))), and the reenqueue repeat check
runs before the scx_bypassing(sch, cpu_of(rq)) check:
kernel/sched/ext/ext.c:scx_do_enqueue_task() {
...
if (enq_flags & SCX_ENQ_REENQ) {
if (++p->scx.reenq_cnt > 1)
__scx_add_event(sch, SCX_EV_REENQ_REPEAT, 1);
if (unlikely(p->scx.reenq_cnt > SCX_REENQ_MAX_REPEAT)) {
__scx_exit(sch, SCX_EXIT_ERROR_REENQ, 0, cpu_of(rq),
"%s[%d] reenqueued %u times without running",
p->comm, p->pid, p->scx.reenq_cnt);
goto out;
}
}
if (!scx_rq_online(rq))
goto local;
if (scx_bypassing(sch, cpu_of(rq))) {
__scx_add_event(sch, SCX_EV_BYPASS_DISPATCH, 1);
goto bypass;
}
...
}
Jumping to "goto out" leaves action at its initial value and returns without
inserting p into any DSQ, leaving p->scx.dsq == NULL while p remains queued
in sched_ext.
This appears to break in two scenarios:
1. If p originally triggered SCX_EXIT_ERROR_REENQ, its p->scx.reenq_cnt is
257 (SCX_REENQ_MAX_REPEAT + 1). When scx_sub_disable() or scx_root_disable()
calls scx_bypass(sch, true), it cycles runnable tasks with
DEQUEUE_SAVE | DEQUEUE_MOVE, which calls:
dequeue_task_scx() -> ops_dequeue() -> clr_task_runnable(p, false)
kernel/sched/ext/ext.c:clr_task_runnable() {
list_del_init(&p->scx.runnable_node);
WRITE_ONCE(p->scx.runnable_cpu, -1);
if (reset_runnable_at) {
p->scx.flags |= SCX_TASK_RESET_RUNNABLE_AT;
p->scx.reenq_cnt = 0;
}
}
Because reset_runnable_at is false, p->scx.reenq_cnt is not reset and stays
at 257 on bypass_dsq. On the very first occasion p is consumed with
SCX_ENQ_IMMED and reenqueued by reenq_local(), ++p->scx.reenq_cnt becomes
258, __scx_exit() is a no-op because sch already exited, and "goto out"
strands p off all DSQs after scx_bypass(sch, true) has already finished.
2. Even when p->scx.reenq_cnt starts at 0, if p is reenqueued 257 times while
bypassing (for example, when a parent scheduler repeatedly queues its own
task before calling scx_bpf_sub_dispatch()), scx_do_enqueue_task() calls
__scx_exit() on the bypassing task owner sch instead of the active host or
parent scheduler, and jumps to "goto out". Because sch is already in bypass
mode (bypass_depth >= 1), any subsequent scx_bypass(sch, true) sees
!changed in kernel/sched/ext/ext.c:scx_bypass():
if (!changed)
goto unlock;
and returns without cycling rq->scx.runnable_list, leaving p stranded off all
DSQs and potentially deadlocking kernel/sched/ext/sub.c:scx_sub_disable() or
scx_root_disable() if p holds a lock or resource waited on during teardown.
Should scx_do_enqueue_task() check scx_bypassing(sch, cpu_of(rq)) before the
SCX_ENQ_REENQ handling (or still route the task to "goto bypass" when the
reenqueue limit is hit), and should scx_bypass() also reset p->scx.reenq_cnt?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261023052452.2889061-1-tj@kernel.org?part=3
next prev parent reply other threads:[~2026-10-23 5:48 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 5:24 [PATCHSET v2 sched_ext/for-7.4] sched_ext: Fix bypassed sub-scheduler tasks stalling when the host lacks caps Tejun Heo
2026-10-23 5:24 ` [PATCH 01/15] sched_ext: Pack scx_ent_flags' multi-bit fields at the top Tejun Heo
2026-10-23 5:24 ` [PATCH 02/15] sched_ext: Clear holding_cpu where the claim is confirmed Tejun Heo
2026-10-23 5:24 ` [PATCH 03/15] sched_ext: Fix bypassed tasks looping on a host without SCX_CAP_ENQ Tejun Heo
2026-10-23 5:48 ` sashiko-bot [this message]
2026-10-23 5:24 ` [PATCH 04/15] sched_ext: Move refill_task_slice_dfl() below apply_slice_vtime() Tejun Heo
2026-10-23 5:24 ` [PATCH 05/15] sched_ext: Restore a rescuee's slice after ops.stopping() Tejun Heo
2026-10-23 5:54 ` sashiko-bot
2026-10-23 5:24 ` [PATCH 06/15] sched_ext: Fix a cpu idling with a runnable rescuee Tejun Heo
2026-10-23 5:24 ` [PATCH 07/15] sched_ext: Mark the rescue grant as a kernel slice with SCX_TASK_KERNEL_SLICE Tejun Heo
2026-10-23 5:24 ` [PATCH 08/15] sched_ext: Refuse extending a kernel slice Tejun Heo
2026-10-23 5:24 ` [PATCH 09/15] sched_ext: Fix a cpu idling after put_prev_task_scx() queues its task on the local DSQ Tejun Heo
2026-10-23 5:24 ` [PATCH 10/15] sched_ext: Expire prev's slice in dispatch_one() under bypass Tejun Heo
2026-10-23 5:24 ` [PATCH 11/15] sched_ext: Fix bypassed tasks stalling when the host lacks SCX_CAP_BASE Tejun Heo
2026-10-23 5:24 ` [PATCH 12/15] sched_ext: Fix the bypass load balancer stranding tasks on cpus where " Tejun Heo
2026-10-23 5:24 ` [PATCH 13/15] sched_ext: Factor out bypass_lb_move() and insert with the donor DSQ unlocked Tejun Heo
2026-10-23 5:24 ` [PATCH 14/15] sched_ext: Let the bypass load balancer move tasks homed on other cpus Tejun Heo
2026-10-23 5:24 ` [PATCH 15/15] sched_ext: Fix queued bypassed tasks stalling when the host loses SCX_CAP_BASE Tejun Heo
2026-10-23 5:47 ` sashiko-bot
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=sashiko-outbox-182332@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=sched-ext@lists.linux.dev \
--cc=tj@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.