The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Rik van Riel <riel@surriel.com>
To: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>,
	Juri Lelli <juri.lelli@redhat.com>,
	Vincent Guittot <vincent.guittot@linaro.org>,
	Dietmar Eggemann <dietmar.eggemann@arm.com>,
	linux-kernel@vger.kernel.org, kernel-team@meta.com
Subject: [PATCH] sched/fair: clamp rescaled vlag in reweight_entity() to bound entity_key()
Date: Wed, 10 Jun 2026 10:55:29 -0400	[thread overview]
Message-ID: <20260610105529.14e014dd@fangorn> (raw)

A 252-CPU machine running an EEVDF kernel hard-locked up.  The trigger was
the s64 overflow guard in __sum_w_vruntime_add():

  WARNING: CPU: 181 ... at kernel/sched/fair.c __enqueue_entity+0x1fc
  ...
  __enqueue_entity / put_prev_entity / put_prev_task_fair / __schedule

firing during a reschedule, resulting in the CPU trying to wake up
the printk worker, while already holding the runqueue lock, resulting
in a deadlock.

Root cause for this scheduler bug is in the reweight path, not the
enqueue that tripped the WARN.

reweight_entity() preserves an entity's lag across a weight change by
scaling vlag in rescale_entity():

	se->vlag = vl' = vl * old_weight / new_weight;

and then, for an on_rq entity, recomputes:

	se->vruntime = avruntime - se->vlag;

On a large weight decrease (w' << w) this inflates vlag without bound;
nothing re-clamps it to the per-entity lag limit that entity_lag() and
update_entity_lag() enforce everywhere else.  The deadline is rescaled
and re-based separately, so only vruntime drifts.

For a group entity reweighted via update_cfs_group() this lets
se->vruntime drift arbitrarily far below cfs_rq->zero_vruntime.  A later
__enqueue_entity() then computes

	key        = entity_key(cfs_rq, se) = se->vruntime - cfs_rq->zero_vruntime
	weight     = avg_vruntime_weight(cfs_rq, se->load.weight)
	w_vruntime = key * weight

and trips the s64 overflow guard in __sum_w_vruntime_add():

	WARN_ON_ONCE((w_vruntime >> 63) != (w_vruntime >> 62));

leaving the avg-vruntime accounting and the rb-tree ordering corrupt,
which in turn wedges the load balancer (every CPU spinning in
sched_balance_rq()) into a hard lockup.

Observed on a 252-CPU machine on a depth-7 group sched_entity:

	cfs_rq->zero_vruntime = -503694424797
	se->vruntime          = -15964975487901
	key                   = -15461281063104   (~2^43.8)
	se->load.weight       = 438118, sum_shift = 0
	key * weight          = -6773865536804998272 = -(2^62.55)  -> WARN

Re-clamp vlag to the same bound entity_lag() uses, after the new weight
is installed:

	limit = calc_delta_fair(cfs_rq_max_slice(cfs_rq) + TICK_NSEC, se);
	se->vlag = clamp(se->vlag, -limit, limit);

Since calc_delta_fair(t, se) * se->load.weight <= t << NICE_0_LOAD_SHIFT
(equality but for the floor in __calc_delta), this bounds key * weight by
(max_slice + TICK_NSEC) << NICE_0_LOAD_SHIFT independent of the entity's
weight.  For the case above (max_slice = 3 ms, NICE_0_LOAD = 1 << 20):

	limit        = (4000000 << 20) / 438118 = 9573457        (~2^23.2)
	|key|          clamped from 2^43.8 -> 2^23.2  (20.6 bits, ~1.6e6x)
	key * weight <= 9573457 * 438118 = 4194303833926 = 2^41.9

i.e. ~20 bits of headroom below the 2^62 guard, so the overflow and the
resulting lockup cannot occur.  For legitimate reweights the rescaled
vlag is already within the bound, so the clamp is a no-op.

Fixes: 4823725d9d1d ("sched/fair: Increase weight bits for avg_vruntime")
Signed-off-by: Rik van Riel <riel@surriel.com>
Assisted-by: Claude:claude-opus-4-8
---
PS: do we want some LOGLEVEL_SCHED equivalent WARN_ON for inside
    the scheduler, so we can warn without a deadlock?

 kernel/sched/fair.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 1b23e73f48b0..49b48c5f5746 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -4669,6 +4669,7 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,
 	bool curr = cfs_rq->curr == se;
 	bool rel_vprot = false;
 	u64 avruntime = 0;
+	s64 limit;
 
 	if (se->on_rq) {
 		/* commit outstanding execution time */
@@ -4693,6 +4694,23 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,
 
 	update_load_set(&se->load, weight);
 
+	/*
+	 * rescale_entity() scaled vlag by old_weight/new_weight to preserve
+	 * lag across the reweight (vl' = vl * w/w').  On a large weight
+	 * decrease this can inflate vlag well past the legal lag bound.  Left
+	 * unclamped, the resulting se->vruntime = avruntime - vlag (computed
+	 * just below for an on_rq entity, or via place_entity() on the next
+	 * enqueue for an off_rq one) drifts far from cfs_rq->zero_vruntime, and
+	 * a subsequent __enqueue_entity() then overflows entity_key() * weight
+	 * in __sum_w_vruntime_add().  Re-clamp to the per-entity lag limit for
+	 * the new weight, exactly as entity_lag() does on every fresh lag.
+	 * Note calc_delta_fair(t, se) * se->load.weight <= t << NICE_0_LOAD_SHIFT
+	 * (equality but for the floor in __calc_delta), so this bounds
+	 * key * weight regardless of the entity's weight.
+	 */
+	limit = calc_delta_fair(cfs_rq_max_slice(cfs_rq) + TICK_NSEC, se);
+	se->vlag = clamp(se->vlag, -limit, limit);
+
 	do {
 		u32 divider = get_pelt_divider(&se->avg);
 		se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider);
-- 
2.53.0-Meta


             reply	other threads:[~2026-06-10 14:56 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-10 14:55 Rik van Riel [this message]
2026-06-11  8:19 ` [PATCH] sched/fair: clamp rescaled vlag in reweight_entity() to bound entity_key() Peter Zijlstra
2026-06-11 21:55   ` Rik van Riel

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=20260610105529.14e014dd@fangorn \
    --to=riel@surriel.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=kernel-team@meta.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=vincent.guittot@linaro.org \
    /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