Re: [PATCH 1/4] sched/cache: Keep nr_pref_llc_running in the runnable domain
From: Peter Zijlstra
Date: Wed Sep 16 2026 - 08:51:22 EST
On Thu, Sep 10, 2026 at 10:46:09AM -0700, Tim Chen wrote:
> alb_break_llc() decides whether to break LLC preference during active
> load balance. It does so by testing that every runnable fair task on the
> source rq prefers its LLC:
>
> env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable
>
> But the two counters cover different sets. nr_pref_llc_running is updated
> in account_llc_enqueue()/account_llc_dequeue(), next to cfs_rq->nr_queued,
> so it follows queued tasks. h_nr_runnable is updated in set_delayed()/
> clear_delayed() and drops delay-dequeued tasks.
>
> So under DELAY_DEQUEUE, a preferring task that goes to sleep stays counted
> in nr_pref_llc_running while h_nr_runnable falls. The equality then breaks,
> alb_break_llc() returns false, and active balance is free to pull a task
> off its preferred LLC. Active balance only moves runnable tasks, and this
> is the only LLC check it consults: once the stopper runs, LBF_ACTIVE_LB
> skips the per-task test in can_migrate_task(). The runnable set is the one
> we want.
>
> Fix it on the counter side. A task should be counted in
> nr_pref_llc_running exactly while it is both queued on its preferred LLC
> (pref_llc_queued) and runnable (!sched_delayed). Define that membership
> once in task_pref_llc_runnable(), and adjust the counter only through
> pref_llc_running_inc()/pref_llc_running_dec() from the four sites that
> change either input: account_llc_enqueue(), account_llc_dequeue(),
> set_delayed() and clear_delayed(). Gating every update on the same
> predicate keeps the delay, wake and dequeue paths from double-counting
> or underflowing; see the comments at those sites for the ordering.
>
> nr_llc_running and sd->llc_counts are not touched and stay on queued
> semantics.
Needs a fixes tag.
> Reported-by: Zhan Xusheng <zhanxusheng@xxxxxxxxxx>
> Closes: https://lore.kernel.org/lkml/20260827135000.735138-1-zhanxusheng@xxxxxxxxxx/
> Suggested-by: Chen Yu <yu.c.chen@xxxxxxxxx>
> Signed-off-by: Tim Chen <tim.c.chen@xxxxxxxxxxxxxxx>
> ---
> @@ -6395,6 +6428,14 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq);
>
> static void set_delayed(struct sched_entity *se)
> {
> + /*
> + * Drop a task leaving the runnable set. Must run before sched_delayed
> + * is set, or task_pref_llc_runnable() would already exclude it;
> + * clear_delayed() mirrors this after clearing the flag.
> + */
> + if (entity_is_task(se))
> + pref_llc_running_dec(rq_of(cfs_rq_of(se)), task_of(se));
> +
> se->sched_delayed = 1;
>
> /*
With flat pick stuff only task can get delayed. Now, I think we merged
the cache aware stuff last cycle, so it we backport this (see that Fixes
thing) then this must stay. But then also please make a note to take out
this condition again later.