mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] sched/fair: Fix cgroup::idle preemption
@ 2026-11-05 16:55 Peter Zijlstra
  2026-11-05 16:55 ` [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle() Peter Zijlstra
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Peter Zijlstra @ 2026-11-05 16:55 UTC (permalink / raw)
  To: mingo
  Cc: linux-kernel, peterz, juri.lelli, vincent.guittot,
	dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
	kprateek.nayak, joshdon, tj, jakub

Hi!

so I finally got around to looking at this again with a fresh mind and an LLM
generated testcase. It appears to actually work now.

I'm still not convinced on the whole find_matching_se() thing; at the end of
the day we mark a subtree 'idle', which is akin to saying 'dont care', so then
actually caring about how it ends up preempting on a different level is weird
to me.

As such, I have Vincent's patch last; modified to use the new idle state as
optimization.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle()
  2026-11-05 16:55 [PATCH 0/3] sched/fair: Fix cgroup::idle preemption Peter Zijlstra
@ 2026-11-05 16:55 ` Peter Zijlstra
  2026-11-05 17:42   ` Kayra Cizmeci
  2026-11-05 16:55 ` [PATCH 2/3] sched/fair: Make cfs_rq::idle the sum of ancestor tg::idle Peter Zijlstra
  2026-11-05 16:55 ` [RFC][PATCH 3/3] sched/eevdf: Fix sched idle task group preemption Peter Zijlstra
  2 siblings, 1 reply; 6+ messages in thread
From: Peter Zijlstra @ 2026-11-05 16:55 UTC (permalink / raw)
  To: mingo
  Cc: linux-kernel, peterz, juri.lelli, vincent.guittot,
	dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
	kprateek.nayak, joshdon, tj, jakub

Cleanup the code by using guards; prepares for further changes in this function.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 kernel/sched/fair.c |   16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15790,12 +15790,9 @@ int sched_group_set_idle(struct task_gro
 	if (idle < 0 || idle > 1)
 		return -EINVAL;
 
-	mutex_lock(&shares_mutex);
-
-	if (tg->idle == idle) {
-		mutex_unlock(&shares_mutex);
+	guard(mutex)(&shares_mutex);
+	if (tg->idle == idle)
 		return 0;
-	}
 
 	tg->idle = idle;
 
@@ -15805,13 +15802,12 @@ int sched_group_set_idle(struct task_gro
 		struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, i);
 		bool was_idle = cfs_rq_is_idle(grp_cfs_rq);
 		long idle_task_delta;
-		struct rq_flags rf;
 
-		rq_lock_irqsave(rq, &rf);
+		guard(rq_lock_irqsave)(rq);
 
 		grp_cfs_rq->idle = idle;
 		if (WARN_ON_ONCE(was_idle == cfs_rq_is_idle(grp_cfs_rq)))
-			goto next_cpu;
+			continue;
 
 		idle_task_delta = grp_cfs_rq->h_nr_queued -
 				  grp_cfs_rq->h_nr_idle;
@@ -15830,9 +15826,6 @@ int sched_group_set_idle(struct task_gro
 			if (cfs_rq_is_idle(cfs_rq))
 				break;
 		}
-
-next_cpu:
-		rq_unlock_irqrestore(rq, &rf);
 	}
 
 	/* Idle groups have minimum weight. */
@@ -15841,7 +15834,6 @@ int sched_group_set_idle(struct task_gro
 	else
 		__sched_group_set_shares(tg, NICE_0_LOAD);
 
-	mutex_unlock(&shares_mutex);
 	return 0;
 }
 



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/3] sched/fair: Make cfs_rq::idle the sum of ancestor tg::idle
  2026-11-05 16:55 [PATCH 0/3] sched/fair: Fix cgroup::idle preemption Peter Zijlstra
  2026-11-05 16:55 ` [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle() Peter Zijlstra
@ 2026-11-05 16:55 ` Peter Zijlstra
  2026-11-05 19:20   ` Kayra Cizmeci
  2026-11-05 16:55 ` [RFC][PATCH 3/3] sched/eevdf: Fix sched idle task group preemption Peter Zijlstra
  2 siblings, 1 reply; 6+ messages in thread
From: Peter Zijlstra @ 2026-11-05 16:55 UTC (permalink / raw)
  To: mingo
  Cc: linux-kernel, peterz, juri.lelli, vincent.guittot,
	dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
	kprateek.nayak, joshdon, tj, jakub

Rather than keeping cfs_rq::idle as a copy of tg::idle, make it the sum of
all ancestor tg::idle. This makes it trivial to tell if the current cgroup
is marked idle somewhere along its ancestral path.

This makes se_id_idle() behave differently. Notably is will now be true
when there is a idle cgroup anywhere in the ancestor path. This
addresses an issue where preemption of cgroup::idle was overlooked when
flattening the runqueue.

Fixes: 85570f10a4c6 ("sched/eevdf: Move to a single runqueue")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 kernel/sched/core.c  |    2 -
 kernel/sched/debug.c |    1 
 kernel/sched/fair.c  |   73 ++++++++++++++++++++++++++++++++-------------------
 kernel/sched/sched.h |    4 +-
 4 files changed, 50 insertions(+), 30 deletions(-)

--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -8970,7 +8970,7 @@ void __init sched_init(void)
 		rq->nr_running = 0;
 		rq->calc_load_active = 0;
 		rq->calc_load_update = jiffies + LOAD_FREQ;
-		init_cfs_rq(&rq->cfs);
+		init_cfs_rq(&rq->cfs, NULL);
 		init_rt_rq(&rq->rt);
 		init_dl_rq(&rq->dl);
 #ifdef CONFIG_FAIR_GROUP_SCHED
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -1090,6 +1090,7 @@ void print_cfs_rq(struct seq_file *m, in
 	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_runnable", cfs_rq->h_nr_runnable);
 	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_queued", cfs_rq->h_nr_queued);
 	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_idle", cfs_rq->h_nr_idle);
+	SEQ_printf(m, "  .%-30s: %d\n", "idle", cfs_rq->idle);
 	SEQ_printf(m, "  .%-30s: %ld\n", "load", cfs_rq->load.weight);
 	SEQ_printf(m, "  .%-30s: %lu\n", "load_avg",
 			cfs_rq->avg.load_avg);
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15594,8 +15594,10 @@ static void set_next_task_fair(struct rq
 		hrtick_start_fair(rq, p);
 }
 
-void init_cfs_rq(struct cfs_rq *cfs_rq)
+void init_cfs_rq(struct cfs_rq *cfs_rq, struct cfs_rq *parent)
 {
+	if (parent)
+		cfs_rq->idle = parent->idle;
 	cfs_rq->tasks_timeline = RB_ROOT_CACHED;
 	cfs_rq->zero_vruntime = (u64)(-(1LL << 20));
 	raw_spin_lock_init(&cfs_rq->removed.lock);
@@ -15646,7 +15648,7 @@ int alloc_fair_sched_group(struct task_g
 			goto err;
 
 		se = tg_se(tg, i);
-		init_cfs_rq(cfs_rq);
+		init_cfs_rq(cfs_rq, tg_cfs_rq(parent, i));
 		init_tg_cfs_entry(tg, cfs_rq, se, i, tg_se(parent, i));
 		init_entity_runnable_average(se);
 	}
@@ -15780,6 +15782,43 @@ int sched_group_set_shares(struct task_g
 	return ret;
 }
 
+struct tg_set_idle_struct {
+	long idle;
+	int cpu;
+};
+
+static int tg_set_idle_up(struct task_group *tg, void *data)
+{
+	struct tg_set_idle_struct *tsi = data;
+	struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, tsi->cpu);
+	struct sched_entity *se = tg_se(tg, tsi->cpu);
+	int old_idle = grp_cfs_rq->idle;
+	int task_delta;
+
+	grp_cfs_rq->idle += tsi->idle;
+
+	if (!!old_idle == !!grp_cfs_rq->idle)
+		return 0;
+
+	task_delta = grp_cfs_rq->h_nr_queued - grp_cfs_rq->h_nr_idle;
+	if (!task_delta)
+		return 0;
+
+	task_delta *= tsi->idle;
+	for_each_sched_entity(se) {
+		struct cfs_rq *cfs_rq = cfs_rq_of(se);
+
+		if (!se->on_rq)
+			break;
+
+		cfs_rq->h_nr_idle += task_delta;
+
+		if (cfs_rq_is_idle(cfs_rq))
+			break;
+	}
+	return 0;
+}
+
 int sched_group_set_idle(struct task_group *tg, long idle)
 {
 	int i;
@@ -15798,34 +15837,14 @@ int sched_group_set_idle(struct task_gro
 
 	for_each_possible_cpu(i) {
 		struct rq *rq = cpu_rq(i);
-		struct sched_entity *se = tg_se(tg, i);
-		struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, i);
-		bool was_idle = cfs_rq_is_idle(grp_cfs_rq);
-		long idle_task_delta;
 
 		guard(rq_lock_irqsave)(rq);
 
-		grp_cfs_rq->idle = idle;
-		if (WARN_ON_ONCE(was_idle == cfs_rq_is_idle(grp_cfs_rq)))
-			continue;
-
-		idle_task_delta = grp_cfs_rq->h_nr_queued -
-				  grp_cfs_rq->h_nr_idle;
-		if (!cfs_rq_is_idle(grp_cfs_rq))
-			idle_task_delta *= -1;
-
-		for_each_sched_entity(se) {
-			struct cfs_rq *cfs_rq = cfs_rq_of(se);
-
-			if (!se->on_rq)
-				break;
-
-			cfs_rq->h_nr_idle += idle_task_delta;
-
-			/* Already accounted at parent level and above. */
-			if (cfs_rq_is_idle(cfs_rq))
-				break;
-		}
+		struct tg_set_idle_struct tsi = {
+			.idle = idle ? 1 : -1,
+			.cpu = i,
+		};
+		walk_tg_tree_from(tg, tg_nop, tg_set_idle_up, &tsi);
 	}
 
 	/* Idle groups have minimum weight. */
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -751,7 +751,7 @@ struct cfs_rq {
 	struct list_head	leaf_cfs_rq_list;
 	struct task_group	*tg;	/* Group that "owns" this runqueue */
 
-	/* Locally cached copy of our task_group's idle value */
+	/* Sum of all ancestor tg->idle. */
 	int			idle;
 
 # ifdef CONFIG_CFS_BANDWIDTH
@@ -3520,7 +3520,7 @@ print_numa_stats(struct seq_file *m, int
 		 unsigned long tpf, unsigned long gsf, unsigned long gpf);
 #endif /* CONFIG_NUMA_BALANCING */
 
-extern void init_cfs_rq(struct cfs_rq *cfs_rq);
+extern void init_cfs_rq(struct cfs_rq *cfs_rq, struct cfs_rq *parent);
 extern void init_rt_rq(struct rt_rq *rt_rq);
 extern void init_dl_rq(struct dl_rq *dl_rq);
 



^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC][PATCH 3/3] sched/eevdf: Fix sched idle task group preemption
  2026-11-05 16:55 [PATCH 0/3] sched/fair: Fix cgroup::idle preemption Peter Zijlstra
  2026-11-05 16:55 ` [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle() Peter Zijlstra
  2026-11-05 16:55 ` [PATCH 2/3] sched/fair: Make cfs_rq::idle the sum of ancestor tg::idle Peter Zijlstra
@ 2026-11-05 16:55 ` Peter Zijlstra
  2 siblings, 0 replies; 6+ messages in thread
From: Peter Zijlstra @ 2026-11-05 16:55 UTC (permalink / raw)
  To: mingo
  Cc: linux-kernel, peterz, juri.lelli, vincent.guittot,
	dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
	kprateek.nayak, joshdon, tj, jakub

From: Vincent Guittot <vincent.guittot@linaro.org>

Even with flat hierarchy which puts all runnable tasks in the same cfs rq,
we need to parse the hierarchy to check for sched_idle task group.

Fixes: 85570f10a4c6 ("sched/eevdf: Move to a single runqueue")
Reported-by: Jakub Okoński <jakub@okonski.org>
Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/58dc8e8a-7260-43ce-aebb-8d9c29a6afc9@okonski.org/
Link: https://patch.msgid.link/20261019182622.1584454-1-vincent.guittot@linaro.org
---

Changes since v1:
- fix prototype and cleanup unsued variable

 kernel/sched/fair.c |   87 ++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 74 insertions(+), 13 deletions(-)

--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -433,6 +433,38 @@ static inline struct sched_entity *paren
 	return se->parent;
 }
 
+static void
+find_matching_se(struct sched_entity **se, struct sched_entity **pse)
+{
+	int se_depth, pse_depth;
+
+	/*
+	 * preemption test can be made between sibling entities who are in the
+	 * same cfs_rq i.e who have a common parent. Walk up the hierarchy of
+	 * both tasks until we find their ancestors who are siblings of common
+	 * parent.
+	 */
+
+	/* First walk up until both entities are at same depth */
+	se_depth = (*se)->depth;
+	pse_depth = (*pse)->depth;
+
+	while (se_depth > pse_depth) {
+		se_depth--;
+		*se = parent_entity(*se);
+	}
+
+	while (pse_depth > se_depth) {
+		pse_depth--;
+		*pse = parent_entity(*pse);
+	}
+
+	while (!is_same_group(*se, *pse)) {
+		*se = parent_entity(*se);
+		*pse = parent_entity(*pse);
+	}
+}
+
 static int tg_is_idle(struct task_group *tg)
 {
 	return tg->idle > 0;
@@ -476,6 +508,11 @@ static inline struct sched_entity *paren
 	return NULL;
 }
 
+static inline void
+find_matching_se(struct sched_entity **se, struct sched_entity **pse)
+{
+}
+
 static inline int tg_is_idle(struct task_group *tg)
 {
 	return 0;
@@ -10130,7 +10167,40 @@ preempt_sync(struct rq *rq, int wake_fla
 }
 
 /*
- * Preempt the current task with a newly woken task if needed:
+ * Preempt an idle entity in favor of a non-idle entity (and don't preempt
+ * in the inverse case).
+ */
+static inline enum preempt_wakeup_action
+wakeup_preempt_sched_idle(struct sched_entity *cse, struct sched_entity *pse)
+{
+	int cse_is_idle, pse_is_idle;
+
+	cse_is_idle = se_is_idle(cse);
+	pse_is_idle = se_is_idle(pse);
+
+	if (cse_is_idle || pse_is_idle) {
+		find_matching_se(&cse, &pse);
+		WARN_ON_ONCE(!pse);
+
+		cse_is_idle = se_is_idle(cse);
+		pse_is_idle = se_is_idle(pse);
+
+		/*
+		 * Preempt an idle entity in favor of a non-idle entity (and
+		 * don't preempt in the inverse case).
+		 */
+		if (cse_is_idle && !pse_is_idle)
+			return PREEMPT_WAKEUP_RESCHED;
+
+		if (cse_is_idle != pse_is_idle)
+			return PREEMPT_WAKEUP_NONE;
+	}
+
+	return PREEMPT_WAKEUP_PICK;
+}
+
+/*
+ * Preempt the current task with a newly woken task if needed.
  */
 static void wakeup_preempt_fair(struct rq *rq, struct task_struct *p, int wake_flags)
 {
@@ -10138,7 +10208,6 @@ static void wakeup_preempt_fair(struct r
 	struct task_struct *donor = rq->donor;
 	struct sched_entity *nse, *se = &donor->se, *pse = &p->se;
 	struct cfs_rq *cfs_rq = &rq->cfs;
-	int cse_is_idle, pse_is_idle;
 
 	/*
 	 * XXX Getting preempted by higher class, try and find idle CPU?
@@ -10175,22 +10244,14 @@ static void wakeup_preempt_fair(struct r
 	if (!sched_feat(WAKEUP_PREEMPTION))
 		return;
 
-	WARN_ON_ONCE(!pse);
-
-	cse_is_idle = se_is_idle(se);
-	pse_is_idle = se_is_idle(pse);
+	preempt_action = wakeup_preempt_sched_idle(se, pse);
 
-	nse = se;
-	/*
-	 * Preempt an idle entity in favor of a non-idle entity (and don't preempt
-	 * in the inverse case).
-	 */
-	if (cse_is_idle && !pse_is_idle)
+	if (preempt_action == PREEMPT_WAKEUP_RESCHED)
 		goto preempt;
 
 	update_curr_fair(rq);
 
-	if (cse_is_idle != pse_is_idle)
+	if (preempt_action == PREEMPT_WAKEUP_NONE)
 		goto update;
 
 	/*



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle()
  2026-11-05 16:55 ` [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle() Peter Zijlstra
@ 2026-11-05 17:42   ` Kayra Cizmeci
  0 siblings, 0 replies; 6+ messages in thread
From: Kayra Cizmeci @ 2026-11-05 17:42 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, linux-kernel, juri.lelli, vincent.guittot,
	dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
	kprateek.nayak, joshdon, tj, jakub


Hi Peter,

On Thu, Nov 05, 2026 at 05:55:26PM +0100, Peter Zijlstra wrote:

> Cleanup the code by using guards; prepares for further changes in this function.
> 
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  kernel/sched/fair.c |   16 ++++------------
>  1 file changed, 4 insertions(+), 12 deletions(-)
> 
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15790,12 +15790,9 @@ int sched_group_set_idle(struct task_gro
>  	if (idle < 0 || idle > 1)
>  		return -EINVAL;
>  
> -	mutex_lock(&shares_mutex);
> -
> -	if (tg->idle == idle) {
> -		mutex_unlock(&shares_mutex);
> +	guard(mutex)(&shares_mutex);
> +	if (tg->idle == idle)
>  		return 0;
> -	}
>  
>  	tg->idle = idle;
>  
> @@ -15805,13 +15802,12 @@ int sched_group_set_idle(struct task_gro
>  		struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, i);
>  		bool was_idle = cfs_rq_is_idle(grp_cfs_rq);
>  		long idle_task_delta;
> -		struct rq_flags rf;
>  
> -		rq_lock_irqsave(rq, &rf);
> +		guard(rq_lock_irqsave)(rq);
>  
>  		grp_cfs_rq->idle = idle;
>  		if (WARN_ON_ONCE(was_idle == cfs_rq_is_idle(grp_cfs_rq)))
> -			goto next_cpu;
> +			continue;
>  
>  		idle_task_delta = grp_cfs_rq->h_nr_queued -
>  				  grp_cfs_rq->h_nr_idle;
> @@ -15830,9 +15826,6 @@ int sched_group_set_idle(struct task_gro
>  			if (cfs_rq_is_idle(cfs_rq))
>  				break;
>  		}
> -
> -next_cpu:
> -		rq_unlock_irqrestore(rq, &rf);
>  	}
>  
>  	/* Idle groups have minimum weight. */
> @@ -15841,7 +15834,6 @@ int sched_group_set_idle(struct task_gro
>  	else
>  		__sched_group_set_shares(tg, NICE_0_LOAD);
>  
> -	mutex_unlock(&shares_mutex);
>  	return 0;
>  }
>  
> 
> 

LGTM.

Reviewed-by: Kayra Cizmeci <kayracizmeci@gmail.com>


Cheers,

Kayra

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/3] sched/fair: Make cfs_rq::idle the sum of ancestor tg::idle
  2026-11-05 16:55 ` [PATCH 2/3] sched/fair: Make cfs_rq::idle the sum of ancestor tg::idle Peter Zijlstra
@ 2026-11-05 19:20   ` Kayra Cizmeci
  0 siblings, 0 replies; 6+ messages in thread
From: Kayra Cizmeci @ 2026-11-05 19:20 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, linux-kernel, juri.lelli, vincent.guittot,
	dietmar.eggemann, rostedt, bsegall, mgorman, vschneid,
	kprateek.nayak, joshdon, tj, jakub

On Thu, Nov 05, 2026 at 05:55:27PM +0100, Peter Zijlstra wrote:
> Rather than keeping cfs_rq::idle as a copy of tg::idle, make it the sum of
> all ancestor tg::idle. This makes it trivial to tell if the current cgroup
> is marked idle somewhere along its ancestral path.
> 
> This makes se_id_idle() behave differently. Notably is will now be true
> when there is a idle cgroup anywhere in the ancestor path. This
> addresses an issue where preemption of cgroup::idle was overlooked when
> flattening the runqueue.

How? The diff doesn't touches se_is_idle(). se_is_idle() just looks to
the policy for tasks:

static int se_is_idle(struct sched_entity *se)
{
	if (entity_is_task(se))
		return task_has_idle_policy(task_of(se));
	return cfs_rq_is_idle(group_cfs_rq(se));
}

And the issue in the preemption path is in the entity_is_task() path.

	cse_is_idle = se_is_idle(se);
	pse_is_idle = se_is_idle(pse);

	nse = se;
	/*
	 * Preempt an idle entity in favor of a non-idle entity (and don't preempt
	 * in the inverse case).
	 */
	if (cse_is_idle && !pse_is_idle)
		goto preempt;

	update_curr_fair(rq);

	if (cse_is_idle != pse_is_idle)
		goto update;



Or am I getting something wrong?


> 
> Fixes: 85570f10a4c6 ("sched/eevdf: Move to a single runqueue")
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  kernel/sched/core.c  |    2 -
>  kernel/sched/debug.c |    1 
>  kernel/sched/fair.c  |   73 ++++++++++++++++++++++++++++++++-------------------
>  kernel/sched/sched.h |    4 +-
>  4 files changed, 50 insertions(+), 30 deletions(-)
> 
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -8970,7 +8970,7 @@ void __init sched_init(void)
>  		rq->nr_running = 0;
>  		rq->calc_load_active = 0;
>  		rq->calc_load_update = jiffies + LOAD_FREQ;
> -		init_cfs_rq(&rq->cfs);
> +		init_cfs_rq(&rq->cfs, NULL);
>  		init_rt_rq(&rq->rt);
>  		init_dl_rq(&rq->dl);
>  #ifdef CONFIG_FAIR_GROUP_SCHED
> --- a/kernel/sched/debug.c
> +++ b/kernel/sched/debug.c
> @@ -1090,6 +1090,7 @@ void print_cfs_rq(struct seq_file *m, in
>  	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_runnable", cfs_rq->h_nr_runnable);
>  	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_queued", cfs_rq->h_nr_queued);
>  	SEQ_printf(m, "  .%-30s: %d\n", "h_nr_idle", cfs_rq->h_nr_idle);
> +	SEQ_printf(m, "  .%-30s: %d\n", "idle", cfs_rq->idle);
>  	SEQ_printf(m, "  .%-30s: %ld\n", "load", cfs_rq->load.weight);
>  	SEQ_printf(m, "  .%-30s: %lu\n", "load_avg",
>  			cfs_rq->avg.load_avg);
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15594,8 +15594,10 @@ static void set_next_task_fair(struct rq
>  		hrtick_start_fair(rq, p);
>  }
>  
> -void init_cfs_rq(struct cfs_rq *cfs_rq)
> +void init_cfs_rq(struct cfs_rq *cfs_rq, struct cfs_rq *parent)
>  {
> +	if (parent)
> +		cfs_rq->idle = parent->idle;

AFAICT this creates a race:

Let's say we are creating a group under GA named GB.

We first call sched_create_group(), that calls alloc_fair_sched_group()
and that calls init_cfs_rq(). If the parent groups idle state changes
before GB gets into place in something like this:


* GA is not idle GB idle = 0

* GA gets idle in the window

* GB setells in

* GA then gets idle = 0 again


Now this happens:

GB idle = -1 and GA idle = 0.

So below, in tg_set_idle_up() the code things something changed and
h_nr_idle starts to corrupt.


Or am I missing something?


>  	cfs_rq->tasks_timeline = RB_ROOT_CACHED;
>  	cfs_rq->zero_vruntime = (u64)(-(1LL << 20));
>  	raw_spin_lock_init(&cfs_rq->removed.lock);
> @@ -15646,7 +15648,7 @@ int alloc_fair_sched_group(struct task_g
>  			goto err;
>  
>  		se = tg_se(tg, i);
> -		init_cfs_rq(cfs_rq);
> +		init_cfs_rq(cfs_rq, tg_cfs_rq(parent, i));
>  		init_tg_cfs_entry(tg, cfs_rq, se, i, tg_se(parent, i));
>  		init_entity_runnable_average(se);
>  	}
> @@ -15780,6 +15782,43 @@ int sched_group_set_shares(struct task_g
>  	return ret;
>  }
>  
> +struct tg_set_idle_struct {
> +	long idle;
> +	int cpu;
> +};
> +
> +static int tg_set_idle_up(struct task_group *tg, void *data)
> +{
> +	struct tg_set_idle_struct *tsi = data;
> +	struct cfs_rq *grp_cfs_rq = tg_cfs_rq(tg, tsi->cpu);
> +	struct sched_entity *se = tg_se(tg, tsi->cpu);
> +	int old_idle = grp_cfs_rq->idle;
> +	int task_delta;
> +
> +	grp_cfs_rq->idle += tsi->idle;
> +
> +	if (!!old_idle == !!grp_cfs_rq->idle)
> +		return 0;
> +
> +	task_delta = grp_cfs_rq->h_nr_queued - grp_cfs_rq->h_nr_idle;
> +	if (!task_delta)
> +		return 0;
> +
> +	task_delta *= tsi->idle;
> +	for_each_sched_entity(se) {
> +		struct cfs_rq *cfs_rq = cfs_rq_of(se);
> +
> +		if (!se->on_rq)
> +			break;
> +
> +		cfs_rq->h_nr_idle += task_delta;
> +
> +		if (cfs_rq_is_idle(cfs_rq))
> +			break;
> +	}
> +	return 0;
> +}


Cheers,


Kayra :-)

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-05 19:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-05 16:55 [PATCH 0/3] sched/fair: Fix cgroup::idle preemption Peter Zijlstra
2026-11-05 16:55 ` [PATCH 1/3] sched/fair: Use guard in sched_group_set_idle() Peter Zijlstra
2026-11-05 17:42   ` Kayra Cizmeci
2026-11-05 16:55 ` [PATCH 2/3] sched/fair: Make cfs_rq::idle the sum of ancestor tg::idle Peter Zijlstra
2026-11-05 19:20   ` Kayra Cizmeci
2026-11-05 16:55 ` [RFC][PATCH 3/3] sched/eevdf: Fix sched idle task group preemption Peter Zijlstra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®