Re: [PATCH] sched/stats: Fix run_delay over-count for proxy-migrated blocked donors

From: albin_yang

Date: Sun Nov 01 2026 - 09:32:49 EST


On Wed, Oct 28, 2026 at 09:10:20AM +0530, K Prateek Nayak wrote:
> Yup! on_cpu / task_current() gets dealt at sched_info_switch()
> which will enqueue the signal if the task is preempted.

Prateek, Kayra, Peter -- thanks for the reviews and suggestions.

I tested the full diff below (comments stripped for brevity).

diff --git a/kernel/sched/core.c b/kernel/sched/core.c
@@ static int ttwu_runnable(struct task_struct *p, int wake_flags) @@
if (!task_on_cpu(rq, p)) {
wakeup_preempt(rq, p, wake_flags | WF_TTWU_RQ);
+ sched_info_enqueue(rq, p);
}
ttwu_do_wakeup(p);
return 1;

diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h
@@ static inline void sched_info_dequeue(struct rq *rq, struct task_struct *t) @@
unsigned long long delta = 0;
+ if (t->is_blocked)
+ t->sched_info.last_queued = 0;

if (!t->sched_info.last_queued)
return;
@@ static inline void sched_info_enqueue(struct rq *rq, struct task_struct *t) @@
- if (!t->sched_info.last_queued)
+ if (!t->sched_info.last_queued || t->is_blocked)
t->sched_info.last_queued = rq_clock(rq);

Results -- run_delay is field 2 of /proc/<tid>/schedstat:

proxy exec, donor migrated to the owner's CPU (~170 ms block)
unpatched 167.07-167.09 ms 5/5 migrated
patched 0.00 ms 20/20 migrated
same-cpu 0.00 ms 0/5 not migrated

DELAY_DEQUEUE, sleeper migrated across CPUs (~150 ms sleep)
unpatched 147.94-147.99 ms 5/20
patched 0.00 ms 0/100

Accounting stays alive in both: the proxy reproducer's sanity check
still gains ~39 ms per 80 ms contention window in every run, and the
DELAY_DEQUEUE one shows run_delay growing ~40 ms per round. The latter
also self-checks -- it has to reproduce unpatched, and it does.

Agreed with Kayra and Prateek that p==current needs no call:
try_to_wake_up() WARN_ON_ONCE()s on both se.sched_delayed and
is_blocked there, so that task is running, not waiting.

One ask, about the split rather than the code: keep the DELAY_DEQUEUE
fix as its own patch on se.sched_delayed. p->is_blocked arrived in
4c2a20413d7f and first appears in v7.2-rc1, while that over-count goes
back to v6.12 (152e11f6df29) -- folded in, it cannot be built or
backported for v6.12.y..v7.1.y. It already exists as v4, already
reviewed and tested:

https://lore.kernel.org/all/20261024042753.2276779-1-albin_yang@xxxxxxx/

Happy to be overruled.

On the proxy gate itself, dropping my own !task_is_blocked():
Prateek's covers at least the same ground, and I could not measure a
difference.

Still unmeasured: the donor already being back on p->wake_cpu -- the
case that motivated the ttwu_runnable() call. Every round I measured
ended up with task_cpu(p) != p->wake_cpu, so proxy_needs_return() did
the dequeue and the call was never reached. Forcing that path needs
the owner to migrate mid-hold and the donor to follow it, which is
timing sensitive -- I'll report if I get it, but don't hold the patch
on it.

I'm happy to post this as a series if that helps -- how would you
suggest splitting it?

Thanks,
Wei Yang