From: Xukai Wang <kingxukai@zohomail.com>
To: K Prateek Nayak <kprateek.nayak@amd.com>,
Ingo Molnar <mingo@redhat.com>, John Stultz <jstultz@google.com>,
Peter Zijlstra <peterz@infradead.org>,
Juri Lelli <juri.lelli@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>,
Dietmar Eggemann <dietmar.eggemann@arm.com>,
Steven Rostedt <rostedt@goodmis.org>,
Ben Segall <bsegall@google.com>, Mel Gorman <mgorman@suse.de>,
Valentin Schneider <vschneid@redhat.com>,
Andrea Righi <arighi@nvidia.com>
Cc: linux-kernel@vger.kernel.org
Subject: Re: [PATCH RFC v3] sched/proxy: Defer donor commit until after proxy resolution
Date: Mon, 12 Oct 2026 22:34:55 +0800 [thread overview]
Message-ID: <ab001a53-e2d2-42e9-ba40-8b43a71a7d64@zohomail.com> (raw)
In-Reply-To: <adaa5bee-bfe9-4a03-acc7-0e41c5242e7d@amd.com>
Hi Prateek,
Thanks for the detailed reply.
On 2026/10/12 17:21, K Prateek Nayak wrote:
> I see. I guess, proxy_reshed_idle() would be a better spot to
> consolidate it rather than juggling it in __schedule().
>
>> The next pick clearing rq->dl_server does not undo that assignment,
>> since the pointer has already moved to rq->idle->dl_server. It is only
>> cleared later when idle becomes the prev task of another
>> put_prev_set_next_task() transition.
> Which should be the very next __schedule() since we retain NEED_RESCHED
> except if all the tasks migrate away and we genuinely select rq->idle,
> then the dl_server lingers around.
...
>> If proxy resolution returns NULL or idle, the saved value is not
>> restored into rq->dl_server for a donor commit that will not happen.
>>
>> So the intent is to keep the DL-server selection made for the candidate
>> out of rq->dl_server while proxy resolution is in progress, and restore
>> it only if that donor is actually going to be committed, while keeping
>> the state seen by find_proxy_task() consistent with the pre-patch path.
>>
>> Do you think rq->idle->dl_server being set in the second sequence above
>> is harmless, or is keeping the save/clear/restore the right way to avoid
>> that state?
> I assume it may take the dl_defer_idle path but I'm not super sure. I
> personally prefer resetting state in proxy_resched_idle() since it is
> all in one place to refer at any point.
I agree with your suggestion. So I removed the save/clear/restore around
find_proxy_task() and now clear rq->dl_server in proxy_resched_idle()
before committing idle.
This leaves the successful-resolution path untouched, while a NULL
retry lets the next pick_next_task() reset/recompute rq->dl_server.
> On current tip + sched-ext changes lined for v7.4 from
> https://lore.kernel.org/lkml/20260922165445.943315-9-arighi@nvidia.com/
> some sched-ext bits will need rq->donor to already reflect the next
> selection before they are called
I rebased this onto the latest tip, and also applied the patch on top of
Andrea's sched_ext proxy-exec changes through the curr/donor split to
check the resulting ordering.
The rebased scheduling path now looks roughly like this:
```
pick_again:
donor = pick_next_task(rq, &rf);
rq->next_class = donor->sched_class;
next = donor;
if (sched_proxy_exec()) {
struct task_struct *prev_donor = rq->donor;
donor->blocked_donor = NULL;
if (unlikely(donor->is_blocked)) {
next = find_proxy_task(rq, donor, &rf);
if (!next)
goto pick_again;
if (next == rq->idle) {
scx_proxy_reenqueue_retry(rq, next);
goto keep_resched;
}
}
put_prev_set_next_task(rq, prev_donor, donor);
if (donor == prev_donor && prev != next) {
donor->sched_class->put_prev_task(rq, donor, donor);
donor->sched_class->set_next_task(rq, donor,
SNT_PICK);
}
rq_set_donor(rq, donor);
scx_proxy_donor_start(rq);
scx_proxy_reenqueue_retry(rq, next);
} else {
put_prev_set_next_task(rq, rq->donor, donor);
rq_set_donor(rq, donor);
}
```
and proxy_resched_idle() now does:
```
proxy_resched_idle()
{
rq->dl_server = NULL;
put_prev_set_next_task(...);
...
}
```
So the proxy-specific handling stays in one place in __schedule(), and
the existing sched_ext hooks remain in their current order.
I also looked more closely at the rq->donor dependency
in do_pick_task_scx():
```
static struct task_struct *do_pick_task_scx(...)
{
struct task_struct *prev = rq->donor;
...
}
```
Suppose A is the currently committed donor and the first SCX pick
selects blocked donor D.
With the current early-commit ordering, the sequence is roughly:
```
next = pick_next_task(); /* rq->donor is A */
|-> pick_next_task_scx(); /* SCX pick with rq->donor
is A, selects D */
|-> set_next_task_scx(D); /* D is removed from the local DSQ */
rq_set_donor(rq, D); /*rq->donor is D */;
next = find_proxy_task(D); /* if next == NULL */
if (!next) goto pick_again;
pick_again:
pick_next_task(); /* rq->donor is D */
|-> pick_next_task_scx(); /* SCX pick with prev is D, and D may
be retained through SCX_DSP_PREV. */
```
With the deferred ordering:
```
pick_next_task(); /* rq->donor is A */
|-> pick_next_task_scx(prev=A); /* selects D */
next = find_proxy_task(D); /* if next == NULL */
if (!next) goto pick_again;
pick_again:
pick_next_task(); /* rq->donor is A */
|-> pick_next_task_scx(prev=A); /* D is still SCX_TASK_QUEUED
and still on the local DSQ, because set_next_task_scx() has not run yet. */
```
If find_proxy_task() did not otherwise change the relevant runqueue/SCX
state, the retry therefore starts from the same committed prev=A, while
D remains the local candidate selected by the first pass. Since the
first pass did not retain A through SCX_DSP_PREV, the retry reaches the
local DSQ again and selects D through SCX_DSP_LOCAL.
So the internal retry mechanism changes from `provisional D ->
SCX_DSP_PREV` to `committed A + queued D -> SCX_DSP_LOCAL` without
changing the candidate in the unchanged-state case.
If find_proxy_task() deactivates or migrates D, then the runqueue state
has genuinely changed and the retry should of course select against that
new state. In that case it seems preferable for rq->donor to continue
describing the last committed scheduling context rather than committing
D provisionally and repairing that state afterwards.
Similarly, if SCX dispatch drops the rq lock, the existing
lock_drop_seq / SCX_DSP_RETRY handling already restarts selection
against the new state, so I don't see a need for an early rq->donor
update there either.
For a successful proxy resolution, the existing sched_ext ordering still
works unchanged:
```
find_proxy_task(D);
put_prev_set_next_task(..., D);
rq_set_donor(D);
scx_proxy_donor_start(); /* ops.running(D) is still issued only after D
has become the committed rq->donor. */
```
Given this, I don't see an additional sched_ext complexity introduced by
deferring the commit; the main difference is that rq->donor continues to
describe the committed scheduling context while proxy resolution is
still provisional.
Also, I probably wouldn't characterize the main benefit of this change
as a performance optimization. The end-to-end performance impact I
measured is indeed small.
The main benefit I am after is the scheduling-state semantics. The
invariant I am trying to preserve is that rq->donor and the
scheduler-class current state describe a scheduling context which has
survived proxy resolution, rather than a provisional candidate which
may still be deactivated, migrated, or cause the proxy walk to retry.
In other words, the intended ordering is:
1. pick candidate
2. resolve proxy chain
3. commit donor / scheduler-class state
rather than:
1. pick candidate
-> commit donor / scheduler-class state
2. resolve proxy chain
-> possibly discard that candidate and retry 1.
The reduction in redundant put_prev_task()/set_next_task() callbacks is
a consequence of keeping that commit boundary after proxy resolution,
rather than the primary reason for the change.
The sched_ext integration also seems to illustrate why keeping the
commit boundary after proxy resolution is useful. In Andrea's
'sched_ext: Fix ops.running/stopping() pairing for proxy-exec donors'
commit (5f882051efabaf19f9b4dbe4b50cee1d250ac19e), set_next_task_scx()
has to treat a blocked donor specially and defer ops.running() to
scx_proxy_donor_start(), specifically because proxy resolution can still
fail or migrate the donor after set_next_task_scx() has run.
> Thank you but I'd wait for Peter and John to chime in regarding the added
> code complexity given the visible benefits of this optimization isn't
> too much.
Thanks. I'll wait for Peter/John's thoughts before sending v4.
--
Best regards,
Xukai
prev parent reply other threads:[~2026-10-12 14:35 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-27 9:18 Xukai Wang
2026-10-10 12:57 ` Xukai Wang
2026-10-12 4:05 ` K Prateek Nayak
2026-10-12 7:39 ` Xukai Wang
2026-10-12 9:21 ` K Prateek Nayak
2026-10-12 14:34 ` Xukai Wang [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=ab001a53-e2d2-42e9-ba40-8b43a71a7d64@zohomail.com \
--to=kingxukai@zohomail.com \
--cc=arighi@nvidia.com \
--cc=bsegall@google.com \
--cc=dietmar.eggemann@arm.com \
--cc=jstultz@google.com \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=peterz@infradead.org \
--cc=rostedt@goodmis.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.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
all inboxes | Powered by JetHome®