[PATCH] [Question] sched/fair: Task starvation with RUN_TO_PARITY_WAKEUP under group topologies

Chen Jinghuang posted 1 patch 1 week, 6 days ago
kernel/sched/fair.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
[PATCH] [Question] sched/fair: Task starvation with RUN_TO_PARITY_WAKEUP under group topologies
Posted by Chen Jinghuang 1 week, 6 days ago
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
Re: [PATCH] [Question] sched/fair: Task starvation with RUN_TO_PARITY_WAKEUP under group topologies
Posted by K Prateek Nayak 1 week, 5 days ago
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