public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Peter Zijlstra <peterz@infradead.org>
To: Zicheng Qu <quzicheng@huawei.com>
Cc: mingo@redhat.com, juri.lelli@redhat.com,
	vincent.guittot@linaro.org, dietmar.eggemann@arm.com,
	rostedt@goodmis.org, bsegall@google.com, mgorman@suse.de,
	vschneid@redhat.com, linux-kernel@vger.kernel.org,
	tanghui20@huawei.com
Subject: Re: [PATCH] sched/fair: Fix vruntime drift by preventing double lag scaling during reweight
Date: Mon, 26 Jan 2026 22:23:47 +0100	[thread overview]
Message-ID: <20260126212347.GV166857@noisy.programming.kicks-ass.net> (raw)
In-Reply-To: <20251226001731.3730586-1-quzicheng@huawei.com>

On Fri, Dec 26, 2025 at 12:17:31AM +0000, Zicheng Qu wrote:
> In reweight_entity(), when reweighting a currently running entity (se ==
> cfs_rq->curr), the entity remains on the runqueue context without
> undergoing a full dequeue/enqueue cycle.

I am horribly confused by this statement. In case current is on_rq
(most often the case) then it is dequeued just as much as any other
on_rq task being reweighted.

[edit: ... after much puzzling ...

Ooh, are you perhaps alluding to the fact that avg_vruntime() always
adds cfs_rq->curr in?]

> This means avg_vruntime()
> remains constant throughout the reweight operation.

Now this might have a point; but I don't see how it would be specific to
current in any way.

Consider the tree entities (vruntime,weight):

 (-40,10)
 ( 30,10)
 (-20,10)

This gives:

         -40*10 + 30*10 + -20*10   -300
 w_avg = ----------------------- = ---- = -10
                    30              30

Now, lets randomly pick (30,10) and do reweight to 5, then we have:

  vlag  = w_avg - vruntime = -10 - 30 = -40
  vlag' = vlag * weight / weight' = -40*10/5 = -80

Then placing that at -10 would give:

 (-40,10)
 ( 70,5 )
 (-20,10)

         -40*10 + 70*5 + -20*10   -250
 w_avg = ---------------------- = ---- = -10
                    25             25

However, if we pick any other entity, lets say (-40/10) and do the same:

 30*10/5 = 60

placing at -10 gives, (-70,5), giving the w_avg:

         -70*5 + 30*10 + -20*10   -250
 w_avg = ---------------------- = ---- = -10
                    25             25


Or rather, we can say that w_avg is/should-be invariant under the
reweight transform. Current or no current, it doesn't matter.

