Sched_ext development
 help / color / mirror / Atom feed
From: Tao Cui <cui.tao@linux.dev>
To: Tejun Heo <tj@kernel.org>
Cc: David Vernet <void@manifault.com>,
	sched-ext@lists.linux.dev, Andrea Righi <arighi@nvidia.com>,
	linux-kernel@vger.kernel.org, Changwoo Min <changwoo@igalia.com>
Subject: Re: [REPORT] sched_ext: sub-scheduler tasks are severely under-scheduled vs root-owned tasks
Date: Wed, 14 Oct 2026 12:24:29 +0800	[thread overview]
Message-ID: <e0cdb928-5e4d-487e-90ca-694a11b4ade1@linux.dev> (raw)
In-Reply-To: <14824587e8eb436b2621fef1bb681cce@kernel.org>

Hello, Tejun

在 2026/10/10 7:42, Tejun Heo 写道:
> Hello, Tao.
> 
> The following is a Claude-generated analysis.
> 
> On Fri, Oct 09, 2026 at 09:51:35AM +0800, Tao Cui wrote:
>> The scheduler is a single binary (scx_k2) with parent/root and
>> child/sub modes. The parent grants SCX_CAP_PERF | SCX_CAP_ENQ_IMMED
>> to the child over a cgroup, while the child only writes cpuperf
>> target 1 from ops.tick(). Both schedulers print their counters every
>> 2s.
> 
> Reproduced on for-7.4 with your sources in a 4 cpu VM. The starvation
> comes from the two schedulers, not the kernel.
> 
> The child's ops.enqueue() inserts every task into its own user DSQ. The
> only consumer of that DSQ is the child's ops.dispatch(), and a child's
> dispatch runs only when its parent calls scx_bpf_sub_dispatch() from its
> own ops.dispatch(). scx_k2's parent never does. See the comment above
> enum scx_cap_flags in kernel/sched/ext/internal.h and how scx_qmap
> dispatches its children. The SysRq-D dump during the stall shows the
> spinner in the child's DSQ 0 with a full slice while cpu0 runs root
> tasks.
> 
>> In an A/B test, having the parent kick the task's CPU from
>> ops.enqueue() with scx_bpf_kick_cid() and hand its dispatch turn to
>> the child with scx_bpf_sub_dispatch() prevents the starvation.
> 
> scx_bpf_sub_dispatch() is only callable from ops.dispatch(). With the
> parent calling it there, the child's dispatch does run, but every
> scx_bpf_dsq_move_to_local() is rejected because the grant lacks
> SCX_CAP_ENQ. SCX_CAP_ENQ_IMMED only covers IMMED inserts. The task goes
> to the reject DSQ and is reenqueued, SCX_EV_REENQ_REPEAT climbs on the
> child, and it still starves. With the sub dispatch call and SCX_CAP_ENQ
> granted as well, the spinner runs at full duty under the child.
> 
>> Actual: the child claims the task, and its ops.tick() initially
>> services it, but setcnt then stops increasing and the task receives
>> little or no CPU time - 0-91 ticks per 6s window across our runs,
>> most rounds 0.
> 
> The initial burst is the bypass DSQ drained during attach. In the
> move-after-attach order the task keeps running through dispatch's
> keep-last path until something else wakes on cpu0 and pushes it through
> the child's ops.enqueue(), which is why some windows look healthy. The
> stall watchdog catches it past your 6s window:
> 
>   sched_ext: BPF sub-scheduler "k2" disabled (runnable task stall)
>   sched_ext: k2: sh[1401] failed to run for 30.696s
> 
>> One possibility is that, on a NO_HZ idle CPU, no balance is
>> triggered for the child's DSQ, so the child's ops.dispatch() is
>> never called.
> 
> The parent's dispatch on cid 0 ran several times a second throughout
> the stall. It only consumed the parent's own DSQ.
> 

Thanks a lot for taking the time to reproduce this and for the detailed
analysis.

I completely missed that the child's ops.dispatch() only runs when the
parent explicitly delegates to it with scx_bpf_sub_dispatch(). I had
been assuming the core would eventually route balance to the child once
it owned the task, so I ended up chasing the wrong direction.

The SCX_CAP_ENQ vs SCX_CAP_ENQ_IMMED distinction also explains why my
A/B experiment wasn't actually proving what I thought it was. I hadn't
noticed that scx_bpf_dsq_move_to_local() requires SCX_CAP_ENQ, so the
reject/reenqueue path makes perfect sense now.

I'll update the scheduler to dispatch to the child as intended and
grant SCX_CAP_ENQ where appropriate, then rerun the tests.

Thanks again for the thorough explanation and for pointing me to the
internal.h comments and the scx_qmap implementation.

--
Tao

> Thanks.
> 


      reply	other threads:[~2026-10-14  4:24 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08  8:36 [REPORT] sched_ext: sub-scheduler tasks are severely under-scheduled vs root-owned tasks Tao Cui
2026-10-08  9:10 ` Tejun Heo
2026-10-09  1:51   ` Tao Cui
2026-10-09 23:42     ` Tejun Heo
2026-10-14  4:24       ` Tao Cui [this message]

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=e0cdb928-5e4d-487e-90ca-694a11b4ade1@linux.dev \
    --to=cui.tao@linux.dev \
    --cc=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 \
    /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