From: sashiko-bot@kernel.org
To: "Tejun Heo" <tj@kernel.org>, sched-ext@lists.linux.dev
Subject: Re: [PATCH 03/14] sched_ext: Fix bypassed tasks looping on a host without SCX_CAP_ENQ
Date: Thu, 22 Oct 2026 22:04:10 +0000 [thread overview]
Message-ID: <sashiko-outbox-182095@kernel.org> (raw)
In-Reply-To: <20261022213836.2259069-4-tj@kernel.org>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] sched_ext: bypassed tasks stranded on IMMED reenqueue in scx_do_enqueue_task()
- [Medium] sched_ext: IMMED bypass task not reenqueued in dispatch_one() on SCX_DSP_PREV
--
commit b04a1e4f05e2438aeebad95d1faba49149a6c6e2
Author: Tejun Heo <tj@kernel.org>
sched_ext: Fix bypassed tasks looping on a host without SCX_CAP_ENQ
This commit updates bypass dispatch queue consumption in scx_dispatch_sched()
to pass SCX_ENQ_IMMED so that a host scheduler holding only SCX_CAP_ENQ_IMMED
can consume bypassed tasks onto the local DSQ without rejecting them for
lacking SCX_CAP_ENQ.
> 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;
> }
[Severity: High]
Can consuming bypassed tasks with SCX_ENQ_IMMED here cause a bypassed task to
be stranded off all dispatch queues in scx_do_enqueue_task() when it gets
reenqueued from the local DSQ?
When scx_consume_dispatch_q() moves a bypassed task to rq->scx.local_dsq with
SCX_ENQ_IMMED, dsq_inc_nr() marks the task with SCX_TASK_IMMED. If the task
cannot run right away (for example, due to a higher-priority wakeup, a head
insert, or a parent scheduler queueing its own task ahead of
scx_bpf_sub_dispatch()), reenq_local() dequeues it from rq->scx.local_dsq and
calls scx_do_enqueue_task() with SCX_ENQ_REENQ.
In kernel/sched/ext/ext.c:scx_do_enqueue_task(), sch is resolved as
scx_task_sched(p) (the bypassing scheduler that owns p, rather than the host
scheduler), and the reenqueue limit is checked before scx_bypassing():
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;
}
This appears to break in two ways:
1. If a task previously triggered SCX_EXIT_ERROR_REENQ, it enters bypass with
p->scx.reenq_cnt already at SCX_REENQ_MAX_REPEAT + 1 because cycling the
task in scx_bypass() via dequeue_task_scx() -> ops_dequeue() ->
clr_task_runnable(p, false) does not reset p->scx.reenq_cnt. On the very
first local DSQ reenqueue during bypass, p->scx.reenq_cnt increments again
and hits the goto out path before reaching goto bypass. Because
scx_bypass(sch, true) in scx_sub_disable() or scx_root_disable() has
already run, nothing picks the task back up, leaving it stranded off all
DSQs while teardown waits for forward progress.
2. Even if p->scx.reenq_cnt starts at 0, if a bypassed task is reenqueued
more than SCX_REENQ_MAX_REPEAT times (for example, because a parent
scheduler repeatedly queues a task before calling scx_bpf_sub_dispatch()),
__scx_exit() is called on the already-bypassing descendant sch instead of
the host scheduler, and goto out strands the task off all DSQs.
Should scx_do_enqueue_task() check scx_bypassing(sch, cpu_of(rq)) before the
SCX_ENQ_REENQ handling, or attribute and handle reenqueues of bypassed tasks
against the host scheduler without stranding the task?
> @@ -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)) {
> __scx_add_event(sch, SCX_EV_SUB_BYPASS_DISPATCH, 1);
> return SCX_DSP_LOCAL;
> }
[Severity: Medium]
What happens if a child host holding only SCX_CAP_ENQ_IMMED consumes a single
bypassed task with SCX_ENQ_IMMED during scx_bpf_sub_dispatch() while
rq->scx.local_dsq is empty, and the parent scheduler's ops.dispatch() also
extends prev->scx.slice?
During dispatch_one(), SCX_RQ_IN_DISPATCH is set on rq->scx.flags, so
rq_is_open() returns true:
if (rq->scx.flags & SCX_RQ_IN_DISPATCH)
return true;
When the child consumes one bypassed task onto the empty rq->scx.local_dsq,
dsq_inc_nr() sees dsq->nr == 1 and rq_is_open() == true, so it increments
rq->scx.nr_immed to 1 without calling scx_schedule_reenq_local().
When control returns to the parent's scx_dispatch_sched(), extending
prev->scx.slice causes it to return SCX_DSP_PREV before checking
rq->scx.local_dsq.nr:
if ((prev->scx.flags & SCX_TASK_QUEUED) && prev->scx.slice)
return SCX_DSP_PREV;
if (rq->scx.local_dsq.nr)
return SCX_DSP_LOCAL;
Then at has_tasks in kernel/sched/ext/ext.c:dispatch_one(), the post-dispatch
check only schedules a local reenqueue when rq->scx.local_dsq.nr > 1:
has_tasks:
if (unlikely(rq->scx.local_dsq.nr > 1 && rq->scx.nr_immed))
scx_schedule_reenq_local(rq, 0);
Because prev is rq->donor and not on rq->scx.local_dsq, rq->scx.local_dsq.nr
is 1, so scx_schedule_reenq_local() is not called. Doesn't this leave the
single bypassed task waiting on rq->scx.local_dsq behind prev without holding
SCX_CAP_ENQ and without being reenqueued back to the bypass DSQ?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261022213836.2259069-1-tj@kernel.org?part=3
next prev parent reply other threads:[~2026-10-22 22:04 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 21:38 [PATCHSET sched_ext/for-7.4] sched_ext: Fix bypassed sub-scheduler tasks stalling when the host lacks caps Tejun Heo
2026-10-22 21:38 ` [PATCH 01/14] sched_ext: Pack scx_ent_flags' multi-bit fields at the top Tejun Heo
2026-10-22 21:38 ` [PATCH 02/14] sched_ext: Clear holding_cpu where the claim is confirmed Tejun Heo
2026-10-22 21:38 ` [PATCH 03/14] sched_ext: Fix bypassed tasks looping on a host without SCX_CAP_ENQ Tejun Heo
2026-10-22 22:04 ` sashiko-bot [this message]
2026-10-23 0:03 ` Tejun Heo
2026-10-22 21:38 ` [PATCH 04/14] sched_ext: Move refill_task_slice_dfl() below apply_slice_vtime() Tejun Heo
2026-10-22 21:38 ` [PATCH 05/14] sched_ext: Restore a rescuee's slice after ops.stopping() Tejun Heo
2026-10-22 21:38 ` [PATCH 06/14] sched_ext: Fix a cpu idling with a runnable rescuee Tejun Heo
2026-10-22 21:56 ` sashiko-bot
2026-10-23 0:03 ` Tejun Heo
2026-10-22 21:38 ` [PATCH 07/14] sched_ext: Mark the rescue grant as a kernel slice with SCX_TASK_KERNEL_SLICE Tejun Heo
2026-10-22 21:38 ` [PATCH 08/14] sched_ext: Refuse extending a kernel slice Tejun Heo
2026-10-22 21:38 ` [PATCH 09/14] sched_ext: Fix a cpu idling after put_prev_task_scx() queues its task on the local DSQ Tejun Heo
2026-10-22 21:38 ` [PATCH 10/14] sched_ext: Fix bypassed tasks stalling when the host lacks SCX_CAP_BASE Tejun Heo
2026-10-22 22:09 ` sashiko-bot
2026-10-23 0:03 ` Tejun Heo
2026-10-22 21:38 ` [PATCH 11/14] sched_ext: Fix the bypass load balancer stranding tasks on cpus where " Tejun Heo
2026-10-22 21:38 ` [PATCH 12/14] sched_ext: Factor out bypass_lb_move() and insert with the donor DSQ unlocked Tejun Heo
2026-10-22 21:38 ` [PATCH 13/14] sched_ext: Let the bypass load balancer move tasks homed on other cpus Tejun Heo
2026-10-22 21:38 ` [PATCH 14/14] sched_ext: Fix queued bypassed tasks stalling when the host loses SCX_CAP_BASE Tejun Heo
2026-10-22 22:04 ` sashiko-bot
2026-10-23 0:03 ` 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=sashiko-outbox-182095@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox