mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/4] sched/fair: Rework tash_h_load()
@ 2026-08-28  7:40 Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 1/4] sched: Rename/clarify sched_class::task_tick(.queued) argument Peter Zijlstra
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-28  7:40 UTC (permalink / raw)
  To: mingo
  Cc: peterz, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel

Hi!

So task_h_load() has been known buggered for a while [1], and while looking at
[2] I ran into it again.

These few patches rework the thing and should hopefully result in less broken
behaviour.

Lightly tested...


[1] email that unfortunately ended up private with Vincent
[2] https://lore.kernel.org/all/aoxah90s0bQ4tcUW@three-body/


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

* [PATCH 1/4] sched: Rename/clarify sched_class::task_tick(.queued) argument
  2026-08-28  7:40 [PATCH 0/4] sched/fair: Rework tash_h_load() Peter Zijlstra
@ 2026-08-28  7:41 ` Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 2/4] sched/fair: Fold cfs_rq_of(se) into for_each_sched_entity() Peter Zijlstra
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-28  7:41 UTC (permalink / raw)
  To: mingo
  Cc: peterz, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel

For some reason the sched_class::task_tick() argument that indicates it being
an hrtick, is called @queued. I'm sure naming is hard and all, but lets fix
this.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 kernel/sched/deadline.c |    4 ++--
 kernel/sched/ext/ext.c  |    2 +-
 kernel/sched/fair.c     |   12 ++++++------
 kernel/sched/idle.c     |    2 +-
 kernel/sched/rt.c       |    2 +-
 kernel/sched/sched.h    |    2 +-
 6 files changed, 12 insertions(+), 12 deletions(-)

--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -2876,7 +2876,7 @@ static void put_prev_task_dl(struct rq *
  * and everything must be accessed through the @rq and @curr passed in
  * parameters.
  */
-static void task_tick_dl(struct rq *rq, struct task_struct *p, int queued)
+static void task_tick_dl(struct rq *rq, struct task_struct *p, int hrtick)
 {
 	update_curr_dl(rq);
 
@@ -2886,7 +2886,7 @@ static void task_tick_dl(struct rq *rq,
 	 * not being the leftmost task anymore. In that case NEED_RESCHED will
 	 * be set and schedule() will start a new hrtick for the next task.
 	 */
-	if (hrtick_enabled_dl(rq) && queued && p->dl.runtime > 0 &&
+	if (hrtick_enabled_dl(rq) && hrtick && p->dl.runtime > 0 &&
 	    is_leftmost(&p->dl, &rq->dl))
 		start_hrtick_dl(rq, &p->dl);
 }
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -3787,7 +3787,7 @@ void scx_tick(struct rq *rq)
 	update_other_load_avgs(rq);
 }
 
-static void task_tick_scx(struct rq *rq, struct task_struct *curr, int queued)
+static void task_tick_scx(struct rq *rq, struct task_struct *curr, int hrtick)
 {
 	struct scx_sched *sch = scx_task_sched(curr);
 
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -6542,7 +6542,7 @@ static void put_prev_entity(struct cfs_r
 }
 
 static void
-entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
+entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int hrtick)
 {
 	/*
 	 * Update run-time statistics of the 'current'.
@@ -6557,10 +6557,10 @@ entity_tick(struct cfs_rq *cfs_rq, struc
 
 #ifdef CONFIG_SCHED_HRTICK
 	/*
-	 * queued ticks are scheduled to match the slice, so don't bother
+	 * hrticks are scheduled to match the slice, so don't bother
 	 * validating it and just reschedule.
 	 */
-	if (queued) {
+	if (hrtick) {
 		resched_curr(rq_of(cfs_rq));
 		return;
 	}
@@ -15020,7 +15020,7 @@ static inline void task_tick_core(struct
  * and everything must be accessed through the @rq and @curr passed in
  * parameters.
  */
-static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
+static void task_tick_fair(struct rq *rq, struct task_struct *curr, int hrtick)
 {
 	struct sched_entity *se = &curr->se;
 
@@ -15030,7 +15030,7 @@ static void task_tick_fair(struct rq *rq
 
 		for_each_sched_entity(se) {
 			cfs_rq = cfs_rq_of(se);
-			entity_tick(cfs_rq, se, queued);
+			entity_tick(cfs_rq, se, hrtick);
 
 			weight = __calc_prop_weight(cfs_rq, se, weight);
 		}
@@ -15039,7 +15039,7 @@ static void task_tick_fair(struct rq *rq
 		reweight_eevdf(cfs_rq, se, weight, se->on_rq);
 	}
 
-	if (queued)
+	if (hrtick)
 		return;
 
 	if (static_branch_unlikely(&sched_numa_balancing))
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -535,7 +535,7 @@ dequeue_task_idle(struct rq *rq, struct
  * and everything must be accessed through the @rq and @curr passed in
  * parameters.
  */
-static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
+static void task_tick_idle(struct rq *rq, struct task_struct *curr, int hrtick)
 {
 	update_curr_idle(rq);
 }
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2538,7 +2538,7 @@ static inline void watchdog(struct rq *r
  * and everything must be accessed through the @rq and @curr passed in
  * parameters.
  */
-static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)
+static void task_tick_rt(struct rq *rq, struct task_struct *p, int hrtick)
 {
 	struct sched_rt_entity *rt_se = &p->rt;
 
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2718,7 +2718,7 @@ struct sched_class {
 	 * sched_tick: rq->lock
 	 * sched_tick_remote: rq->lock
 	 */
-	void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);
+	void (*task_tick)(struct rq *rq, struct task_struct *p, int hrtick);
 	/*
 	 * sched_cgroup_fork: p->pi_lock
 	 */



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

* [PATCH 2/4] sched/fair: Fold cfs_rq_of(se) into for_each_sched_entity()
  2026-08-28  7:40 [PATCH 0/4] sched/fair: Rework tash_h_load() Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 1/4] sched: Rename/clarify sched_class::task_tick(.queued) argument Peter Zijlstra
@ 2026-08-28  7:41 ` Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 3/4] sched/fair: Extend for_each_sched_entity() with a back-link Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 4/4] sched/fair: Rework/fix task_h_load() Peter Zijlstra
  3 siblings, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-28  7:41 UTC (permalink / raw)
  To: mingo
  Cc: peterz, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel

Pretty much every for_each_sched_entity() loop does: cfs_rq = cfs_rq_of(se) as
the very first thing. Fold it into the for_each_sched_entity() macro.

This paves the way to have the macro track a backlink transparantly.

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

--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -317,8 +317,8 @@ const struct sched_class fair_sched_clas
 #ifdef CONFIG_FAIR_GROUP_SCHED
 
 /* Walk up scheduling entities hierarchy */
