[RFC PATCH 08/10] arm_mpam: resctrl: Expose the capacity controls as pseudo resources

From: Jinjie Ruan

Date: Thu Oct 22 2026 - 22:48:58 EST


Expose the MPAM capacity controls as L3MAX/L2MAX and L3MIN/L2MIN:
pick_caches() gives each pseudo resource the class picked for its base
cache, gated on the corresponding feature bit. Sharing the class means
the domains and components are shared, and only the mpam_config field
written differs.

control_init() describes them as RESCTRL_SCHEMA_RANGE.
resctrl_arch_update_one() and resctrl_arch_get_config() carry the
CMAX/CMIN values through mpam_config, reusing the fixed-point helpers
with cmax_wd.

Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
---
drivers/resctrl/mpam_resctrl.c | 81 ++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index dcdbf0404f07..5f423b7a8e41 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -756,6 +756,12 @@ static u32 mbw_max_to_percent(u16 mbw_max, struct mpam_props *cprops)
return __mbw_max_to_percent(mbw_max, cprops->bwa_wd);
}

+/* CMAX/CMIN use the same fixed-point format, but cmax_wd bits wide */
+static u32 ca_max_to_percent(u16 ca_max, struct mpam_props *cprops)
+{
+ return __mbw_max_to_percent(ca_max, cprops->cmax_wd);
+}
+
/*
* Find the band whose upper bound is closest to the specified percentage.
*
@@ -783,6 +789,12 @@ static u16 percent_to_mbw_max(u8 pc, struct mpam_props *cprops)
return __percent_to_mbw_max(pc, cprops->bwa_wd);
}

+/* CMAX/CMIN use the same fixed-point format, but cmax_wd bits wide */
+static u16 percent_to_ca_max(u8 pc, struct mpam_props *cprops)
+{
+ return __percent_to_mbw_max(pc, cprops->cmax_wd);
+}
+
static u32 get_mba_min(struct mpam_props *cprops)
{
if (!mba_class_use_mbw_max(cprops)) {
@@ -975,6 +987,18 @@ static void mpam_resctrl_pick_caches(void)
else
res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
res->class = class;
+
+ /*
+ * Re-use the same class for the pseudo resources exposing the
+ * extra controls: the components backing the domains are the
+ * same, only the applied config field differs.
+ */
+ if (mpam_has_feature(mpam_feat_cmax_cmax, &class->props))
+ mpam_resctrl_controls[class->level == 2 ?
+ RDT_RESOURCE_L2_MAX : RDT_RESOURCE_L3_MAX].class = class;
+ if (mpam_has_feature(mpam_feat_cmax_cmin, &class->props))
+ mpam_resctrl_controls[class->level == 2 ?
+ RDT_RESOURCE_L2_MIN : RDT_RESOURCE_L3_MIN].class = class;
}
}

@@ -1249,6 +1273,41 @@ static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
r->name = "MB";
r->alloc_capable = true;
break;
+ case RDT_RESOURCE_L3_MAX:
+ case RDT_RESOURCE_L2_MAX:
+ r->schema_fmt = RESCTRL_SCHEMA_RANGE;
+ r->ctrl_scope = (r->rid == RDT_RESOURCE_L2_MAX) ?
+ RESCTRL_L2_CACHE : RESCTRL_L3_CACHE;
+
+ r->membw.delay_linear = true;
+ r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
+ r->membw.min_bw = 1;
+ r->membw.max_bw = MAX_MBA_BW;
+ /*
+ * bw_validate() rounds up to bw_gran unconditionally, keep
+ * every percentage valid as the underlying fraction is
+ * converted with round-to-nearest anyway.
+ */
+ r->membw.bw_gran = 1;
+
+ r->name = (r->rid == RDT_RESOURCE_L2_MAX) ? "L2MAX" : "L3MAX";
+ r->alloc_capable = true;
+ break;
+ case RDT_RESOURCE_L3_MIN:
+ case RDT_RESOURCE_L2_MIN:
+ r->schema_fmt = RESCTRL_SCHEMA_RANGE;
+ r->ctrl_scope = (r->rid == RDT_RESOURCE_L2_MIN) ?
+ RESCTRL_L2_CACHE : RESCTRL_L3_CACHE;
+
+ r->membw.delay_linear = true;
+ r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
+ r->membw.min_bw = 0;
+ r->membw.max_bw = MAX_MBA_BW;
+ r->membw.bw_gran = 1;
+
+ r->name = (r->rid == RDT_RESOURCE_L2_MIN) ? "L2MIN" : "L3MIN";
+ r->alloc_capable = true;
+ break;
default:
return -EINVAL;
}
@@ -1413,6 +1472,14 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
case RDT_RESOURCE_L3:
configured_by = mpam_feat_cpor_part;
break;
+ case RDT_RESOURCE_L3_MAX:
+ case RDT_RESOURCE_L2_MAX:
+ configured_by = mpam_feat_cmax_cmax;
+ break;
+ case RDT_RESOURCE_L3_MIN:
+ case RDT_RESOURCE_L2_MIN:
+ configured_by = mpam_feat_cmax_cmin;
+ break;
case RDT_RESOURCE_MBA:
if (mpam_has_feature(mpam_feat_mbw_max, cprops)) {
configured_by = mpam_feat_mbw_max;
@@ -1430,6 +1497,10 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
switch (configured_by) {
case mpam_feat_cpor_part:
return cfg->cpbm;
+ case mpam_feat_cmax_cmax:
+ return ca_max_to_percent(cfg->cmax, cprops);
+ case mpam_feat_cmax_cmin:
+ return ca_max_to_percent(cfg->cmin, cprops);
case mpam_feat_mbw_max:
return mbw_max_to_percent(cfg->mbw_max, cprops);
default:
@@ -1482,6 +1553,16 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d,
cfg.cpbm = cfg_val;
mpam_set_feature(mpam_feat_cpor_part, &cfg);
break;
+ case RDT_RESOURCE_L3_MAX:
+ case RDT_RESOURCE_L2_MAX:
+ cfg.cmax = percent_to_ca_max(cfg_val, cprops);
+ mpam_set_feature(mpam_feat_cmax_cmax, &cfg);
+ break;
+ case RDT_RESOURCE_L3_MIN:
+ case RDT_RESOURCE_L2_MIN:
+ cfg.cmin = percent_to_ca_max(cfg_val, cprops);
+ mpam_set_feature(mpam_feat_cmax_cmin, &cfg);
+ break;
case RDT_RESOURCE_MBA:
if (mpam_has_feature(mpam_feat_mbw_max, cprops)) {
cfg.mbw_max = percent_to_mbw_max(cfg_val, cprops);
--
2.34.1