sched/cache: Make LLC id continuous

Introduce an index mapping between CPUs and their LLCs. This provides
a roughly continuous per LLC index needed for cache-aware load balancing in
later patches.

The existing per_cpu llc_id usually points to the first CPU of the
LLC domain, which is sparse and unsuitable as an array index. Using
llc_id directly would waste memory.

With the new mapping, CPUs in the same LLC share an approximate
continuous id:

  per_cpu(llc_id, CPU=0...15)  = 0
  per_cpu(llc_id, CPU=16...31) = 1
  per_cpu(llc_id, CPU=32...47) = 2
  ...

Note that the LLC IDs are allocated via bitmask, so the IDs may be
reused during CPU offline->online transitions.

Suggested-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Originally-by: K Prateek Nayak <kprateek.nayak@amd.com>
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>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/047ef46339e4db497b54a89940a7ebedf27fcf28.1775065312.git.tim.c.chen@linux.intel.com
This commit is contained in:
Tim Chen
2026-04-01 14:52:17 -07:00
committed by Peter Zijlstra
parent 23b2b5ccc4
commit b5ea300a17
3 changed files with 93 additions and 2 deletions

View File

@@ -8630,6 +8630,8 @@ int sched_cpu_deactivate(unsigned int cpu)
*/
synchronize_rcu();
sched_domains_free_llc_id(cpu);
sched_set_rq_offline(rq, cpu);
scx_rq_deactivate(rq);

View File

@@ -4053,6 +4053,9 @@ static inline bool sched_cache_enabled(void)
return false;
}
#endif
void sched_domains_free_llc_id(int cpu);
extern void init_sched_mm(struct task_struct *p);
extern u64 avg_vruntime(struct cfs_rq *cfs_rq);

View File

@@ -19,8 +19,10 @@ void sched_domains_mutex_unlock(void)
}
/* Protected by sched_domains_mutex: */
static cpumask_var_t sched_domains_llc_id_allocmask;
static cpumask_var_t sched_domains_tmpmask;
static cpumask_var_t sched_domains_tmpmask2;
int max_lid;
static int __init sched_debug_setup(char *str)
{
@@ -663,7 +665,7 @@ static void destroy_sched_domains(struct sched_domain *sd)
*/
DEFINE_PER_CPU(struct sched_domain __rcu *, sd_llc);
DEFINE_PER_CPU(int, sd_llc_size);
DEFINE_PER_CPU(int, sd_llc_id);
DEFINE_PER_CPU(int, sd_llc_id) = -1;
DEFINE_PER_CPU(int, sd_share_id);
DEFINE_PER_CPU(struct sched_domain_shared __rcu *, sd_llc_shared);
DEFINE_PER_CPU(struct sched_domain __rcu *, sd_numa);
@@ -692,7 +694,6 @@ static void update_top_cache_domain(int cpu)
rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
per_cpu(sd_llc_size, cpu) = size;
per_cpu(sd_llc_id, cpu) = id;
rcu_assign_pointer(per_cpu(sd_llc_shared, cpu), sds);
sd = lowest_flag_domain(cpu, SD_CLUSTER);
@@ -1790,6 +1791,11 @@ const struct cpumask *tl_mc_mask(struct sched_domain_topology_level *tl, int cpu
{
return cpu_coregroup_mask(cpu);
}
#define llc_mask(cpu) cpu_coregroup_mask(cpu)
#else
#define llc_mask(cpu) cpumask_of(cpu)
#endif
const struct cpumask *tl_pkg_mask(struct sched_domain_topology_level *tl, int cpu)
@@ -2650,6 +2656,61 @@ static void adjust_numa_imbalance(struct sched_domain *sd_llc)
}
}
static int __sched_domains_alloc_llc_id(void)
{
int lid, max;
lockdep_assert_held(&sched_domains_mutex);
lid = cpumask_first_zero(sched_domains_llc_id_allocmask);
/*
* llc_id space should never grow larger than the
* possible number of CPUs in the system.
*/
if (lid >= nr_cpu_ids)
return -1;
__cpumask_set_cpu(lid, sched_domains_llc_id_allocmask);
max = cpumask_last(sched_domains_llc_id_allocmask);
if (max > max_lid)
max_lid = max;
return lid;
}
static void __sched_domains_free_llc_id(int cpu)
{
int i, lid, max;
lockdep_assert_held(&sched_domains_mutex);
lid = per_cpu(sd_llc_id, cpu);
if (lid == -1 || lid >= nr_cpu_ids)
return;
per_cpu(sd_llc_id, cpu) = -1;
for_each_cpu(i, llc_mask(cpu)) {
/* An online CPU owns the llc_id. */
if (per_cpu(sd_llc_id, i) == lid)
return;
}
__cpumask_clear_cpu(lid, sched_domains_llc_id_allocmask);
max = cpumask_last(sched_domains_llc_id_allocmask);
/* shrink max lid to save memory */
if (max < max_lid)
max_lid = max;
}
void sched_domains_free_llc_id(int cpu)
{
sched_domains_mutex_lock();
__sched_domains_free_llc_id(cpu);
sched_domains_mutex_unlock();
}
/*
* Build sched domains for a given set of CPUs and attach the sched domains
* to the individual CPUs
@@ -2675,6 +2736,7 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
/* Set up domains for CPUs specified by the cpu_map: */
for_each_cpu(i, cpu_map) {
struct sched_domain_topology_level *tl;
int lid;
sd = NULL;
for_each_sd_topology(tl) {
@@ -2688,6 +2750,29 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
if (cpumask_equal(cpu_map, sched_domain_span(sd)))
break;
}
lid = per_cpu(sd_llc_id, i);
if (lid == -1) {
/* try to reuse the llc_id of its siblings */
for (int j = cpumask_first(llc_mask(i));
j < nr_cpu_ids;
j = cpumask_next(j, llc_mask(i))) {
if (i == j)
continue;
lid = per_cpu(sd_llc_id, j);
if (lid != -1) {
per_cpu(sd_llc_id, i) = lid;
break;
}
}
/* a new LLC is detected */
if (lid == -1)
per_cpu(sd_llc_id, i) = __sched_domains_alloc_llc_id();
}
}
if (WARN_ON(!topology_span_sane(cpu_map)))
@@ -2831,6 +2916,7 @@ int __init sched_init_domains(const struct cpumask *cpu_map)
{
int err;
zalloc_cpumask_var(&sched_domains_llc_id_allocmask, GFP_KERNEL);
zalloc_cpumask_var(&sched_domains_tmpmask, GFP_KERNEL);
zalloc_cpumask_var(&sched_domains_tmpmask2, GFP_KERNEL);
zalloc_cpumask_var(&fallback_doms, GFP_KERNEL);