-#define for_each_sched_entity(se) \
-		for (; se; se = se->parent)
+#define for_each_sched_entity(se, cfs_rq)				\
+	for (; (se) && ((cfs_rq) = cfs_rq_of(se)); (se) = (se)->parent)
 
 static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq)
 {
@@ -452,8 +452,8 @@ static int se_is_idle(struct sched_entit
 
 #else /* !CONFIG_FAIR_GROUP_SCHED: */
 
-#define for_each_sched_entity(se) \
-		for (; se; se = NULL)
+#define for_each_sched_entity(se, cfs_rq) \
+	for (; (se) && ((cfs_rq) = cfs_of_of(se)); (se) = NULL)
 
 static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq)
 {
@@ -2075,9 +2075,10 @@ static void update_curr(struct cfs_rq *c
 static void update_curr_fair(struct rq *rq)
 {
 	struct sched_entity *se = &rq->donor->se;
+	struct cfs_rq *cfs_rq;
 
-	for_each_sched_entity(se)
-		update_curr(cfs_rq_of(se));
+	for_each_sched_entity(se, cfs_rq)
+		update_curr(cfs_rq);
 }
 
 static inline void
@@ -4800,18 +4801,19 @@ static void reweight_task_fair(struct rq
 {
 	struct sched_entity *se = &p->se;
 	unsigned long weight = NICE_0_LOAD;
+	struct cfs_rq *cfs_rq = cfs_rq_of(se);
 
 	if (se->on_rq)
 		update_curr_fair(rq);
 
-	reweight_entity(cfs_rq_of(se), se, lw->weight);
+	reweight_entity(cfs_rq, se, lw->weight);
 	se->load.inv_weight = lw->inv_weight;
 
 	if (!se->on_rq)
 		return;
 
-	for_each_sched_entity(se)
-		weight = __calc_prop_weight(cfs_rq_of(se), se, weight);
+	for_each_sched_entity(se, cfs_rq)
+		weight = __calc_prop_weight(cfs_rq, se, weight);
 
 	reweight_eevdf(&rq->cfs, &p->se, weight, p->se.on_rq);
 }
@@ -6382,6 +6384,8 @@ static __always_inline void return_cfs_r
 
 static void set_delayed(struct sched_entity *se)
 {
+	struct cfs_rq *cfs_rq;
+
 	se->sched_delayed = 1;
 
 	/*
@@ -6392,15 +6396,14 @@ static void set_delayed(struct sched_ent
 	if (!entity_is_task(se))
 		return;
 
-	for_each_sched_entity(se) {
-		struct cfs_rq *cfs_rq = cfs_rq_of(se);
-
+	for_each_sched_entity(se, cfs_rq)
 		cfs_rq->h_nr_runnable--;
-	}
 }
 
 static void clear_delayed(struct sched_entity *se)
 {
+	struct cfs_rq *cfs_rq;
+
 	se->sched_delayed = 0;
 
 	/*
@@ -6412,11 +6415,8 @@ static void clear_delayed(struct sched_e
 	if (!entity_is_task(se))
 		return;
 
-	for_each_sched_entity(se) {
-		struct cfs_rq *cfs_rq = cfs_rq_of(se);
-
+	for_each_sched_entity(se, cfs_rq)
 		cfs_rq->h_nr_runnable++;
-	}
 }
 
 static void
@@ -7078,14 +7078,16 @@ void unthrottle_cfs_rq(struct cfs_rq *cf
 	walk_tg_tree_from(cfs_rq->tg, tg_nop, tg_unthrottle_up, (void *)rq);
 
 	if (!cfs_rq->load.weight) {
+		struct cfs_rq *cfs_rq_se;
+
 		if (!cfs_rq->on_list)
 			return;
 		/*
 		 * Nothing to run but something to decay (on_list)?
 		 * Complete the branch.
 		 */
-		for_each_sched_entity(se) {
-			if (list_add_leaf_cfs_rq(cfs_rq_of(se)))
+		for_each_sched_entity(se, cfs_rq_se) {
+			if (list_add_leaf_cfs_rq(cfs_rq_se))
 				break;
 		}
 	}
@@ -7931,13 +7933,12 @@ static unsigned long enqueue_hierarchy(s
 	struct sched_entity *se = &p->se;
 	int h_nr_idle = task_has_idle_policy(p);
 	int h_nr_runnable = 1;
+	struct cfs_rq *cfs_rq;
 
 	if (task_new && se->sched_delayed)
 		h_nr_runnable = 0;
 
-	for_each_sched_entity(se) {
-		struct cfs_rq *cfs_rq = cfs_rq_of(se);
-
+	for_each_sched_entity(se, cfs_rq) {
 		update_curr(cfs_rq);
 
 		if (!se->on_rq) {
@@ -8066,16 +8067,15 @@ static void dequeue_hierarchy(struct tas
 	bool task_sleep = flags & DEQUEUE_SLEEP;
 	bool task_delayed = flags & DEQUEUE_DELAYED;
 	bool task_throttled = flags & DEQUEUE_THROTTLE;
-	int h_nr_runnable = 0;
 	int h_nr_idle = task_has_idle_policy(p);
+	int h_nr_runnable = 0;
+	struct cfs_rq *cfs_rq;
 	bool dequeue = true;
 
 	if (task_sleep || task_delayed || !se->sched_delayed)
 		h_nr_runnable = 1;
 
-	for_each_sched_entity(se) {
-		struct cfs_rq *cfs_rq = cfs_rq_of(se);
-
+	for_each_sched_entity(se, cfs_rq) {
 		update_curr(cfs_rq);
 
 		if (dequeue) {
@@ -11301,8 +11301,7 @@ static void update_cfs_rq_h_load(struct
 		return;
 
 	WRITE_ONCE(cfs_rq->h_load_next, NULL);
-	for_each_sched_entity(se) {
-		cfs_rq = cfs_rq_of(se);
+	for_each_sched_entity(se, cfs_rq) {
 		WRITE_ONCE(cfs_rq->h_load_next, se);
 		if (cfs_rq->last_h_load_update == now)
 			break;
@@ -14948,9 +14947,9 @@ static inline void task_tick_core(struct
 static void se_fi_update(const struct sched_entity *se, unsigned int fi_seq,
 			 bool forceidle)
 {
-	for_each_sched_entity(se) {
-		struct cfs_rq *cfs_rq = cfs_rq_of(se);
+	struct cfs_rq *cfs_rq;
 
+	for_each_sched_entity(se, cfs_rq) {
 		if (forceidle) {
 			if (cfs_rq->forceidle_seq == fi_seq)
 				break;
@@ -15028,10 +15027,8 @@ static void task_tick_fair(struct rq *rq
 		unsigned long weight = NICE_0_LOAD;
 		struct cfs_rq *cfs_rq;
 
-		for_each_sched_entity(se) {
-			cfs_rq = cfs_rq_of(se);
+		for_each_sched_entity(se, cfs_rq) {
 			entity_tick(cfs_rq, se, hrtick);
-
 			weight = __calc_prop_weight(cfs_rq, se, weight);
 		}
 
@@ -15113,9 +15110,7 @@ static void propagate_entity_cfs_rq(stru
 	/* Start to propagate at parent */
 	se = se->parent;
 
-	for_each_sched_entity(se) {
-		cfs_rq = cfs_rq_of(se);
-
+	for_each_sched_entity(se, cfs_rq) {
 		update_load_avg(cfs_rq, se, UPDATE_TG);
 
 		if (!cfs_rq_pelt_clock_throttled(cfs_rq))
@@ -15218,9 +15213,7 @@ static void set_next_task_fair(struct rq
 	if (on_rq)
 		__dequeue_entity(cfs_rq, se);
 
-	for_each_sched_entity(se) {
-		cfs_rq = cfs_rq_of(se);
-
+	for_each_sched_entity(se, cfs_rq) {
 		if (!IS_ENABLED(CONFIG_FAIR_GROUP_SCHED) ||
 		    !first || !cfs_rq->h_curr)
 			set_next_entity(cfs_rq, se);
@@ -15420,13 +15413,14 @@ static int __sched_group_set_shares(stru
 	for_each_possible_cpu(i) {
 		struct rq *rq = cpu_rq(i);
 		struct sched_entity *se = tg_se(tg, i);
+		struct cfs_rq *cfs_rq;
 		struct rq_flags rf;
 
 		/* Propagate contribution to hierarchy */
 		rq_lock_irqsave(rq, &rf);
 		update_rq_clock(rq);
-		for_each_sched_entity(se) {
-			update_load_avg(cfs_rq_of(se), se, UPDATE_TG);
+		for_each_sched_entity(se, cfs_rq) {
+			update_load_avg(cfs_rq, se, UPDATE_TG);
 			update_cfs_group(se);
 		}
 		rq_unlock_irqrestore(rq, &rf);
@@ -15473,6 +15467,7 @@ int sched_group_set_idle(struct task_gro
 		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);
+		struct cfs_rq *cfs_rq;
 		long idle_task_delta;
 		struct rq_flags rf;
 
@@ -15487,9 +15482,7 @@ int sched_group_set_idle(struct task_gro
 		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);
-
+		for_each_sched_entity(se, cfs_rq) {
 			if (!se->on_rq)
 				break;
 



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

* [PATCH 3/4] sched/fair: Extend for_each_sched_entity() with a back-link
  2026-08-28  7:40 [PATCH 0/4] sched/fair: Rework tash_h_load() Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 1/4] sched: Rename/clarify sched_class::task_tick(.queued) argument Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 2/4] sched/fair: Fold cfs_rq_of(se) into for_each_sched_entity() Peter Zijlstra
@ 2026-08-28  7:41 ` Peter Zijlstra
  2026-08-28  7:41 ` [PATCH 4/4] sched/fair: Rework/fix task_h_load() Peter Zijlstra
  3 siblings, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-28  7:41 UTC (permalink / raw)
  To: mingo
  Cc: peterz, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel

Leave a trail of bread crumbs, such that we can find out way back down the
hierarchy. No actual users yet, but split out because its a bit tricky.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 kernel/sched/fair.c  |    9 +++++++--
 kernel/sched/sched.h |    1 +
 2 files changed, 8 insertions(+), 2 deletions(-)

--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -317,8 +317,13 @@ const struct sched_class fair_sched_clas
 #ifdef CONFIG_FAIR_GROUP_SCHED
 
 /* Walk up scheduling entities hierarchy */
-#define for_each_sched_entity(se, cfs_rq)				\
-	for (; (se) && ((cfs_rq) = cfs_rq_of(se)); (se) = (se)->parent)
+#define for_each_sched_entity(se, cfs_rq)					\
+	for (struct sched_entity *_BL = NULL;					\
+	     (se) && ((cfs_rq) = cfs_rq_of(se), (cfs_rq)->backlink = _BL, true);\
+	     (se) = (se)->parent, _BL = (se))
+
+#define for_each_sched_entity_bl(se, cfs_rq) \
+	for (; ((se) = (cfs_rq)->backlink); (cfs_rq) = group_cfs_rq(se))
 
 static inline bool list_add_leaf_cfs_rq(struct cfs_rq *cfs_rq)
 {
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -726,6 +726,7 @@ struct cfs_rq {
 	unsigned long		tg_runnable_avg_contrib;
 	long			propagate;
 	long			prop_runnable_sum;
+	struct sched_entity	*backlink;
 
 	/*
 	 *   h_load = weight * f(tg)



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

* [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-08-28  7:40 [PATCH 0/4] sched/fair: Rework tash_h_load() Peter Zijlstra
                   ` (2 preceding siblings ...)
  2026-08-28  7:41 ` [PATCH 3/4] sched/fair: Extend for_each_sched_entity() with a back-link Peter Zijlstra
@ 2026-08-28  7:41 ` Peter Zijlstra
  2026-08-31 10:10   ` Vincent Guittot
  2026-09-02  5:26   ` Chen Yu
  3 siblings, 2 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-28  7:41 UTC (permalink / raw)
  To: mingo
  Cc: peterz, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel

There are a number of issues with task_h_load():

 - its hierarchy traversal is racy to the point of being broken; where
   originally it was meant to be used under rq->lock, but lacking an assertion
   for that fact, its use spread and violated this. The result is that the
   back-link state is prone to races.

 - it is rate-limited on jiffies, which is HZ, not the underlying PELT decay.

 - since its update is tied to task_h_load() usage, the cfs_rq->h_load numbers are
   not often 'up-to-date', rendering their output in sched/debug near useless.

Rework the whole thing to keep a more up-to-date and less broken cfs_rq->h_load
number.

Move the back-link tracking into for_each_sched_entity(), such that any such
loop sets up a path back. Use this to (optionally) re-compute cfs_rq->h_load on
enqueue, dequeue, set_next and tick, all sites that hold rq->lock.

This ensures that 'active' cgroups have reasonably up-to-date cfs_rq->h_load.
Additionally, have __update_blocked_fair() update cfs_rq->h_load for all
cgroups.

Finally, replace the jiffy rate-limit with one that is tied to the PELT decay.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 kernel/sched/fair.c  |  108 ++++++++++++++++++++++++++++++++-------------------
 kernel/sched/pelt.h  |    7 +++
 kernel/sched/sched.h |    1 
 3 files changed, 76 insertions(+), 40 deletions(-)

--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -5541,6 +5541,44 @@ static inline bool skip_blocked_update(s
 	return true;
 }
 
+static inline void __update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
+					  struct sched_entity *se,
+					  struct cfs_rq *p_cfs_rq)
+{
+	unsigned long load = cfs_rq->avg.load_avg;
+
+	if (cfs_rq != &cfs_rq->rq->cfs) {
+		if (!se)
+			se = &container_of(cfs_rq, struct cfs_tg_state, cfs_rq)->se;
+		if (!p_cfs_rq)
+			p_cfs_rq = cfs_rq_of(se);
+
+		load = p_cfs_rq->h_load;
+		load = div64_ul(load * se->avg.load_avg,
+				p_cfs_rq->avg.load_avg + 1);
+	}
+
+	WRITE_ONCE(cfs_rq->h_load, load);
+}
+
+static inline bool update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
+					struct sched_entity *se,
+					struct cfs_rq *p_cfs_rq)
+{
+	/*
+	 * Mask out the segment bits, if the remaining bits match, then there
+	 * hasn't been a decay since the last time.
+	 */
+	if ((cfs_rq->last_h_load_update & ~PELT_SEGMENT_MASK) ==
+	    (cfs_rq->avg.last_update_time & ~PELT_SEGMENT_MASK))
+		return false;
+
+	__update_cfs_rq_h_load(cfs_rq, se, p_cfs_rq);
+
+	cfs_rq->last_h_load_update = cfs_rq->avg.last_update_time;
+	return true;
+}
+
 #else /* !CONFIG_FAIR_GROUP_SCHED: */
 
 static inline void update_tg_load_avg(struct cfs_rq *cfs_rq) {}
@@ -5554,6 +5592,10 @@ static inline int propagate_entity_load_
 
 static inline void add_tg_cfs_propagate(struct cfs_rq *cfs_rq, long runnable_sum) {}
 
+static inline bool update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
+					struct sched_entity *se,
+					struct cfs_rq *p_cfs_rq) { return false; }
+
 #endif /* !CONFIG_FAIR_GROUP_SCHED */
 
 #ifdef CONFIG_NO_HZ_COMMON
@@ -7966,6 +8008,9 @@ static unsigned long enqueue_hierarchy(s
 		flags = ENQUEUE_WAKEUP;
 	}
 
+	for_each_sched_entity_bl(se, cfs_rq)
+		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
+
 	return weight;
 }
 
@@ -8107,6 +8152,9 @@ static void dequeue_hierarchy(struct tas
 		flags |= DEQUEUE_SLEEP;
 		flags &= ~(DEQUEUE_DELAYED | DEQUEUE_SPECIAL);
 	}
+
+	for_each_sched_entity_bl(se, cfs_rq)
+		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
 }
 
 /*
@@ -11288,51 +11336,23 @@ static bool __update_blocked_fair(struct
 			*done = false;
 	}
 
-	return decayed;
-}
-
-/*
- * Compute the hierarchical load factor for cfs_rq and all its ascendants.
- * This needs to be done in a top-down fashion because the load of a child
- * group is a fraction of its parents load.
- */
-static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq)
-{
-	struct sched_entity *se = cfs_rq_se(cfs_rq);
-	unsigned long now = jiffies;
-	unsigned long load;
-
-	if (cfs_rq->last_h_load_update == now)
-		return;
-
-	WRITE_ONCE(cfs_rq->h_load_next, NULL);
-	for_each_sched_entity(se, cfs_rq) {
-		WRITE_ONCE(cfs_rq->h_load_next, se);
-		if (cfs_rq->last_h_load_update == now)
-			break;
-	}
-
-	if (!se) {
-		cfs_rq->h_load = cfs_rq_load_avg(cfs_rq);
-		cfs_rq->last_h_load_update = now;
-	}
+	/*
+	 * The above (forward) leaf_cfs_rq_list traversal will have done
+	 * update_cfs_rq_load_avg() in a bottom-up fashion. Now iterate the
+	 * list backwards, such that we're ensured to have visited every
+	 * parent of the current group to update h_load in a top-down fashion.
+	 */
+	list_for_each_entry_reverse(cfs_rq, &rq->leaf_cfs_rq_list, leaf_cfs_rq_list)
+		update_cfs_rq_h_load(cfs_rq, NULL, NULL);
 
-	while ((se = READ_ONCE(cfs_rq->h_load_next)) != NULL) {
-		load = cfs_rq->h_load;
-		load = div64_ul(load * se->avg.load_avg,
-				cfs_rq_load_avg(cfs_rq) + 1);
-		cfs_rq = group_cfs_rq(se);
-		cfs_rq->h_load = load;
-		cfs_rq->last_h_load_update = now;
-	}
+	return decayed;
 }
 
 static unsigned long task_h_load(struct task_struct *p)
 {
 	struct cfs_rq *cfs_rq = task_cfs_rq(p);
 
-	update_cfs_rq_h_load(cfs_rq);
-	return div64_ul(p->se.avg.load_avg * cfs_rq->h_load,
+	return div64_ul(p->se.avg.load_avg * READ_ONCE(cfs_rq->h_load),
 			cfs_rq_load_avg(cfs_rq) + 1);
 }
 #else /* !CONFIG_FAIR_GROUP_SCHED: */
@@ -14949,7 +14969,7 @@ static inline void task_tick_core(struct
 /*
  * se_fi_update - Update the cfs_rq->zero_vruntime_fi in a CFS hierarchy if needed.
  */
-static void se_fi_update(const struct sched_entity *se, unsigned int fi_seq,
+static void se_fi_update(struct sched_entity *se, unsigned int fi_seq,
 			 bool forceidle)
 {
 	struct cfs_rq *cfs_rq;
@@ -15039,6 +15059,11 @@ static void task_tick_fair(struct rq *rq
 
 		se = &curr->se;
 		reweight_eevdf(cfs_rq, se, weight, se->on_rq);
+
+		if (!hrtick) {
+			for_each_sched_entity_bl(se, cfs_rq)
+				update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
+		}
 	}
 
 	if (hrtick)
@@ -15230,6 +15255,9 @@ static void set_next_task_fair(struct rq
 			weight = __calc_prop_weight(cfs_rq, se, weight);
 	}
 
+	for_each_sched_entity_bl(se, cfs_rq)
+		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
+
 	if (throttled)
 		task_throttle_setup_work(p);
 
@@ -15428,6 +15456,8 @@ static int __sched_group_set_shares(stru
 			update_load_avg(cfs_rq, se, UPDATE_TG);
 			update_cfs_group(se);
 		}
+		for_each_sched_entity_bl(se, cfs_rq)
+			update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
 		rq_unlock_irqrestore(rq, &rf);
 	}
 
--- a/kernel/sched/pelt.h
+++ b/kernel/sched/pelt.h
@@ -5,6 +5,13 @@
 
 #include "sched-pelt.h"
 
+/*
+ * Pelt uses apprixmate 'us' as ns/1024; and then uses time segments of 1024
+ * 'us'. As a result each segment is in fact '1<<20' ns.
+ */
+#define PELT_SEGMENT_NS		(1<<20)
+#define PELT_SEGMENT_MASK	(PELT_SEGMENT_NS-1)
+
 int __update_load_avg_blocked_se(u64 now, struct sched_entity *se);
 int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se);
 int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq);
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -736,7 +736,6 @@ struct cfs_rq {
 	 */
 	unsigned long		h_load;
 	u64			last_h_load_update;
-	struct sched_entity	*h_load_next;
 
 	struct rq		*rq;	/* CPU runqueue to which this cfs_rq is attached */
 



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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-08-28  7:41 ` [PATCH 4/4] sched/fair: Rework/fix task_h_load() Peter Zijlstra
@ 2026-08-31 10:10   ` Vincent Guittot
  2026-08-31 10:37     ` Peter Zijlstra
  2026-09-02  5:26   ` Chen Yu
  1 sibling, 1 reply; 15+ messages in thread
From: Vincent Guittot @ 2026-08-31 10:10 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman,
	vschneid, kprateek.nayak, tj, linux-kernel

On Fri, 28 Aug 2026 at 09:56, Peter Zijlstra <peterz@infradead.org> wrote:
>
> There are a number of issues with task_h_load():
>
>  - its hierarchy traversal is racy to the point of being broken; where
>    originally it was meant to be used under rq->lock, but lacking an assertion
>    for that fact, its use spread and violated this. The result is that the
>    back-link state is prone to races.
>
>  - it is rate-limited on jiffies, which is HZ, not the underlying PELT decay.
>
>  - since its update is tied to task_h_load() usage, the cfs_rq->h_load numbers are
>    not often 'up-to-date', rendering their output in sched/debug near useless.
>
> Rework the whole thing to keep a more up-to-date and less broken cfs_rq->h_load
> number.
>
> Move the back-link tracking into for_each_sched_entity(), such that any such
> loop sets up a path back. Use this to (optionally) re-compute cfs_rq->h_load on
> enqueue, dequeue, set_next and tick, all sites that hold rq->lock.
>
> This ensures that 'active' cgroups have reasonably up-to-date cfs_rq->h_load.
> Additionally, have __update_blocked_fair() update cfs_rq->h_load for all
> cgroups.
>
> Finally, replace the jiffy rate-limit with one that is tied to the PELT decay.
>
> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
> ---
>  kernel/sched/fair.c  |  108 ++++++++++++++++++++++++++++++++-------------------
>  kernel/sched/pelt.h  |    7 +++
>  kernel/sched/sched.h |    1
>  3 files changed, 76 insertions(+), 40 deletions(-)
>
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -5541,6 +5541,44 @@ static inline bool skip_blocked_update(s
>         return true;
>  }
>
> +static inline void __update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> +                                         struct sched_entity *se,
> +                                         struct cfs_rq *p_cfs_rq)
> +{
> +       unsigned long load = cfs_rq->avg.load_avg;
> +
> +       if (cfs_rq != &cfs_rq->rq->cfs) {

A comment explaining that the 2 belows are used for the case where
for_each_sched_entity has not been called to set backlink would be
helpful

I haven't run tests yet (I will during the week) but the rework looks good to me




> +               if (!se)
> +                       se = &container_of(cfs_rq, struct cfs_tg_state, cfs_rq)->se;
> +               if (!p_cfs_rq)
> +                       p_cfs_rq = cfs_rq_of(se);
> +
> +               load = p_cfs_rq->h_load;
> +               load = div64_ul(load * se->avg.load_avg,
> +                               p_cfs_rq->avg.load_avg + 1);
> +       }
> +
> +       WRITE_ONCE(cfs_rq->h_load, load);
> +}
> +
> +static inline bool update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> +                                       struct sched_entity *se,
> +                                       struct cfs_rq *p_cfs_rq)
> +{
> +       /*
> +        * Mask out the segment bits, if the remaining bits match, then there
> +        * hasn't been a decay since the last time.
> +        */
> +       if ((cfs_rq->last_h_load_update & ~PELT_SEGMENT_MASK) ==
> +           (cfs_rq->avg.last_update_time & ~PELT_SEGMENT_MASK))
> +               return false;
> +
> +       __update_cfs_rq_h_load(cfs_rq, se, p_cfs_rq);
> +
> +       cfs_rq->last_h_load_update = cfs_rq->avg.last_update_time;
> +       return true;
> +}
> +
>  #else /* !CONFIG_FAIR_GROUP_SCHED: */
>
>  static inline void update_tg_load_avg(struct cfs_rq *cfs_rq) {}
> @@ -5554,6 +5592,10 @@ static inline int propagate_entity_load_
>
>  static inline void add_tg_cfs_propagate(struct cfs_rq *cfs_rq, long runnable_sum) {}
>
> +static inline bool update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> +                                       struct sched_entity *se,
> +                                       struct cfs_rq *p_cfs_rq) { return false; }
> +
>  #endif /* !CONFIG_FAIR_GROUP_SCHED */
>
>  #ifdef CONFIG_NO_HZ_COMMON
> @@ -7966,6 +8008,9 @@ static unsigned long enqueue_hierarchy(s
>                 flags = ENQUEUE_WAKEUP;
>         }
>
> +       for_each_sched_entity_bl(se, cfs_rq)
> +               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> +
>         return weight;
>  }
>
> @@ -8107,6 +8152,9 @@ static void dequeue_hierarchy(struct tas
>                 flags |= DEQUEUE_SLEEP;
>                 flags &= ~(DEQUEUE_DELAYED | DEQUEUE_SPECIAL);
>         }
> +
> +       for_each_sched_entity_bl(se, cfs_rq)
> +               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
>  }
>
>  /*
> @@ -11288,51 +11336,23 @@ static bool __update_blocked_fair(struct
>                         *done = false;
>         }
>
> -       return decayed;
> -}
> -
> -/*
> - * Compute the hierarchical load factor for cfs_rq and all its ascendants.
> - * This needs to be done in a top-down fashion because the load of a child
> - * group is a fraction of its parents load.
> - */
> -static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq)
> -{
> -       struct sched_entity *se = cfs_rq_se(cfs_rq);
> -       unsigned long now = jiffies;
> -       unsigned long load;
> -
> -       if (cfs_rq->last_h_load_update == now)
> -               return;
> -
> -       WRITE_ONCE(cfs_rq->h_load_next, NULL);
> -       for_each_sched_entity(se, cfs_rq) {
> -               WRITE_ONCE(cfs_rq->h_load_next, se);
> -               if (cfs_rq->last_h_load_update == now)
> -                       break;
> -       }
> -
> -       if (!se) {
> -               cfs_rq->h_load = cfs_rq_load_avg(cfs_rq);
> -               cfs_rq->last_h_load_update = now;
> -       }
> +       /*
> +        * The above (forward) leaf_cfs_rq_list traversal will have done
> +        * update_cfs_rq_load_avg() in a bottom-up fashion. Now iterate the
> +        * list backwards, such that we're ensured to have visited every
> +        * parent of the current group to update h_load in a top-down fashion.
> +        */
> +       list_for_each_entry_reverse(cfs_rq, &rq->leaf_cfs_rq_list, leaf_cfs_rq_list)
> +               update_cfs_rq_h_load(cfs_rq, NULL, NULL);
>
> -       while ((se = READ_ONCE(cfs_rq->h_load_next)) != NULL) {
> -               load = cfs_rq->h_load;
> -               load = div64_ul(load * se->avg.load_avg,
> -                               cfs_rq_load_avg(cfs_rq) + 1);
> -               cfs_rq = group_cfs_rq(se);
> -               cfs_rq->h_load = load;
> -               cfs_rq->last_h_load_update = now;
> -       }
> +       return decayed;
>  }
>
>  static unsigned long task_h_load(struct task_struct *p)
>  {
>         struct cfs_rq *cfs_rq = task_cfs_rq(p);
>
> -       update_cfs_rq_h_load(cfs_rq);
> -       return div64_ul(p->se.avg.load_avg * cfs_rq->h_load,
> +       return div64_ul(p->se.avg.load_avg * READ_ONCE(cfs_rq->h_load),
>                         cfs_rq_load_avg(cfs_rq) + 1);
>  }
>  #else /* !CONFIG_FAIR_GROUP_SCHED: */
> @@ -14949,7 +14969,7 @@ static inline void task_tick_core(struct
>  /*
>   * se_fi_update - Update the cfs_rq->zero_vruntime_fi in a CFS hierarchy if needed.
>   */
> -static void se_fi_update(const struct sched_entity *se, unsigned int fi_seq,
> +static void se_fi_update(struct sched_entity *se, unsigned int fi_seq,
>                          bool forceidle)
>  {
>         struct cfs_rq *cfs_rq;
> @@ -15039,6 +15059,11 @@ static void task_tick_fair(struct rq *rq
>
>                 se = &curr->se;
>                 reweight_eevdf(cfs_rq, se, weight, se->on_rq);
> +
> +               if (!hrtick) {
> +                       for_each_sched_entity_bl(se, cfs_rq)
> +                               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> +               }
>         }
>
>         if (hrtick)
> @@ -15230,6 +15255,9 @@ static void set_next_task_fair(struct rq
>                         weight = __calc_prop_weight(cfs_rq, se, weight);
>         }
>
> +       for_each_sched_entity_bl(se, cfs_rq)
> +               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> +
>         if (throttled)
>                 task_throttle_setup_work(p);
>
> @@ -15428,6 +15456,8 @@ static int __sched_group_set_shares(stru
>                         update_load_avg(cfs_rq, se, UPDATE_TG);
>                         update_cfs_group(se);
>                 }
> +               for_each_sched_entity_bl(se, cfs_rq)
> +                       update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
>                 rq_unlock_irqrestore(rq, &rf);
>         }
>
> --- a/kernel/sched/pelt.h
> +++ b/kernel/sched/pelt.h
> @@ -5,6 +5,13 @@
>
>  #include "sched-pelt.h"
>
> +/*
> + * Pelt uses apprixmate 'us' as ns/1024; and then uses time segments of 1024
> + * 'us'. As a result each segment is in fact '1<<20' ns.
> + */
> +#define PELT_SEGMENT_NS                (1<<20)
> +#define PELT_SEGMENT_MASK      (PELT_SEGMENT_NS-1)
> +
>  int __update_load_avg_blocked_se(u64 now, struct sched_entity *se);
>  int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se);
>  int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq);
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -736,7 +736,6 @@ struct cfs_rq {
>          */
>         unsigned long           h_load;
>         u64                     last_h_load_update;
> -       struct sched_entity     *h_load_next;
>
>         struct rq               *rq;    /* CPU runqueue to which this cfs_rq is attached */
>
>
>

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-08-31 10:10   ` Vincent Guittot
@ 2026-08-31 10:37     ` Peter Zijlstra
  2026-08-31 12:06       ` Vincent Guittot
  0 siblings, 1 reply; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-31 10:37 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman,
	vschneid, kprateek.nayak, tj, linux-kernel

On Mon, Aug 31, 2026 at 12:10:42PM +0200, Vincent Guittot wrote:
> > +static inline void __update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> > +                                         struct sched_entity *se,
> > +                                         struct cfs_rq *p_cfs_rq)
> > +{
> > +       unsigned long load = cfs_rq->avg.load_avg;
> > +
> > +       if (cfs_rq != &cfs_rq->rq->cfs) {
> 
> A comment explaining that the 2 belows are used for the case where
> for_each_sched_entity has not been called to set backlink would be
> helpful

Fair enough; something like so?

		/*
		 * These last two arguments can be NULL then used outside of
		 * the for_each_sched_entity() hierarchy iteration. Like in
		 * __update_blocked_fair() where leaf_cfs_rq_list is iterated
		 * instead.
		 */

> > +               if (!se)
> > +                       se = &container_of(cfs_rq, struct cfs_tg_state, cfs_rq)->se;
> > +               if (!p_cfs_rq)
> > +                       p_cfs_rq = cfs_rq_of(se);
> > +
> > +               load = p_cfs_rq->h_load;
> > +               load = div64_ul(load * se->avg.load_avg,
> > +                               p_cfs_rq->avg.load_avg + 1);
> > +       }
> > +
> > +       WRITE_ONCE(cfs_rq->h_load, load);
> > +}
> > +
> > +static inline bool update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> > +                                       struct sched_entity *se,
> > +                                       struct cfs_rq *p_cfs_rq)
> > +{
> > +       /*
> > +        * Mask out the segment bits, if the remaining bits match, then there
> > +        * hasn't been a decay since the last time.
> > +        */
> > +       if ((cfs_rq->last_h_load_update & ~PELT_SEGMENT_MASK) ==
> > +           (cfs_rq->avg.last_update_time & ~PELT_SEGMENT_MASK))
> > +               return false;
> > +
> > +       __update_cfs_rq_h_load(cfs_rq, se, p_cfs_rq);
> > +
> > +       cfs_rq->last_h_load_update = cfs_rq->avg.last_update_time;
> > +       return true;
> > +}

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-08-31 10:37     ` Peter Zijlstra
@ 2026-08-31 12:06       ` Vincent Guittot
  2026-08-31 13:07         ` Peter Zijlstra
  0 siblings, 1 reply; 15+ messages in thread
From: Vincent Guittot @ 2026-08-31 12:06 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman,
	vschneid, kprateek.nayak, tj, linux-kernel

On Mon, 31 Aug 2026 at 12:37, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Mon, Aug 31, 2026 at 12:10:42PM +0200, Vincent Guittot wrote:
> > > +static inline void __update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> > > +                                         struct sched_entity *se,
> > > +                                         struct cfs_rq *p_cfs_rq)
> > > +{
> > > +       unsigned long load = cfs_rq->avg.load_avg;
> > > +
> > > +       if (cfs_rq != &cfs_rq->rq->cfs) {
> >
> > A comment explaining that the 2 belows are used for the case where
> > for_each_sched_entity has not been called to set backlink would be
> > helpful
>
> Fair enough; something like so?

yes, looks good

>
>                 /*
>                  * These last two arguments can be NULL then used outside of
 s/then/when/ ?

>                  * the for_each_sched_entity() hierarchy iteration. Like in
>                  * __update_blocked_fair() where leaf_cfs_rq_list is iterated
>                  * instead.
>                  */
>
> > > +               if (!se)
> > > +                       se = &container_of(cfs_rq, struct cfs_tg_state, cfs_rq)->se;
> > > +               if (!p_cfs_rq)
> > > +                       p_cfs_rq = cfs_rq_of(se);
> > > +
> > > +               load = p_cfs_rq->h_load;
> > > +               load = div64_ul(load * se->avg.load_avg,
> > > +                               p_cfs_rq->avg.load_avg + 1);
> > > +       }
> > > +
> > > +       WRITE_ONCE(cfs_rq->h_load, load);
> > > +}
> > > +
> > > +static inline bool update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> > > +                                       struct sched_entity *se,
> > > +                                       struct cfs_rq *p_cfs_rq)
> > > +{
> > > +       /*
> > > +        * Mask out the segment bits, if the remaining bits match, then there
> > > +        * hasn't been a decay since the last time.
> > > +        */
> > > +       if ((cfs_rq->last_h_load_update & ~PELT_SEGMENT_MASK) ==
> > > +           (cfs_rq->avg.last_update_time & ~PELT_SEGMENT_MASK))
> > > +               return false;
> > > +
> > > +       __update_cfs_rq_h_load(cfs_rq, se, p_cfs_rq);
> > > +
> > > +       cfs_rq->last_h_load_update = cfs_rq->avg.last_update_time;
> > > +       return true;
> > > +}

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-08-31 12:06       ` Vincent Guittot
@ 2026-08-31 13:07         ` Peter Zijlstra
  0 siblings, 0 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-08-31 13:07 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall, mgorman,
	vschneid, kprateek.nayak, tj, linux-kernel

On Mon, Aug 31, 2026 at 02:06:14PM +0200, Vincent Guittot wrote:
> On Mon, 31 Aug 2026 at 12:37, Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > On Mon, Aug 31, 2026 at 12:10:42PM +0200, Vincent Guittot wrote:
> > > > +static inline void __update_cfs_rq_h_load(struct cfs_rq *cfs_rq,
> > > > +                                         struct sched_entity *se,
> > > > +                                         struct cfs_rq *p_cfs_rq)
> > > > +{
> > > > +       unsigned long load = cfs_rq->avg.load_avg;
> > > > +
> > > > +       if (cfs_rq != &cfs_rq->rq->cfs) {
> > >
> > > A comment explaining that the 2 belows are used for the case where
> > > for_each_sched_entity has not been called to set backlink would be
> > > helpful
> >
> > Fair enough; something like so?
> 
> yes, looks good
> 
> >
> >                 /*
> >                  * These last two arguments can be NULL then used outside of
>  s/then/when/ ?

Just so, typing hard :/

> >                  * the for_each_sched_entity() hierarchy iteration. Like in
> >                  * __update_blocked_fair() where leaf_cfs_rq_list is iterated
> >                  * instead.
> >                  */

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-08-28  7:41 ` [PATCH 4/4] sched/fair: Rework/fix task_h_load() Peter Zijlstra
  2026-08-31 10:10   ` Vincent Guittot
@ 2026-09-02  5:26   ` Chen Yu
  2026-09-02  7:55     ` Vincent Guittot
  1 sibling, 1 reply; 15+ messages in thread
From: Chen Yu @ 2026-09-02  5:26 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: mingo, juri.lelli, vincent.guittot, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel,
	chen.yu, tim.c.chen

On Fri, Aug 28, 2026 at 09:41:03AM +0200, Peter Zijlstra wrote:

[ ... ]

> @@ -15230,6 +15255,9 @@ static void set_next_task_fair(struct rq
>  			weight = __calc_prop_weight(cfs_rq, se, weight);
>  	}
>  
> +	for_each_sched_entity_bl(se, cfs_rq)
> +		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> +

It panics during bootup on a 192 Cores system,
[    9.445362][ T1687] Oops: general protection fault, kernel NULL pointer dereference 0x69: 0000 [#1] SMP NOPTI
[   18.987940][ T1687] CPU: 58 UID: 0 PID: 1687 Comm: systemd-udevd Not tainted 7.3.0-rc1-flat-hload+ #9 PREEMPTLAZY 
[   19.022374][ T1687] RIP: 0010:pick_task_fair+0x43/0xd0
[   19.175316][ T1687]  <TASK>
[   19.181239][ T1687]  __pick_next_task+0x49/0x1b0
[   19.189229][ T1687]  __schedule+0x14b/0x6b0
[   19.196711][ T1687]  preempt_schedule+0x3a/0x60
[   19.204543][ T1687]  preempt_schedule_thunk+0x16/0x40
[   19.212931][ T1687]  _raw_spin_unlock_irqrestore+0x2b/0x30
[   19.221833][ T1687]  autogroup_move_group+0xc5/0x160
[   19.230124][ T1687]  sched_autogroup_create_attach+0xa7/0x180
[   19.239280][ T1687]  ksys_setsid+0x12c/0x170
[   19.246753][ T1687]  __do_sys_setsid+0xe/0x20
[   19.254292][ T1687]  do_syscall_64+0xbc/0x470
[   19.307858][ T1687]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
[   19.316405][ T1687] RIP: 0033:0x7f347bfbcb9b

It seems that the crash is a read of se->sched_delayed on a NULL se inside
pick_next_entity(), so pick_eevdf() return NULL

After the following top->down backlink traverse,
for_each_sched_entity_bl(se, cfs_rq)
    update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);

cfs_rq is not the root->cfs_rq anymore, but a middle cfs_rq(autogroup
in above example). Meanwhile rq->cfs.curr remains NULL because the
rq->cfs.curr has been dequeued if the task is runnable and queued:
if (on_rq)
	__dequeue_entity(cfs_rq, se)

se = &p->se;
cfs_rq->curr = se; /*wrong cfs_rq*/

Then later _raw_spin_unlock_irqrestore triggers the scheduling
it picks from rq->cfs_rq.curr with a NULL tree.

Maybe we need to restore the rq->cfs_rq after the
for_each_sched_entity_bl()?
se = &p->se;
cfs_rq = &rq->cfs; <--
cfs_rq->curr = se;

thanks,
Chenyu

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-09-02  5:26   ` Chen Yu
@ 2026-09-02  7:55     ` Vincent Guittot
  2026-09-02  8:13       ` Peter Zijlstra
  0 siblings, 1 reply; 15+ messages in thread
From: Vincent Guittot @ 2026-09-02  7:55 UTC (permalink / raw)
  To: Chen Yu
  Cc: Peter Zijlstra, mingo, juri.lelli, dietmar.eggemann, rostedt,
	bsegall, mgorman, vschneid, kprateek.nayak, tj, linux-kernel,
	chen.yu, tim.c.chen

On Wed, 2 Sept 2026 at 07:39, Chen Yu <yu.c.chen@intel.com> wrote:
>
> On Fri, Aug 28, 2026 at 09:41:03AM +0200, Peter Zijlstra wrote:
>
> [ ... ]
>
> > @@ -15230,6 +15255,9 @@ static void set_next_task_fair(struct rq
> >                       weight = __calc_prop_weight(cfs_rq, se, weight);
> >       }
> >
> > +     for_each_sched_entity_bl(se, cfs_rq)
> > +             update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> > +
>
> It panics during bootup on a 192 Cores system,
> [    9.445362][ T1687] Oops: general protection fault, kernel NULL pointer dereference 0x69: 0000 [#1] SMP NOPTI
> [   18.987940][ T1687] CPU: 58 UID: 0 PID: 1687 Comm: systemd-udevd Not tainted 7.3.0-rc1-flat-hload+ #9 PREEMPTLAZY
> [   19.022374][ T1687] RIP: 0010:pick_task_fair+0x43/0xd0
> [   19.175316][ T1687]  <TASK>
> [   19.181239][ T1687]  __pick_next_task+0x49/0x1b0
> [   19.189229][ T1687]  __schedule+0x14b/0x6b0
> [   19.196711][ T1687]  preempt_schedule+0x3a/0x60
> [   19.204543][ T1687]  preempt_schedule_thunk+0x16/0x40
> [   19.212931][ T1687]  _raw_spin_unlock_irqrestore+0x2b/0x30
> [   19.221833][ T1687]  autogroup_move_group+0xc5/0x160
> [   19.230124][ T1687]  sched_autogroup_create_attach+0xa7/0x180
> [   19.239280][ T1687]  ksys_setsid+0x12c/0x170
> [   19.246753][ T1687]  __do_sys_setsid+0xe/0x20
> [   19.254292][ T1687]  do_syscall_64+0xbc/0x470
> [   19.307858][ T1687]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> [   19.316405][ T1687] RIP: 0033:0x7f347bfbcb9b
>
> It seems that the crash is a read of se->sched_delayed on a NULL se inside
> pick_next_entity(), so pick_eevdf() return NULL

I faced the same crash while testing

>
> After the following top->down backlink traverse,
> for_each_sched_entity_bl(se, cfs_rq)
>     update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
>
> cfs_rq is not the root->cfs_rq anymore, but a middle cfs_rq(autogroup
> in above example). Meanwhile rq->cfs.curr remains NULL because the
> rq->cfs.curr has been dequeued if the task is runnable and queued:
> if (on_rq)
>         __dequeue_entity(cfs_rq, se)
>
> se = &p->se;
> cfs_rq->curr = se; /*wrong cfs_rq*/
>
> Then later _raw_spin_unlock_irqrestore triggers the scheduling
> it picks from rq->cfs_rq.curr with a NULL tree.
>
> Maybe we need to restore the rq->cfs_rq after the
> for_each_sched_entity_bl()?
> se = &p->se;
> cfs_rq = &rq->cfs; <--

Yes, this fixes it for me too

> cfs_rq->curr = se;
>
> thanks,
> Chenyu

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-09-02  7:55     ` Vincent Guittot
@ 2026-09-02  8:13       ` Peter Zijlstra
  2026-09-02 10:36         ` Peter Zijlstra
  2026-09-02 10:37         ` Vincent Guittot
  0 siblings, 2 replies; 15+ messages in thread
From: Peter Zijlstra @ 2026-09-02  8:13 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: Chen Yu, mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, kprateek.nayak, tj, linux-kernel, chen.yu,
	tim.c.chen

On Wed, Sep 02, 2026 at 09:55:49AM +0200, Vincent Guittot wrote:
> On Wed, 2 Sept 2026 at 07:39, Chen Yu <yu.c.chen@intel.com> wrote:
> >
> > On Fri, Aug 28, 2026 at 09:41:03AM +0200, Peter Zijlstra wrote:
> >
> > [ ... ]
> >
> > > @@ -15230,6 +15255,9 @@ static void set_next_task_fair(struct rq
> > >                       weight = __calc_prop_weight(cfs_rq, se, weight);
> > >       }
> > >
> > > +     for_each_sched_entity_bl(se, cfs_rq)
> > > +             update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> > > +
> >
> > It panics during bootup on a 192 Cores system,
> > [    9.445362][ T1687] Oops: general protection fault, kernel NULL pointer dereference 0x69: 0000 [#1] SMP NOPTI
> > [   18.987940][ T1687] CPU: 58 UID: 0 PID: 1687 Comm: systemd-udevd Not tainted 7.3.0-rc1-flat-hload+ #9 PREEMPTLAZY
> > [   19.022374][ T1687] RIP: 0010:pick_task_fair+0x43/0xd0
> > [   19.175316][ T1687]  <TASK>
> > [   19.181239][ T1687]  __pick_next_task+0x49/0x1b0
> > [   19.189229][ T1687]  __schedule+0x14b/0x6b0
> > [   19.196711][ T1687]  preempt_schedule+0x3a/0x60
> > [   19.204543][ T1687]  preempt_schedule_thunk+0x16/0x40
> > [   19.212931][ T1687]  _raw_spin_unlock_irqrestore+0x2b/0x30
> > [   19.221833][ T1687]  autogroup_move_group+0xc5/0x160
> > [   19.230124][ T1687]  sched_autogroup_create_attach+0xa7/0x180
> > [   19.239280][ T1687]  ksys_setsid+0x12c/0x170
> > [   19.246753][ T1687]  __do_sys_setsid+0xe/0x20
> > [   19.254292][ T1687]  do_syscall_64+0xbc/0x470
> > [   19.307858][ T1687]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > [   19.316405][ T1687] RIP: 0033:0x7f347bfbcb9b
> >
> > It seems that the crash is a read of se->sched_delayed on a NULL se inside
> > pick_next_entity(), so pick_eevdf() return NULL
> 
> I faced the same crash while testing

Weirdly that crash didn't show up for me :-(, I had a few others that I
cured.

> >
> > After the following top->down backlink traverse,
> > for_each_sched_entity_bl(se, cfs_rq)
> >     update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> >
> > cfs_rq is not the root->cfs_rq anymore, but a middle cfs_rq(autogroup
> > in above example). Meanwhile rq->cfs.curr remains NULL because the
> > rq->cfs.curr has been dequeued if the task is runnable and queued:
> > if (on_rq)
> >         __dequeue_entity(cfs_rq, se)
> >
> > se = &p->se;
> > cfs_rq->curr = se; /*wrong cfs_rq*/
> >
> > Then later _raw_spin_unlock_irqrestore triggers the scheduling
> > it picks from rq->cfs_rq.curr with a NULL tree.
> >
> > Maybe we need to restore the rq->cfs_rq after the
> > for_each_sched_entity_bl()?
> > se = &p->se;
> > cfs_rq = &rq->cfs; <--
> 
> Yes, this fixes it for me too

So I had this issue in task_tick_fair(), where
for_each_sched_entity_bl() clobbered cfs_rq, and fixed that by moving
things after reweight_eevdf() (which is what uses cfs_rq).

I at point I did actually look to see if anybody else would suffer that
same problem, but clearly I missed one.

I'm thinking the problem here is set_next_task_fair() ? I think I
misread the:

	se = &p->se;
	cfs_rq->curr = se;

to reset both se and cfs_rq, but clearly it doesn't. The below should
fix I suppose. Let me go and try and reproduce.


---
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15287,9 +15287,6 @@ static void set_next_task_fair(struct rq
 			weight = __calc_prop_weight(cfs_rq, se, weight);
 	}
 
-	for_each_sched_entity_bl(se, cfs_rq)
-		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
-
 	if (throttled)
 		task_throttle_setup_work(p);
 
@@ -15302,6 +15299,9 @@ static void set_next_task_fair(struct rq
 			set_protect_slice(cfs_rq, se);
 	}
 
+	for_each_sched_entity_bl(se, cfs_rq)
+		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
+
 	if (task_on_rq_queued(p)) {
 		/*
 		 * Move the next running task to the front of the list, so our

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-09-02  8:13       ` Peter Zijlstra
@ 2026-09-02 10:36         ` Peter Zijlstra
  2026-09-02 10:39           ` Vincent Guittot
  2026-09-02 10:37         ` Vincent Guittot
  1 sibling, 1 reply; 15+ messages in thread
From: Peter Zijlstra @ 2026-09-02 10:36 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: Chen Yu, mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, kprateek.nayak, tj, linux-kernel, chen.yu,
	tim.c.chen

On Wed, Sep 02, 2026 at 10:13:01AM +0200, Peter Zijlstra wrote:

> Let me go and try and reproduce.

Different physical machine.. *splat*, virtual machine it lives. Argh I
hate computers.

Anyway, confirmed on physical machine that triggered it, the blow seems
to cure things. I'll fold it in.

---
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -15287,9 +15287,6 @@ static void set_next_task_fair(struct rq
 			weight = __calc_prop_weight(cfs_rq, se, weight);
 	}
 
-	for_each_sched_entity_bl(se, cfs_rq)
-		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
-
 	if (throttled)
 		task_throttle_setup_work(p);
 
@@ -15309,11 +15306,15 @@ static void set_next_task_fair(struct rq
 		 */
 		list_move(&se->group_node, &rq->cfs_tasks);
 	}
-	if (!first)
-		return;
 
 	WARN_ON_ONCE(se->sched_delayed);
 
+	for_each_sched_entity_bl(se, cfs_rq)
+		update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
+
+	if (!first)
+		return;
+
 	if (hrtick_enabled_fair(rq))
 		hrtick_start_fair(rq, p);
 



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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-09-02  8:13       ` Peter Zijlstra
  2026-09-02 10:36         ` Peter Zijlstra
@ 2026-09-02 10:37         ` Vincent Guittot
  1 sibling, 0 replies; 15+ messages in thread
From: Vincent Guittot @ 2026-09-02 10:37 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Chen Yu, mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, kprateek.nayak, tj, linux-kernel, chen.yu,
	tim.c.chen

On Wed, 2 Sept 2026 at 10:13, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Wed, Sep 02, 2026 at 09:55:49AM +0200, Vincent Guittot wrote:
> > On Wed, 2 Sept 2026 at 07:39, Chen Yu <yu.c.chen@intel.com> wrote:
> > >
> > > On Fri, Aug 28, 2026 at 09:41:03AM +0200, Peter Zijlstra wrote:
> > >
> > > [ ... ]
> > >
> > > > @@ -15230,6 +15255,9 @@ static void set_next_task_fair(struct rq
> > > >                       weight = __calc_prop_weight(cfs_rq, se, weight);
> > > >       }
> > > >
> > > > +     for_each_sched_entity_bl(se, cfs_rq)
> > > > +             update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> > > > +
> > >
> > > It panics during bootup on a 192 Cores system,
> > > [    9.445362][ T1687] Oops: general protection fault, kernel NULL pointer dereference 0x69: 0000 [#1] SMP NOPTI
> > > [   18.987940][ T1687] CPU: 58 UID: 0 PID: 1687 Comm: systemd-udevd Not tainted 7.3.0-rc1-flat-hload+ #9 PREEMPTLAZY
> > > [   19.022374][ T1687] RIP: 0010:pick_task_fair+0x43/0xd0
> > > [   19.175316][ T1687]  <TASK>
> > > [   19.181239][ T1687]  __pick_next_task+0x49/0x1b0
> > > [   19.189229][ T1687]  __schedule+0x14b/0x6b0
> > > [   19.196711][ T1687]  preempt_schedule+0x3a/0x60
> > > [   19.204543][ T1687]  preempt_schedule_thunk+0x16/0x40
> > > [   19.212931][ T1687]  _raw_spin_unlock_irqrestore+0x2b/0x30
> > > [   19.221833][ T1687]  autogroup_move_group+0xc5/0x160
> > > [   19.230124][ T1687]  sched_autogroup_create_attach+0xa7/0x180
> > > [   19.239280][ T1687]  ksys_setsid+0x12c/0x170
> > > [   19.246753][ T1687]  __do_sys_setsid+0xe/0x20
> > > [   19.254292][ T1687]  do_syscall_64+0xbc/0x470
> > > [   19.307858][ T1687]  entry_SYSCALL_64_after_hwframe+0x76/0x7e
> > > [   19.316405][ T1687] RIP: 0033:0x7f347bfbcb9b
> > >
> > > It seems that the crash is a read of se->sched_delayed on a NULL se inside
> > > pick_next_entity(), so pick_eevdf() return NULL
> >
> > I faced the same crash while testing
>
> Weirdly that crash didn't show up for me :-(, I had a few others that I
> cured.

Some of my platforms didn't crash until I added +cpu in cgroup.sub_controller

>
> > >
> > > After the following top->down backlink traverse,
> > > for_each_sched_entity_bl(se, cfs_rq)
> > >     update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> > >
> > > cfs_rq is not the root->cfs_rq anymore, but a middle cfs_rq(autogroup
> > > in above example). Meanwhile rq->cfs.curr remains NULL because the
> > > rq->cfs.curr has been dequeued if the task is runnable and queued:
> > > if (on_rq)
> > >         __dequeue_entity(cfs_rq, se)
> > >
> > > se = &p->se;
> > > cfs_rq->curr = se; /*wrong cfs_rq*/
> > >
> > > Then later _raw_spin_unlock_irqrestore triggers the scheduling
> > > it picks from rq->cfs_rq.curr with a NULL tree.
> > >
> > > Maybe we need to restore the rq->cfs_rq after the
> > > for_each_sched_entity_bl()?
> > > se = &p->se;
> > > cfs_rq = &rq->cfs; <--
> >
> > Yes, this fixes it for me too
>
> So I had this issue in task_tick_fair(), where
> for_each_sched_entity_bl() clobbered cfs_rq, and fixed that by moving
> things after reweight_eevdf() (which is what uses cfs_rq).

Yeah, all eevdf related things aim to use root cfs

>
> I at point I did actually look to see if anybody else would suffer that
> same problem, but clearly I missed one.
>
> I'm thinking the problem here is set_next_task_fair() ? I think I
> misread the:
>
>         se = &p->se;
>         cfs_rq->curr = se;
>
> to reset both se and cfs_rq, but clearly it doesn't. The below should
> fix I suppose. Let me go and try and reproduce.

it doesn't boot witht he below

>
>
> ---
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15287,9 +15287,6 @@ static void set_next_task_fair(struct rq
>                         weight = __calc_prop_weight(cfs_rq, se, weight);
>         }
>
> -       for_each_sched_entity_bl(se, cfs_rq)
> -               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> -
>         if (throttled)
>                 task_throttle_setup_work(p);
>
> @@ -15302,6 +15299,9 @@ static void set_next_task_fair(struct rq
>                         set_protect_slice(cfs_rq, se);
>         }
>
> +       for_each_sched_entity_bl(se, cfs_rq)

Doesn't for_each_sched_entity_bl assume to start from root cfs as well ?


> +               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> +

This modifies se, which is then used below in
list_move(se->group_node, &rq->cfs_tasks);
and
WARN_ON_ONCE(se->sched_delayed); below

Might be good to save pse = p->se


>         if (task_on_rq_queued(p)) {
>                 /*
>                  * Move the next running task to the front of the list, so our

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

* Re: [PATCH 4/4] sched/fair: Rework/fix task_h_load()
  2026-09-02 10:36         ` Peter Zijlstra
@ 2026-09-02 10:39           ` Vincent Guittot
  0 siblings, 0 replies; 15+ messages in thread
From: Vincent Guittot @ 2026-09-02 10:39 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Chen Yu, mingo, juri.lelli, dietmar.eggemann, rostedt, bsegall,
	mgorman, vschneid, kprateek.nayak, tj, linux-kernel, chen.yu,
	tim.c.chen

On Wed, 2 Sept 2026 at 12:37, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Wed, Sep 02, 2026 at 10:13:01AM +0200, Peter Zijlstra wrote:
>
> > Let me go and try and reproduce.
>
> Different physical machine.. *splat*, virtual machine it lives. Argh I
> hate computers.
>
> Anyway, confirmed on physical machine that triggered it, the blow seems
> to cure things. I'll fold it in.
>
> ---
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -15287,9 +15287,6 @@ static void set_next_task_fair(struct rq
>                         weight = __calc_prop_weight(cfs_rq, se, weight);
>         }
>
> -       for_each_sched_entity_bl(se, cfs_rq)
> -               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> -
>         if (throttled)
>                 task_throttle_setup_work(p);
>
> @@ -15309,11 +15306,15 @@ static void set_next_task_fair(struct rq
>                  */
>                 list_move(&se->group_node, &rq->cfs_tasks);
>         }
> -       if (!first)
> -               return;
>
>         WARN_ON_ONCE(se->sched_delayed);
>
> +       for_each_sched_entity_bl(se, cfs_rq)
> +               update_cfs_rq_h_load(group_cfs_rq(se), se, cfs_rq);
> +

should work too

> +       if (!first)
> +               return;
> +
>         if (hrtick_enabled_fair(rq))
>                 hrtick_start_fair(rq, p);
>
>
>

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

end of thread, other threads:[~2026-09-02 10:39 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-28  7:40 [PATCH 0/4] sched/fair: Rework tash_h_load() Peter Zijlstra
2026-08-28  7:41 ` [PATCH 1/4] sched: Rename/clarify sched_class::task_tick(.queued) argument Peter Zijlstra
2026-08-28  7:41 ` [PATCH 2/4] sched/fair: Fold cfs_rq_of(se) into for_each_sched_entity() Peter Zijlstra
2026-08-28  7:41 ` [PATCH 3/4] sched/fair: Extend for_each_sched_entity() with a back-link Peter Zijlstra
2026-08-28  7:41 ` [PATCH 4/4] sched/fair: Rework/fix task_h_load() Peter Zijlstra
2026-08-31 10:10   ` Vincent Guittot
2026-08-31 10:37     ` Peter Zijlstra
2026-08-31 12:06       ` Vincent Guittot
2026-08-31 13:07         ` Peter Zijlstra
2026-09-02  5:26   ` Chen Yu
2026-09-02  7:55     ` Vincent Guittot
2026-09-02  8:13       ` Peter Zijlstra
2026-09-02 10:36         ` Peter Zijlstra
2026-09-02 10:39           ` Vincent Guittot
2026-09-02 10:37         ` Vincent Guittot

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®