[RFC PATCH 6/6] arm_mpam: Program the hard limit for the software controller
From: Jinjie Ruan
Date: Mon Nov 09 2026 - 08:28:06 EST
From: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
MPAM's maximum bandwidth control has a HARDLIM bit (IHI 0099 B.d
Section 9.6.9): when set, a PARTID that uses more than its maximum gets
no further bandwidth; when clear, requests above the maximum are
serviced when no other request is waiting. The driver writes only the
16-bit maximum, so the control is a soft limit and the software
controller cannot keep a single requester below its target.
Carry the hard limit in mpam_config and set it when the controller is
enabled. MPAMF_MBW_IDR.MAX_LIM (Section 9.5) reports the implemented
behaviors: 00 both, 10 hard only, 01 soft only. Record the soft-only
case (and the reserved 0b11) in resctrl_membw::max_is_soft_limit so
mba_MBps is refused there. Merging resources into a class propagates
the soft-only case, as the hard limit is only usable when every
resource implements it.
The percentage interface never sets the hard limit, and resctrl clears
one set by the controller when it resets the controls on unmount.
Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
---
drivers/resctrl/mpam_devices.c | 55 ++++++++++++++++++++++++++-------
drivers/resctrl/mpam_internal.h | 4 +++
drivers/resctrl/mpam_resctrl.c | 9 ++++++
3 files changed, 57 insertions(+), 11 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index c067b6d57d71..2c8f60c96d3f 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1025,8 +1025,22 @@ static int mpam_ris_hw_probe(struct mpam_msc_ris *ris)
*/
props->bwa_wd = min(props->bwa_wd, 16);
- if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features))
+ if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) {
+ /*
+ * MAX_LIM: 0b00 both limits, 0b01 the soft limit
+ * only, 0b10 the hard limit only. 0b11 is reserved;
+ * treat it as the soft limit only so the software
+ * controller is refused rather than rely on an
+ * unknown limit behavior.
+ */
+ props->max_lim = FIELD_GET(MPAMF_MBW_IDR_MAX_LIM, mbw_features);
+ if (props->max_lim == 3)
+ props->max_lim = 1;
+
mpam_set_feature(mpam_feat_mbw_max, props);
+ /* The hard-limit bit is a configuration bit. */
+ mpam_set_feature(mpam_feat_max_limit, props);
+ }
if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MIN, mbw_features))
mpam_set_feature(mpam_feat_mbw_min, props);
@@ -1911,16 +1925,21 @@ static int mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
}
if (mpam_has_feature(mpam_feat_mbw_max, rprops)) {
- if (mpam_has_feature(mpam_feat_mbw_max, cfg)) {
- ret = mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max);
- if (ret)
- return ret;
- } else {
- ret = mpam_write_partsel_reg(msc, MBW_MAX,
- MPAMCFG_MBW_MAX_MAX);
- if (ret)
- return ret;
- }
+ u32 val = MPAMCFG_MBW_MAX_MAX;
+
+ if (mpam_has_feature(mpam_feat_mbw_max, cfg))
+ val = cfg->mbw_max;
+
+ /*
+ * The hard-limit bit follows its own feature bit so that it
+ * can be set without changing the maximum.
+ */
+ if (mpam_has_feature(mpam_feat_max_limit, cfg) && cfg->max_limit)
+ val |= MPAMCFG_MBW_MAX_HARDLIM;
+
+ ret = mpam_write_partsel_reg(msc, MBW_MAX, val);
+ if (ret)
+ return ret;
}
if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) {
@@ -2661,6 +2680,19 @@ static void __props_mismatch(struct mpam_props *parent,
parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd);
}
+ /*
+ * The hard limit is only usable if every resource implements it,
+ * so the soft limit only wins the merge. The encoding follows
+ * MPAMF_MBW_IDR.MAX_LIM.
+ */
+ if (alias && !mpam_has_feature(mpam_feat_mbw_max, parent) &&
+ mpam_has_feature(mpam_feat_mbw_max, child)) {
+ parent->max_lim = child->max_lim;
+ } else if (mpam_has_feature(mpam_feat_mbw_max, parent) &&
+ child->max_lim == 1) {
+ parent->max_lim = 1;
+ }
+
if (alias && !mpam_has_cmax_wd_feature(parent) && mpam_has_cmax_wd_feature(child)) {
parent->cmax_wd = child->cmax_wd;
} else if (MISMATCHED_HELPER(parent, child, mpam_has_cmax_wd_feature,
@@ -3405,6 +3437,7 @@ static bool mpam_update_config(struct mpam_config *cfg,
maybe_update_config(cfg, mpam_feat_cpor_part, newcfg, cpbm, has_changes);
maybe_update_config(cfg, mpam_feat_mbw_part, newcfg, mbw_pbm, has_changes);
maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, has_changes);
+ maybe_update_config(cfg, mpam_feat_max_limit, newcfg, max_limit, has_changes);
return has_changes;
}
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 1f86cc158100..184fbc01fa91 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -234,6 +234,7 @@ enum mpam_device_features {
mpam_feat_msmon_mbwu_capture,
mpam_feat_msmon_mbwu_rwbw,
mpam_feat_partid_nrw,
+ mpam_feat_max_limit,
MPAM_FEATURE_LAST
};
@@ -243,6 +244,7 @@ struct mpam_props {
u16 cpbm_wd;
u16 mbw_pbm_bits;
u16 bwa_wd;
+ u8 max_lim;
u16 cmax_wd;
u16 cassoc_wd;
u16 intpri_wd;
@@ -364,6 +366,7 @@ struct mpam_config {
u32 cpbm;
u32 mbw_pbm;
u16 mbw_max;
+ bool max_limit;
struct mpam_garbage garbage;
};
@@ -628,6 +631,7 @@ int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc);
/* MPAMF_MBW_IDR - MPAM features memory bandwidth partitioning ID register */
#define MPAMF_MBW_IDR_BWA_WD GENMASK(5, 0)
+#define MPAMF_MBW_IDR_MAX_LIM GENMASK(9, 8)
#define MPAMF_MBW_IDR_HAS_MIN BIT(10)
#define MPAMF_MBW_IDR_HAS_MAX BIT(11)
#define MPAMF_MBW_IDR_HAS_PBM BIT(12)
diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 5468367776e2..5f35bfe1955a 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -1236,6 +1236,7 @@ static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
r->ctrl_scope = RESCTRL_L3_CACHE;
r->membw.delay_linear = true;
+ r->membw.max_is_soft_limit = (cprops->max_lim == 1);
r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
r->membw.min_bw = get_mba_min(cprops);
r->membw.max_bw = MAX_MBA_BW;
@@ -1481,6 +1482,14 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d,
if (mpam_has_feature(mpam_feat_mbw_max, cprops)) {
cfg.mbw_max = percent_to_mbw_max(cfg_val, cprops);
mpam_set_feature(mpam_feat_mbw_max, &cfg);
+ /*
+ * Always carry the hard-limit bit so that writing the
+ * maximum clears a hard limit set by an earlier mount
+ * of the software controller.
+ */
+ mpam_set_feature(mpam_feat_max_limit, &cfg);
+ if (r->membw.mba_sc)
+ cfg.max_limit = true;
break;
}
fallthrough;
--
2.43.0