Re: [PATCH v3 sched_ext/for-7.4] sched_ext: Keep proxy donors with slice left on the local DSQ
From: Tejun Heo
Date: Fri Oct 09 2026 - 19:02:53 EST
Hello, Andrea.
On Fri, Oct 09, 2026 at 10:08:26PM +0200, Andrea Righi wrote:
> if ((p->scx.slice || unlikely(p == scx_rescuee(rq))) &&
> !scx_bypassing(sch, cpu_of(rq))) {
> - if (p->scx.flags & SCX_TASK_IMMED) {
> + if ((p->scx.flags & SCX_TASK_IMMED) && !proxy_put) {
The bypassing test skips the keep for a proxy_put as well, so under bypass
the put falls through to the ENQ_LAST branch with idle as @next, and the
WARN there fires for an ENQ_BLOCKED scheduler without ENQ_LAST. Every
blocked donor picked during enable or disable and put back by
proxy_resched_idle() takes that path; the base tree's blocked-donor block
never reached it. Reproduced on a 16-CPU VM with pipe mutex contention
while cycling scx_qmap -X, the first disable trips it on three CPUs:
WARNING: kernel/sched/ext/ext.c:3607 at put_prev_task_scx+0x743/0x870, CPU#5: pipe-contend/2042
Sched_ext: qmap (disabling), task: runnable_at=+0ms
Call Trace:
proxy_resched_idle+0x51/0x120
__schedule+0x3f1/0x1900
schedule+0xae/0x100
schedule_preempt_disabled+0x12/0x20
__mutex_lock+0x4d6/0xc20
anon_pipe_write+0xa0/0x650
The same branch is also reachable without bypass if an out-of-band slice
write zeroes a non-running donor's slice before the bookkeeping put.
> + /* SCX_ENQ_IMMED uses SCX_CAP_ENQ_IMMED, the base cap. */
> + if (proxy_put && (p->scx.flags & SCX_TASK_IMMED))
> + enq_flags |= SCX_ENQ_IMMED;
Not from this patch, but noticed while going through it: this insert runs
with rq->next_class still ext, and proxy_resched_idle() then lowers it to
idle with the IMMED donor on the local DSQ. A higher class waking on the
CPU before the re-pick hits the idle class's wakeup_preempt() rather than
wakeup_preempt_scx(), so nothing reenqueues the donor and it sits behind
the new task. The scan dsq_inc_nr() queued only helps if it runs after that
wakeup. proxy_resched_idle() never looked at the local DSQ, but with this
patch that's the regular state after every IMMED donor's bookkeeping put.
The SCX_ENQ_IMMED comment in internal.h describes v2: in v3 an IMMED donor
stays local only for the bookkeeping put and is reenqueued on preemption or
by the deferred scan like any other IMMED task. The dispatch_one() addition
about a blocked IMMED donor making the scan a no-op comes from v2's
exemption in local_task_should_reenq() too, and the dsq_inc_nr() and
local_task_should_reenq() comment changes don't go with anything in v3.
Thanks.
--
tejun