[RFC PATCH 7/8] x86/resctrl: Check for and enable asymmetric L2 cache allocation

From: Tony Luck

Date: Mon Oct 26 2026 - 17:58:43 EST


Systems with asymmetric L2 allocation do not enumerate using CPUID
leaf 0x10, they use leaf 0x28 instead.

Add a check for asymmetric allocation support (and update the check
for symmetric support to use cpu_feature_enabled()).

Enumerate the per-domain cache bit mask length for asymmetric L2
cache implementations. Rename the resource to "L2A" to avoid
confusing legacy code that only understands symmetric L2 cache.

The only asymmetric L2 feature of note is the cache bit mask length.
Other features are assumed to be symmetric.

Flag the resource as asymmetric for use by file system code.

Signed-off-by: Tony Luck <tony.luck@xxxxxxxxx>
---
include/linux/resctrl.h | 2 ++
arch/x86/kernel/cpu/resctrl/core.c | 26 ++++++++++++++++++++++++--
2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index 8ac799d1feff..ef1c96b8f679 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -495,6 +495,7 @@ enum resctrl_ctrl_mode {
* @mon_domains: RCU list of all monitor domains for this resource
* @name: Name to use in "schemata" file.
* @cdp_capable: Is the CDP feature available on this resource
+ * @asymmetric: Are domains in the resource asymmetric
* @bw_throttle_mode: Bandwidth throttling mode when threads request
* different memory bandwidths
* @cache_io_alloc_capable:True if portion of the cache can be configured
@@ -513,6 +514,7 @@ struct rdt_resource {
struct list_head mon_domains;
char *name;
bool cdp_capable;
+ bool asymmetric;
enum membw_throttle_mode bw_throttle_mode;
bool cache_io_alloc_capable;
enum resctrl_ctrl_mode ctrl_mode;
diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c
index f6b7358e50d7..4e2fdc7e9fe9 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -348,6 +348,16 @@ static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r)
return true;
}

+static int rdt_get_l2_asym_cbm_len(void)
+{
+ union cpuid_0x10_1_eax eax;
+ u32 ebx, ecx, edx;
+
+ cpuid_count(CPUID_LEAF_RDT_A_ASYM, 2, &eax.full, &ebx, &ecx, &edx);
+
+ return eax.split.cbm_len + 1;
+}
+
static void rdt_get_cache_alloc_cfg(int leaf, int idx, struct rdt_resource *r)
{
struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r);
@@ -596,7 +606,10 @@ static void rdt_sync_ctrl_bitmap(struct rdt_resource *r, struct rdt_ctrl_domain
for_each_resource_ctrl(ctrl, r) {
if (ctrl->type != RESCTRL_CTRL_BITMAP)
continue;
- d->cbm_len = ctrl->bitmap.cbm_len;
+ if (r->asymmetric)
+ d->cbm_len = rdt_get_l2_asym_cbm_len();
+ else
+ d->cbm_len = ctrl->bitmap.cbm_len;
break;
}
}
@@ -1073,7 +1086,7 @@ static __init bool get_rdt_alloc_resources(void)
if (rdt_alloc_capable)
return true;

- if (!boot_cpu_has(X86_FEATURE_RDT_A))
+ if (!cpu_feature_enabled(X86_FEATURE_RDT_A) && !cpu_feature_enabled(X86_FEATURE_RDT_A_ASYM))
return false;

if (rdt_cpu_has(X86_FEATURE_CAT_L3)) {
@@ -1092,6 +1105,15 @@ static __init bool get_rdt_alloc_resources(void)
if (rdt_cpu_has(X86_FEATURE_CDP_L2))
rdt_get_cdp_l2_config();
ret = true;
+ } else if (rdt_cpu_has(X86_FEATURE_ASYM_CAT_L2)) {
+ /* CPUID 0x28.2 fields are same format as 0x10.1 */
+ r = &rdt_resources_all[RDT_RESOURCE_L2].r_resctrl;
+ rdt_get_cache_alloc_cfg(CPUID_LEAF_RDT_A_ASYM, 2, r);
+ if (rdt_cpu_has(X86_FEATURE_ASYM_CDP_L2))
+ rdt_get_cdp_l2_config();
+ r->asymmetric = true;
+ r->name = "L2A";
+ ret = true;
}

if (get_mem_config())
--
2.56.0