Re: [PATCH v9 05/11] sched/fair: Load balance only among preferred CPUs

Yury Norov <[email protected]> Fri, 24 Jul 2026 17:40:26 -0400
Newsgroups dev.linux.lists.virtualization,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel
Message-ID <amPbythM-IBt6j_e@yury>
On Fri, Jul 24, 2026 at 07:37:26PM +0530, Shrikanth Hegde wrote:
> When cpu is marked as non preferred, any load pulled towards it is
> pointless since in the next tick task will be pushed out again.
> So, Consider only preferred CPUs for load balance.
> 
> This makes it not fight against the push task mechanism which happens
> at tick. Also, this stops active balance to happen on non-preferred CPU
> pulling the load.
> 
> This means there is no load balancing if the task is pinned only to
> non-preferred CPUs. They will continue to run where they were previously
> running before the CPUs was marked as non-preferred.
> 
> Bailout early for NEWIDLE and IDLE balance as load balancing is done
> only on preferred CPUs.
> 
> Signed-off-by: Shrikanth Hegde <[email protected]>
> ---
>  kernel/sched/fair.c | 11 +++++------
>  1 file changed, 5 insertions(+), 6 deletions(-)
> 
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index df8c9c2c7918..12f5b7de28d2 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -13399,7 +13399,7 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq,
>  	};
>  	bool need_unlock = false;
>  
> -	cpumask_and(cpus, sched_domain_span(sd), cpu_active_mask);
> +	cpumask_and(cpus, sched_domain_span(sd), cpu_preferred_mask);
>  
>  	schedstat_inc(sd->lb_count[idle]);
>  
> @@ -14337,7 +14337,8 @@ static void _nohz_idle_balance(struct rq *this_rq, unsigned int flags)
>  			update_rq_clock(rq);
>  			rq_unlock_irqrestore(rq, &rf);
>  
> -			if (flags & NOHZ_BALANCE_KICK)
> +			if (flags & NOHZ_BALANCE_KICK &&
> +			    cpu_preferred(balance_cpu))
>  				sched_balance_domains(rq, CPU_IDLE);

Here you skip the sched_balance_domains() for idle non-preferred CPUs,
which means you don't re-calculate the rq->next_balance.

In the following code, we update the global nohz.next_balance
depending on the new rq->next_balance, which doesn't happen if
the sched_balance_domains() is not invoked.

So, you propagate an already-expired per-CPU deadline back into the
global NOHZ deadline. It may lead to unneeded asynchronous IPI in the
nohz_balancer_kick() -> kick_ilb() path.

I think, you need another helper, something like:

        void sched_balance_domains(rq, idle)
        {
                __sched_balance_domains(rq, idle);
                advance_rq_next_balance(rq, idle);
        }

And then in the code above:

        if (flags & NOHZ_BALANCE_KICK) {
                if (cpu_preferred(balance_cpu))
                        __sched_balance_domains(rq, CPU_IDLE);

                advance_rq_next_balance(rq, idle);
        }

> @@ -14481,10 +14482,8 @@ static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
>  	 */
>  	this_rq->idle_stamp = rq_clock(this_rq);
>  
> -	/*
> -	 * Do not pull tasks towards !active CPUs...
> -	 */
> -	if (!cpu_active(this_cpu))
> +	/* Do not pull tasks towards !preferred CPUs */
> +	if (!cpu_preferred(this_cpu))
>  		return 0;
>  
>  	/*
> -- 
> 2.47.3