Which is more or less what we had prior to: 6d71a9c61604 ("sched/fair:
Fix EEVDF entity placement bug causing scheduling lag")

> However, the current implementation calls place_entity(..., 0) at the
> end of reweight_entity(). Under EEVDF, place_entity() is designed to
> handle entities entering the runqueue and calculates the virtual lag
> (vlag) to account for the change in the weighted average vruntime (V)
> using the formula:
> 
> 	vlag' = vlag * (W + w_i) / W
>
> Where 'W' is the current aggregate weight (including
> cfs_rq->curr->load.weight) and 'w_i' is the weight of the entity being
> enqueued (in this case, the se is exactly the cfs_rq->curr).
> 
> This leads to a "double scaling" logic for running entities:
> 1. reweight_entity() already rescales se->vlag based on the new weight
>    ratio.
> 2. place_entity() then mistakenly applies the (W + w_i)/W scaling again,
>    treating the reweight as a fresh enqueue into a new total weight
> pool.
> 
> This can cause the entity's vlag to be amplified (if positive) or
> suppressed (if negative) incorrectly during the reweight process.

Again, nothing specific to current here.

[edit: except for the fact that avg_vruntime() actually does include
current when re-adding current -- but that still doesn't explain why it
would be a good idea to have two different ways of doing reweight]

This all suggests we should revert 6d71a9c61604 ("sched/fair: Fix EEVDF
entity placement bug causing scheduling lag"), except we first need an
explanation for the problem described there.

At the time I observed reweight cycles 1048576 -> 2 -> 1048576 inflating
the lag, which obviously should not be.

I might have made a mistake, but we should ensure we're not
re-introducing that problem, so let me see if I can figure out where
that came from if not from this.

Now, poking at all this, I did find some numerical stability issues, but
rather than blowing up like I saw for 6d71a9c61604, these cause lag to
evaporate (also not good).

Observe the below proglet; when using w_avg_t (for truncate, like
sum_w_vruntime with use of scale_load_weight()):

vruntime: 40960 (-51200) -> 40960 (-51200)
avg: -10240 -- -10240
vruntime: 40960 (-51200) -> 26843535360 (-26843545600)
avg: -35840 -- -35840
vruntime: 26843535360 (-26843571200) -> 15360 (-51200)
avg: -18773 -- -18773
vruntime: 15360 (-34133) -> 17895503531 (-17895522304)
avg: -35840 -- -35840
vruntime: 17895503531 (-17895539371) -> -1707 (-34133)
avg: -24462 -- -24462
vruntime: -1707 (-22755) -> 11930148978 (-11930173440)
avg: -35840 -- -35840
vruntime: 11930148978 (-11930184818) -> -13085 (-22755)
avg: -28255 -- -28255


Where as, if we run it with w_avg_n:

vruntime: 40960 (-51200) -> 40960 (-51200)
avg: -10240 -- -10240
vruntime: 40960 (-51200) -> 26843535360 (-26843545600)
avg: -10240 -- -35840
vruntime: 26843535360 (-26843545600) -> 40960 (-51200)
avg: -10240 -- -10240
vruntime: 40960 (-51200) -> 26843535360 (-26843545600)
avg: -10240 -- -35840
vruntime: 26843535360 (-26843545600) -> 40960 (-51200)
avg: -10240 -- -10240
vruntime: 40960 (-51200) -> 26843535360 (-26843545600)
avg: -10240 -- -35840
vruntime: 26843535360 (-26843545600) -> 40960 (-51200)
avg: -10240 -- -10240

Specifically, the problem is because the 2 weight is below the
representable value, causing accuracy issues for avg_vruntime which
propagate.

Luckily, we recently replaced min_vruntime with zero_vruntime, which
tracks avg_vruntime far better resulting in smaller 'keys', which in
turn -- as it happens -- allows us to get rid of that
scale_load_down().


Could you please try queue.git/sched/reweight ?

I've only confirmed it boots on my machine. I'll prod a little more at
it tomorrow.


---
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define min(a,b) \
	({ __typeof__ (a) _a = (a); \
	   __typeof__ (b) _b = (b); \
	   _a < _b ? _a : _b; })

struct e { int64_t vr; int64_t w; };

const int scale = 1024;

struct e es[] = {
	{  40*scale, 1024*1024 },
	{ -20*scale, 1024*1024 },
	{ -50*scale, 1024*1024 },
};

const int n = sizeof(es)/sizeof(es[0]);

int64_t w_avg_n(void)
{
	int64_t ws = 0, w = 0;
	for (int i=0; i<n; i++) {
		w += es[i].w;
		ws += es[i].vr * es[i].w;
	}
	return ws/w;
}

int64_t w_avg_t(void)
{
	int64_t ws = 0, w = 0;
	for (int i=0; i<n; i++) {
		int64_t t = min(2, es[i].w >> 10);
		w += t;
		ws += es[i].vr * t;
	}
	return ws/w;
}

typedef int64_t (*avg_f)(void);

avg_f w_avg = &w_avg_t; // w_avg_n for good, w_avg_t for broken

void reweight(int i, int64_t w)
{
	int64_t a = w_avg();
	int64_t t, vl = a - es[i].vr;
	t = (vl * es[i].w) / w;
	printf("vruntime: %Ld (%Ld) -> %Ld (%Ld)\n", es[i].vr, vl, a-t, t);
	es[i].vr = a - t;
	es[i].w = w;
}

int main(int argc, char **argv)
{
	int i = 0;
	
	if (argc > 1)
		i = atoi(argv[1]);

	reweight(i, 1024*1024);
	printf("avg: %Ld -- %Ld\n", w_avg(), w_avg_t());
	for (int j=0; j<3; j++) {
		reweight(i, 2);
		printf("avg: %Ld -- %Ld\n", w_avg(), w_avg_t());
		reweight(i, 1024*1024);
		printf("avg: %Ld -- %Ld\n", w_avg(), w_avg_t());
	}
	return 0;
}


      parent reply	other threads:[~2026-01-26 21:23 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-26  0:17 [PATCH] sched/fair: Fix vruntime drift by preventing double lag scaling during reweight Zicheng Qu
2026-01-08  6:27 ` Zicheng Qu
2026-01-09  4:50 ` K Prateek Nayak
2026-01-09  8:40   ` Zicheng Qu
2026-01-09 10:21     ` K Prateek Nayak
2026-01-12  4:03       ` Zicheng Qu
2026-01-26 21:23 ` Peter Zijlstra [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=20260126212347.GV166857@noisy.programming.kicks-ass.net \
    --to=peterz@infradead.org \
    --cc=bsegall@google.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mgorman@suse.de \
    --cc=mingo@redhat.com \
    --cc=quzicheng@huawei.com \
    --cc=rostedt@goodmis.org \
    --cc=tanghui20@huawei.com \
    --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