In the generic load balance(non-cache-aware-load-balance),
if the busiest runqueue has only one task, active balancing may be
invoked to move it. However, this migration might break LLC locality.
Before migration, check whether the task is running on its preferred
LLC: Do not move a lone task to another LLC if it would move the task
away from its preferred LLC or cause excessive imbalance between LLCs.
On the other hand, if the migration type is migrate_llc_task, it means
that there are tasks on the env->src_cpu that want to be migrated to
their preferred LLC, launch the active load balance anyway.
Co-developed-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Chen Yu <yu.c.chen@intel.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
---
Notes:
v2->v3:
Remove redundant rcu read lock in break_llc_locality().
kernel/sched/fair.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 1697791ef11c..03959a701514 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -9999,12 +9999,60 @@ static __maybe_unused enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu
task_util(p), to_pref);
}
+/*
+ * Check if active load balance breaks LLC locality in
+ * terms of cache aware load balance.
+ */
+static inline bool
+alb_break_llc(struct lb_env *env)
+{
+ if (!sched_cache_enabled())
+ return false;
+
+ if (cpus_share_cache(env->src_cpu, env->dst_cpu))
+ return false;
+ /*
+ * All tasks prefer to stay on their current CPU.
+ * Do not pull a task from its preferred CPU if:
+ * 1. It is the only task running there; OR
+ * 2. Migrating it away from its preferred LLC would violate
+ * the cache-aware scheduling policy.
+ */
+ if (env->src_rq->nr_pref_llc_running &&
+ env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable) {
+ unsigned long util = 0;
+ struct task_struct *cur;
+
+ if (env->src_rq->nr_running <= 1)
+ return true;
+
+ /*
+ * Reach here in load balance with
+ * rcu_read_lock() protected.
+ */
+ cur = rcu_dereference(env->src_rq->curr);
+ if (cur)
+ util = task_util(cur);
+
+ if (can_migrate_llc(env->src_cpu, env->dst_cpu,
+ util, false) == mig_forbid)
+ return true;
+ }
+
+ return false;
+}
#else
static inline bool get_llc_stats(int cpu, unsigned long *util,
unsigned long *cap)
{
return false;
}
+
+static inline bool
+alb_break_llc(struct lb_env *env)
+{
+ return false;
+}
#endif
/*
* can_migrate_task - may task p from runqueue rq be migrated to this_cpu?
@@ -12421,6 +12469,9 @@ static int need_active_balance(struct lb_env *env)
{
struct sched_domain *sd = env->sd;
+ if (alb_break_llc(env))
+ return 0;
+
if (asym_active_balance(env))
return 1;
@@ -12440,7 +12491,8 @@ static int need_active_balance(struct lb_env *env)
return 1;
}
- if (env->migration_type == migrate_misfit)
+ if (env->migration_type == migrate_misfit ||
+ env->migration_type == migrate_llc_task)
return 1;
return 0;
--
2.32.0
On Tue, Feb 10, 2026 at 02:18:53PM -0800, Tim Chen wrote:
> In the generic load balance(non-cache-aware-load-balance),
> if the busiest runqueue has only one task, active balancing may be
> invoked to move it. However, this migration might break LLC locality.
I'm thinking this wants more explanation; yes it might break locality,
but why is that bad?
This way you're inhibiting a full LLC from being able to power down.
> +/*
> + * Check if active load balance breaks LLC locality in
> + * terms of cache aware load balance.
> + */
> +static inline bool
> +alb_break_llc(struct lb_env *env)
> +{
> + if (!sched_cache_enabled())
> + return false;
> +
> + if (cpus_share_cache(env->src_cpu, env->dst_cpu))
> + return false;
> + /*
> + * All tasks prefer to stay on their current CPU.
> + * Do not pull a task from its preferred CPU if:
> + * 1. It is the only task running there; OR
> + * 2. Migrating it away from its preferred LLC would violate
> + * the cache-aware scheduling policy.
> + */
> + if (env->src_rq->nr_pref_llc_running &&
> + env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable) {
> + unsigned long util = 0;
> + struct task_struct *cur;
> +
> + if (env->src_rq->nr_running <= 1)
> + return true;
> +
> + /*
> + * Reach here in load balance with
> + * rcu_read_lock() protected.
> + */
Not sure that comment helps much. rcu_dereference() itself will already
cause complaints if the constraints are violated.
> + cur = rcu_dereference(env->src_rq->curr);
> + if (cur)
> + util = task_util(cur);
> +
> + if (can_migrate_llc(env->src_cpu, env->dst_cpu,
> + util, false) == mig_forbid)
> + return true;
> + }
> +
> + return false;
> +}
On Fri, 2026-02-20 at 14:53 +0100, Peter Zijlstra wrote:
> On Tue, Feb 10, 2026 at 02:18:53PM -0800, Tim Chen wrote:
> > In the generic load balance(non-cache-aware-load-balance),
> > if the busiest runqueue has only one task, active balancing may be
> > invoked to move it. However, this migration might break LLC locality.
>
> I'm thinking this wants more explanation; yes it might break locality,
> but why is that bad?
That's to prevent regular load balance from migrating a task that
prefers the current LLC, but the load level and imbalance doesn't warrant
breaking LLC preference per can_migrate_llc() policy.
Okay, will add more comments.
Tim
>
> This way you're inhibiting a full LLC from being able to power down.
>
> > +/*
> > + * Check if active load balance breaks LLC locality in
> > + * terms of cache aware load balance.
> > + */
> > +static inline bool
> > +alb_break_llc(struct lb_env *env)
> > +{
> > + if (!sched_cache_enabled())
> > + return false;
> > +
> > + if (cpus_share_cache(env->src_cpu, env->dst_cpu))
> > + return false;
> > + /*
> > + * All tasks prefer to stay on their current CPU.
> > + * Do not pull a task from its preferred CPU if:
> > + * 1. It is the only task running there; OR
> > + * 2. Migrating it away from its preferred LLC would violate
> > + * the cache-aware scheduling policy.
> > + */
> > + if (env->src_rq->nr_pref_llc_running &&
> > + env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable) {
> > + unsigned long util = 0;
> > + struct task_struct *cur;
> > +
> > + if (env->src_rq->nr_running <= 1)
> > + return true;
> > +
> > + /*
> > + * Reach here in load balance with
> > + * rcu_read_lock() protected.
> > + */
>
> Not sure that comment helps much. rcu_dereference() itself will already
> cause complaints if the constraints are violated.
Okay, will remove this.
>
> > + cur = rcu_dereference(env->src_rq->curr);
> > + if (cur)
> > + util = task_util(cur);
> > +
> > + if (can_migrate_llc(env->src_cpu, env->dst_cpu,
> > + util, false) == mig_forbid)
> > + return true;
> > + }
> > +
> > + return false;
> > +}
>
>
On 11/02/26 03:48, Tim Chen wrote:
> In the generic load balance(non-cache-aware-load-balance),
> if the busiest runqueue has only one task, active balancing may be
> invoked to move it. However, this migration might break LLC locality.
>
> Before migration, check whether the task is running on its preferred
> LLC: Do not move a lone task to another LLC if it would move the task
> away from its preferred LLC or cause excessive imbalance between LLCs.
>
> On the other hand, if the migration type is migrate_llc_task, it means
> that there are tasks on the env->src_cpu that want to be migrated to
> their preferred LLC, launch the active load balance anyway.
Nit:
But the check of migrate_llc_task is made after checking alb_break_llc
which seems to be contradicting. I understand that this check
env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable
prevents alb_break_llc to return true when migrate_llc_task exists. However,
checking migrate_llc_task first would make the priority and intent more
explicit.
Thanks,
Vineeth
>
> Co-developed-by: Chen Yu <yu.c.chen@intel.com>
> Signed-off-by: Chen Yu <yu.c.chen@intel.com>
> Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
> ---
>
> Notes:
> v2->v3:
> Remove redundant rcu read lock in break_llc_locality().
>
> kernel/sched/fair.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 53 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> index 1697791ef11c..03959a701514 100644
> --- a/kernel/sched/fair.c
> +++ b/kernel/sched/fair.c
> @@ -9999,12 +9999,60 @@ static __maybe_unused enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu
> task_util(p), to_pref);
> }
>
> +/*
> + * Check if active load balance breaks LLC locality in
> + * terms of cache aware load balance.
> + */
> +static inline bool
> +alb_break_llc(struct lb_env *env)
> +{
> + if (!sched_cache_enabled())
> + return false;
> +
> + if (cpus_share_cache(env->src_cpu, env->dst_cpu))
> + return false;
> + /*
> + * All tasks prefer to stay on their current CPU.
> + * Do not pull a task from its preferred CPU if:
> + * 1. It is the only task running there; OR
> + * 2. Migrating it away from its preferred LLC would violate
> + * the cache-aware scheduling policy.
> + */
> + if (env->src_rq->nr_pref_llc_running &&
> + env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable) {
> + unsigned long util = 0;
> + struct task_struct *cur;
> +
> + if (env->src_rq->nr_running <= 1)
> + return true;
> +
> + /*
> + * Reach here in load balance with
> + * rcu_read_lock() protected.
> + */
> + cur = rcu_dereference(env->src_rq->curr);
> + if (cur)
> + util = task_util(cur);
> +
> + if (can_migrate_llc(env->src_cpu, env->dst_cpu,
> + util, false) == mig_forbid)
> + return true;
> + }
> +
> + return false;
> +}
> #else
> static inline bool get_llc_stats(int cpu, unsigned long *util,
> unsigned long *cap)
> {
> return false;
> }
> +
> +static inline bool
> +alb_break_llc(struct lb_env *env)
> +{
> + return false;
> +}
> #endif
> /*
> * can_migrate_task - may task p from runqueue rq be migrated to this_cpu?
> @@ -12421,6 +12469,9 @@ static int need_active_balance(struct lb_env *env)
> {
> struct sched_domain *sd = env->sd;
>
> + if (alb_break_llc(env))
> + return 0;
> +
> if (asym_active_balance(env))
> return 1;
>
> @@ -12440,7 +12491,8 @@ static int need_active_balance(struct lb_env *env)
> return 1;
> }
>
> - if (env->migration_type == migrate_misfit)
> + if (env->migration_type == migrate_misfit ||
> + env->migration_type == migrate_llc_task)
> return 1;
>
> return 0;
On Wed, 2026-02-18 at 00:30 +0530, Madadi Vineeth Reddy wrote:
> On 11/02/26 03:48, Tim Chen wrote:
> > In the generic load balance(non-cache-aware-load-balance),
> > if the busiest runqueue has only one task, active balancing may be
> > invoked to move it. However, this migration might break LLC locality.
> >
> > Before migration, check whether the task is running on its preferred
> > LLC: Do not move a lone task to another LLC if it would move the task
> > away from its preferred LLC or cause excessive imbalance between LLCs.
> >
> > On the other hand, if the migration type is migrate_llc_task, it means
> > that there are tasks on the env->src_cpu that want to be migrated to
> > their preferred LLC, launch the active load balance anyway.
>
> Nit:
> But the check of migrate_llc_task is made after checking alb_break_llc
> which seems to be contradicting. I understand that this check
>
> env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable
>
> prevents alb_break_llc to return true when migrate_llc_task exists. However,
> checking migrate_llc_task first would make the priority and intent more
> explicit.
We have actually considered that.
Suppose we do migrate_llc_task check first, we still have to check that
this migration will not cause load balance to go beyond the load
imbalance we allow with can_migrate_llc(), i.e. do the same
check as in alb_break_llc().
Then for other kinds of task migration, we also need to check
that those migrations don't break LLC policy with alb_break_llc().
So it is better to just do the alb_break_llc() check first to
cover all migration types.
It doesn't really help to save any code by moving migrate_llc_task
check up.
Tim
>
> Thanks,
> Vineeth
>
> >
> > Co-developed-by: Chen Yu <yu.c.chen@intel.com>
> > Signed-off-by: Chen Yu <yu.c.chen@intel.com>
> > Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
> > ---
> >
> > Notes:
> > v2->v3:
> > Remove redundant rcu read lock in break_llc_locality().
> >
> > kernel/sched/fair.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 53 insertions(+), 1 deletion(-)
> >
> > diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
> > index 1697791ef11c..03959a701514 100644
> > --- a/kernel/sched/fair.c
> > +++ b/kernel/sched/fair.c
> > @@ -9999,12 +9999,60 @@ static __maybe_unused enum llc_mig can_migrate_llc_task(int src_cpu, int dst_cpu
> > task_util(p), to_pref);
> > }
> >
> > +/*
> > + * Check if active load balance breaks LLC locality in
> > + * terms of cache aware load balance.
> > + */
> > +static inline bool
> > +alb_break_llc(struct lb_env *env)
> > +{
> > + if (!sched_cache_enabled())
> > + return false;
> > +
> > + if (cpus_share_cache(env->src_cpu, env->dst_cpu))
> > + return false;
> > + /*
> > + * All tasks prefer to stay on their current CPU.
> > + * Do not pull a task from its preferred CPU if:
> > + * 1. It is the only task running there; OR
> > + * 2. Migrating it away from its preferred LLC would violate
> > + * the cache-aware scheduling policy.
> > + */
> > + if (env->src_rq->nr_pref_llc_running &&
> > + env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable) {
> > + unsigned long util = 0;
> > + struct task_struct *cur;
> > +
> > + if (env->src_rq->nr_running <= 1)
> > + return true;
> > +
> > + /*
> > + * Reach here in load balance with
> > + * rcu_read_lock() protected.
> > + */
> > + cur = rcu_dereference(env->src_rq->curr);
> > + if (cur)
> > + util = task_util(cur);
> > +
> > + if (can_migrate_llc(env->src_cpu, env->dst_cpu,
> > + util, false) == mig_forbid)
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > #else
> > static inline bool get_llc_stats(int cpu, unsigned long *util,
> > unsigned long *cap)
> > {
> > return false;
> > }
> > +
> > +static inline bool
> > +alb_break_llc(struct lb_env *env)
> > +{
> > + return false;
> > +}
> > #endif
> > /*
> > * can_migrate_task - may task p from runqueue rq be migrated to this_cpu?
> > @@ -12421,6 +12469,9 @@ static int need_active_balance(struct lb_env *env)
> > {
> > struct sched_domain *sd = env->sd;
> >
> > + if (alb_break_llc(env))
> > + return 0;
> > +
> > if (asym_active_balance(env))
> > return 1;
> >
> > @@ -12440,7 +12491,8 @@ static int need_active_balance(struct lb_env *env)
> > return 1;
> > }
> >
> > - if (env->migration_type == migrate_misfit)
> > + if (env->migration_type == migrate_misfit ||
> > + env->migration_type == migrate_llc_task)
> > return 1;
> >
> > return 0;
>
© 2016 - 2026 Red Hat, Inc.