Re: [RFC PATCH] sched/fair: Address avruntime drift caused by entity_lag()
From: chenjinghuang
Date: Tue Jul 07 2026 - 08:51:27 EST
On 7/7/2026 12:03 AM, Vincent Guittot wrote:
> On Fri, 3 Jul 2026 at 11:50, Chen Jinghuang <chenjinghuang2@xxxxxxxxxx> wrote:
>>
>> While running EEVDF-related test cases, we observed an anomalous
>> phenomenon where the vruntime of a continuously running scheduling
>> entity (se) exhibits an overall downward trend.
>>
>> The test topology and observed behavior are as follows:
>>
>> CPU 0 CPU 1 / Other CPUs
>> +----------------------------+ +----------------------------+
>> | cgroup A cgroup B | | cgroup A cgroup B |
>> | |- A1 (Run) |- B1 | | |- A2, A3... |- B2, B3...|
>> | (50us S)| | (Frequent sleep/wake) |
>> | (10us W)| | |
>> +----------------------------+ +----------------------------+
>> |
>> v
>> A2/A3 frequent sleep/wake -> Frequent reweight of gse(A) on CPU 0
>> -> Triggers vlag clamping -> Unidirectional avruntime drift
>> -> gse->vruntime drops abnormally (while gse weight remains stable)
>>
>> According to function_graph traces, frequent sleep/wake cycles of
>> sibling tasks within the same cgroup cause the running task to repeatedly
>> trigger update_cfs_cgroup() -> reweight_entity().
>>
>> The root cause appears to point to the entity_lag() boundary limit
>> introduced in `reweight_entity()` to prevent overflows. During repeated
>> weight scaling, this limit introduces an asymmetric cumulative error.
>> Connsider an example here where se->vruntime > avruntime:
>>
>> 1. Downscale:
>> se->vlag = entity_lag(cfs_rq, se, avruntime);
>
> The clamp applied in entity_lag() should "in theory never" happen.
>
However, I observed that vlag does indeed get clamped in entity_lag() on
CPU 0 in the test case
> Do you have more details to share about your setup?
>
Here is the exact layout of the cgroups:
- cgroup A: Besides task A1 (which is bound to CPU 0), there are 20 other
tasks running on the remaining CPUs (excluding CPU 0) that frequently
sleep and wake up. This setup is designed to continuously trigger weight
adjustments for A1.
- cgroup B: There is only a single task B1 bound to CPU 0, which sleeps for
long periods and wakes up briefly to compete with A1.(Please note that my
previous diagram mistakenly showed other tasks running under cgroup B on
other CPUs; that was an error on my part).
> Which kernel version do you use ? A number of changes have been queued
> recently that should ensure that the vlag never goes above the
> theoretical limit.
>
I am running the mainline kernel, specifically on the v7.2-rc2 tag.
> How many CPUs are in the system ?
> Is there other tasks running in the cgroups ?
>
The system has 32 CPUs in total.
> Could you try to run without RUN_TO_PARITY ?
> echo NO_RUN_TO_PARITY > /sys/kernel/debug/sched/features
>
I tried disabling RUN_TO_PARITY, but the issue persists—vlag
still exceeds the limit and triggers the clamp.
> Do you set the share of task groups to particular values?
> Do you change the slice of the tasks ?
>
No, I haven't set specific shares for the task groups (they are all at the
default value of 1024), nor have I modified the default task slices.
> Note that even without clamping, part of the vlag is lost in div64_long
>
>> se->vlag = div64_long(se->vlag * old_weight, weight);
>> se->vruntime = avruntime - se->vlag;
>>
>> If the calculated vlag exceeds the limit boundary, it is clamped.
>> This causes the scaled vlag to be bigger than its theoretical value,
>> making the resulting se->vruntime smaller than it should be (forced
>> closer to avruntime).
>>
>> 2. Upscale:
>> Because se->vruntime already carries the clamping error from the
>> previous step, it implicitly shifts the subsequent global avruntime
>> computation trajectory. When the weight is restored, se->vruntime
>> deviates from its original true value, preventing the true vlag from
>> being reconstructed and leading to a unidirectional avruntime drift.
>>
>> Under frequent cgroup reweighting, this asymmetric truncation error
>> accumulates, causing a unidirectional downward drift of vruntime.
>>
>> Regarding this vlag limit mechanism in reweight_entity(), we would like
>> to raise a few questions for discussion:
>>
>> Question 1: Is this deviation of se->vruntime expected?
>> For a currently running entity (curr se), due to RUN_TO_PARITY, it
>> typically operates in a state where se->vruntime > avruntime. During
>> reweighting, the limit forces its vruntime downward (closer to avruntime),
>> while attributes like deadline remain unaffected by the limit.
>>
>> Question 2: Is the unidirectional drift of avruntime reasonable?
>> Since avruntime = avg_vruntime(), the deviation of se->vruntime
>> before and after reweighting (while its weight remains unchanged) shifts
>> the global avruntime downward. According to EEVDF core principles,
>> avruntime should remain invariant across weight changes.
>> (https://lore.kernel.org/lkml/20260415211149.2658910-1-jstultz@xxxxxxxxxx/),
>>
>> Question 3: Is the reduction of avruntime fair to other entities?
>> A non-functional reduction in global avruntime passively decreases the
>> se->vlag of other queued entities on the runqueue, which practically
>> worsens the starvation of those tasks.
>>
>> Question 4: Does this lead to complete preemption suppression?
>> In entity_tick -> update_deadline, because curr->vruntime is abnormally
>> pulled down by the clamping error while its deadline is unchanged, the
>> condition vruntime < deadline can be continuously satisfied at every
>> tick. This skips resched_curr(), suppressing preemption until the
>> frequent reweighting stops or curr's vlag falls back within the limit.
>>
>> While entity_lag() was originally introduced to prevent overflow-induced
>> NULL pointer dereferences, given its significant impact on EEVDF fairness,
>> should the necessity and implementation of this limit be reconsidered?
>>
>> Analyzed-by: Hui Tang <tanghui20@xxxxxxxxxx>
>> Analyzed-by: Zhang Qiao <zhangqiao22@xxxxxxxxxx>
>> Analyzed-by: Zicheng Qu <quzicheng@xxxxxxxxxx>
>> Analyzed-by: Chen Jinghuang <chenjinghuang2@xxxxxxxxxx>
>