kernel/sched/fair.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
Hi all,
While adapting the RUN_TO_PARITY_WAKEUP feature on top of mainline v7.2-rc2,
I encountered a severe task starvation issue under specific cgroup topologies.
Specifically, a running task completely hogs the CPU and prevents any
preemption.
Test Topology and Observed Behavior:
- CPU 0: Bound with two tasks: A1 (non-sleeping task) from cgroup A, and
B1 (frequent sleep/wake task) from cgroup B.
- CPU 20: Bound with other tasks from cgroup A (A2, A3... which sleep/wake
frequently) and task C1 (non-sleeping task) from cgroup C.
- All cgroups maintain default shares.
CPU 0 CPU 20 / Other CPUs
+----------------------------+ +----------------------------+
| cgroup A cgroup B | | cgroup A cgroup C |
| |- A1 (Run) |- B1 | | |- A2, A3... |- C1 |
| (Non-sleeping) (50us S) | | (Freq S/W) (Non-sleeping)|
| (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)
-> protect_slice() returns true -> pick_eevdf always returns curr
-> Task A1 constantly hogs the CPU
According to function_graph traces, the frequent sleep/wake cycles of
sibling tasks (A2/A3) on CPU 20 cause gse(A) on CPU 0 to undergo continuous
reweighting via update_cfs_cgroup() -> reweight_entity(). This triggers the
following chain reaction:
1. gse->vlag triggers the lag clamping logic in entity_lag().
2. Due to the clamping limit, A1->vruntime drifts unidirectionally against
avg_vruntime (i.e., gse->vruntime drops abnormally even though its weight
remains stable).
3. protect_slice() constantly returns true, forcing pick_eevdf() to always
return curr(A1) during wakeup preemption checks.
4. As a result, A1 indefinitely hogs CPU 0. Even in entity_tick(),
update_curr() fails to trigger resched_curr(). Both wakeup and periodic
preemptions end up picking curr.
Analysis:
Experimentally, either disabling RUN_TO_PARITY_WAKEUP or reverting the vlag
clamping patches completely resolves this starvation issue.
This indicates a problematic interaction between the extended runtime
introduced by RUN_TO_PARITY_WAKEUP and the vlag clamping mechanism. While
A1 runs beyond its expected share, frequent sibling-induced reweighting
pushes vlag to its limit, pulling A1->vruntime artificially closer to
avg_vruntime. However, A1->deadline and A1->vprot are still rescaled in
rescale_entity(). This mathematical asymmetry effectively ensures that
A1->vruntime < A1->vprot`always holds true. Consequently, protect_slice()
permanently returns true, trapping the scheduler into believing A1 has not
yet exhausted its promised slice, thereby completely blocking legitimate
context switches.
Discussion:
I'd love to hear your thoughts on how we can fix this issue without losing
the throughput gains of RUN_TO_PARITY_WAKEUP.
Thanks all.
Signed-off-by: Chen Jinghuang <chenjinghuang2@huawei.com>
---
kernel/sched/fair.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index d78467ec6ee1..00869624d914 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -1157,7 +1157,23 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect)
return cfs_rq->next;
}
- if (curr && (!curr->on_rq || !entity_eligible(cfs_rq, curr)))
+ if (curr && !curr->on_rq)
+ curr = NULL;
+
+ /*
+ * When an entity with positive lag wakes up, it pushes the
+ * avg_vruntime of the runqueue backwards. This may causes the
+ * current entity to be ineligible soon into its run leading to
+ * wakeup preemption.
+ *
+ * To prevent such aggressive preemption of the current running
+ * entity during task wakeups, skip the eligibility check if the
+ * slice promised to the entity since its selection has not yet
+ * elapsed.
+ */
+ if (curr &&
+ !(sched_feat(RUN_TO_PARITY_WAKEUP) && protect && protect_slice(curr)) &&
+ !entity_eligible(cfs_rq, curr))
curr = NULL;
if (curr && protect && protect_slice(curr))
--
2.34.1
Hello Chen, On 7/12/2026 5:01 PM, Chen Jinghuang wrote: > Hi all, > > While adapting the RUN_TO_PARITY_WAKEUP feature on top of mainline v7.2-rc2, > I encountered a severe task starvation issue under specific cgroup topologies. > Specifically, a running task completely hogs the CPU and prevents any > preemption. I'm assuming you are referring to https://lore.kernel.org/lkml/20240325060226.1540-2-kprateek.nayak@amd.com/ There was a reason it was not mainlined for this exact issue. ... > Discussion: > I'd love to hear your thoughts on how we can fix this issue without losing > the throughput gains of RUN_TO_PARITY_WAKEUP. Run your tasks as SCHED_BATCH and they'll forego wakeup preemption. This is the exact recipe that had helped a bunch of workloads that suffered throughput degradation from wakeup preemption after EEVDF was introduced. If you have well behaved workloads, you can also play around with custom slice (sched_attr.sched_runtime for fair tasks) which gets propagated down the cgroup hierarchy and should be better behaved. > > Thanks all. > > Signed-off-by: Chen Jinghuang <chenjinghuang2@huawei.com> > --- > kernel/sched/fair.c | 18 +++++++++++++++++- > 1 file changed, 17 insertions(+), 1 deletion(-) > > diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c > index d78467ec6ee1..00869624d914 100644 > --- a/kernel/sched/fair.c > +++ b/kernel/sched/fair.c > @@ -1157,7 +1157,23 @@ static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq, bool protect) > return cfs_rq->next; > } > > - if (curr && (!curr->on_rq || !entity_eligible(cfs_rq, curr))) > + if (curr && !curr->on_rq) > + curr = NULL; > + > + /* > + * When an entity with positive lag wakes up, it pushes the > + * avg_vruntime of the runqueue backwards. This may causes the > + * current entity to be ineligible soon into its run leading to > + * wakeup preemption. > + * > + * To prevent such aggressive preemption of the current running > + * entity during task wakeups, skip the eligibility check if the > + * slice promised to the entity since its selection has not yet > + * elapsed. > + */ > + if (curr && > + !(sched_feat(RUN_TO_PARITY_WAKEUP) && protect && protect_slice(curr)) && > + !entity_eligible(cfs_rq, curr)) > curr = NULL; We also have a bunch of changes in tip:sched/core that improves wakeup preemption with custom slices. You may want to check them out too: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/log/?h=sched/core > > if (curr && protect && protect_slice(curr)) -- Thanks and Regards, Prateek
© 2016 - 2026 Red Hat, Inc.