[RFC PATCH] sched/fair: Address avruntime drift caused by entity_lag()
From: Chen Jinghuang
Date: Fri Jul 03 2026 - 05:51:34 EST
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);
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>