Re: [PATCH v3 7/7] sched/eevdf: Move to a single runqueue
From: Mike Galbraith
Date: Sun Nov 08 2026 - 04:07:11 EST
On Fri, 2026-11-06 at 17:43 +0100, Vincent Guittot wrote:
> On Tue, 3 Nov 2026 at 10:46, Chengfeng Lin <lin2530632123@xxxxxxxxx> wrote:
> >
> > Hi Tianchen, Mike,
> >
> > Thanks for the suggestion. Disabling DELAY_ZERO also improves the
> > 15-worker pipe-handoff case here.
> >
> > I tested the same v7.3 kernel image across three fresh boots with
> > DELAY_ZERO on, off, and on again. Both 333238da9a19 and DELAY_DEQUEUE
> > were retained. I ran context_switch1_threads with -s 15 and -t 2 or
> > -t 15, keeping the native CORE affinity on the same eight P-cores.
> >
> > Each entry below is the mean of nine runs, using the native 15-second
> > post-warmup measurement window. Throughput is in millions of handoffs/s:
> >
> > Workers On A Off On B Off vs. mean of On A/B
> > 2 1.156 1.159 1.158 +0.17%
> > 15 9.172 10.425 9.165 +13.70%
> >
> > The largest within-boot CV was 0.60%. The 15-worker on-again baseline
> > was 0.08% below the first one.
> >
> > GNU time's counters from the same calls, covering the whole invocation
> > including warmup, also show a change in switch types. At 15 workers,
> > the mean involuntary share of context switches was 0.36%, 41.48%, and
> > 0.36% for on/off/on. These results are consistent with your handoff
> > explanation and Mike's point that the added accounting changes
> > DELAY_DEQUEUE's behavior, beyond the cost of the update itself.
>
> 333238da9a19 ("sched: Update time before requeueing delayed entities")
> fixes a bug in how the lag of a delayed dequeued task is accounted.
>
> Let take the example of 2 tasks TA and TB running on root
>
> Without 333238da9a19:
>
> - task TA starts to sleep and is delayed dequeued with a negative lag
> of -1 usec
> - task TB becomes the current running task
> - During tick, update_curr() updates TB and moves forward its vruntime
> (and avg_vruntime) but it is still behind deadline/vprot so no
> resched().
> - TA's vruntime is now behind avg_vruntime (i.e. its lag is positive)
> - task TA wakes up just before the next tick so TB has not yet been
> updated for the last ongoing tick (up to 10ms)
If only these longish term ineligible survivors are the issue, perhaps
we could do something like below, and turn "cache cold" sleepers loose
to migrate, and only requeue the markedly shorter term still "cache
hot" sleepers? That way we get some LB benefit instead of grief from
the problem children, and sleepers still get the most important benefit
of DELAY_DEQUEUE (I think), namely short mutex et al sleeps that eats
away at heavily threaded applications ability to compete with compute
on an equal footing.
Repurposed Q/D hacklet against Peter's ttwu series...
627675b9c524 sched: Add ttwu_queue support for delayed tasks
...below.
---
kernel/sched/core.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3895,7 +3895,15 @@ static bool ttwu_queue_wakelist(struct t
*/
static int ttwu_runnable(struct task_struct *p, int wake_flags)
{
+ unsigned long thresh = sysctl_sched_migration_cost;
+
if (sched_feat(TTWU_QUEUE_DELAYED) && READ_ONCE(p->se.sched_delayed)) {
+ int task_hot;
+
+ /* XXX hack alert */
+ sched_clock_cpu(task_cpu(p)); /* Sync clocks across CPUs */
+ task_hot = task_rq(p)->clock_task - p->se.exec_start < thresh;
+
/*
* Similar to try_to_block_task():
*
@@ -3920,7 +3928,7 @@ static int ttwu_runnable(struct task_str
*/
p->wake_cpu = smp_processor_id();
- if (ttwu_queue_wakelist(p, task_cpu(p), wake_flags | WF_DELAYED))
+ if (task_hot && ttwu_queue_wakelist(p, task_cpu(p), wake_flags | WF_DELAYED))
return 1;
}
@@ -3933,8 +3941,18 @@ static int ttwu_runnable(struct task_str
update_rq_clock(rq);
if (p->is_blocked) {
if (p->se.sched_delayed) {
- dequeue_task(rq, p, DEQUEUE_NOCLOCK | DEQUEUE_DELAYED | DEQUEUE_SLEEP);
- return 0;
+ /*
+ * Deflect tasks that can migrate toward wakeup balancing logic,
+ * but only for longish sleeps so we don't inject the additional
+ * dequeue/enqueue overhead and migration chaos at high frequency.
+ * XXX pinned sleeper must go too if bug blocker is actually viable.
+ */
+ if (/* p->nr_cpus_allowed == 1 || */ rq_clock_task(rq) - p->se.exec_start < thresh)
+ enqueue_task(rq, p, ENQUEUE_NOCLOCK | ENQUEUE_DELAYED);
+ else {
+ dequeue_task(rq, p, DEQUEUE_NOCLOCK | DEQUEUE_DELAYED | DEQUEUE_SLEEP);
+ return 0;
+ }
}
if (proxy_needs_return(rq, p))
return 0;