* [PATCH RFC v1 1/6] resctrl: wire MB_MAXHLM schemata parsing and group setup
2026-11-09 21:29 [PATCH RFC v1 0/6] arm_mpam,resctrl: expose MPAM MBW_MAX hard limit Fenghua Yu
@ 2026-11-09 21:29 ` Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 2/6] arm_mpam: resctrl: probe MPAMF_MBW_IDR MAX_LIM and hardlim_rw Fenghua Yu
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Fenghua Yu @ 2026-11-09 21:29 UTC (permalink / raw)
To: Reinette Chatre, Tony Luck, Ben Horgan, James Morse, Will Deacon,
Dave Martin, Catalin Marinas, Shaopeng Tan, Chen Yu, Babu Moger,
Drew Fustini, Vikram Sethi, Shanker Donthineni, Newton Liu,
Richard Cheng
Cc: linux-kernel, linux-arm-kernel, Fenghua Yu
The hard-limit control is a 0/1 value on the MBA resource, not a
separate schemata resource. Parse MB_MAXHLM in the bandwidth parser,
reject values other than 0 and 1, and initialize new groups with the
hard limit off.
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
fs/resctrl/ctrlmondata.c | 35 +++++++++++++++++++++++++++++++++++
fs/resctrl/rdtgroup.c | 17 +++++++++++++++++
include/linux/resctrl.h | 8 +++++++-
3 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c
index 77a2f2e877a6..9111b0130783 100644
--- a/fs/resctrl/ctrlmondata.c
+++ b/fs/resctrl/ctrlmondata.c
@@ -62,6 +62,29 @@ static struct resctrl_ctrl_priv resctrl_ctrl_priv_all[] = {
},
};
+static bool mb_maxhlm_validate(char *buf, u32 *data, struct rdt_resource *r,
+ struct resctrl_ctrl *ctrl)
+{
+ int ret;
+ u32 mb_maxhlm;
+
+ ret = kstrtou32(buf, 10, &mb_maxhlm);
+ if (ret) {
+ rdt_last_cmd_printf("Invalid MB_MAXHLM value %s\n", buf);
+ return false;
+ }
+
+ if (mb_maxhlm != 0 && mb_maxhlm != 1) {
+ rdt_last_cmd_printf("MB_MAXHLM value %u isn't 0/1\n",
+ mb_maxhlm);
+ return false;
+ }
+
+ *data = mb_maxhlm;
+
+ return true;
+}
+
/*
* Iterator finding enabled controls.
* Dynamically traverses controls based on active control mode.
@@ -165,6 +188,17 @@ static int parse_bw(struct rdt_parse_data *data, struct rdt_resource_final *f,
return -EINVAL;
}
+ /* Parse mb_maxhlm line */
+ if (ctrl->name == RESCTRL_CTRL_NAME_MAXHLM) {
+ if (!mb_maxhlm_validate(data->buf, &bw_val, r, ctrl))
+ return -EINVAL;
+
+ cfg->new_ctrl = bw_val;
+ cfg->have_new_ctrl = true;
+ return 0;
+ }
+
+ /* Parse mb line */
if (!bw_validate(data->buf, &bw_val, r, ctrl))
return -EINVAL;
@@ -362,6 +396,7 @@ static const char * const resctrl_ctrl_name[] = {
[RESCTRL_CTRL_NAME_NODE] = "NODE",
[RESCTRL_CTRL_NAME_REGION0_MIN] = "REGION0_MIN",
[RESCTRL_CTRL_NAME_REGION0_MAX] = "REGION0_MAX",
+ [RESCTRL_CTRL_NAME_MAXHLM] = "MAXHLM",
};
const char *resctrl_ctrl_name_str(enum resctrl_ctrl_name name)
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index a16e35b587c4..01e69d0d9e96 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -4401,6 +4401,19 @@ static void rdtgroup_init_mba(struct rdt_resource *r, struct resctrl_ctrl *ctrl,
}
}
+/* Initialize the MB MAXHLM control with default hardlim off (0). */
+static void rdtgroup_init_mb_hlim(struct resctrl_ctrl *ctrl)
+{
+ struct resctrl_staged_config *cfg;
+ struct rdt_ctrl_domain *d;
+
+ list_for_each_entry(d, &ctrl->domains, hdr.list) {
+ cfg = &d->staged_config[CDP_NONE];
+ cfg->new_ctrl = 0;
+ cfg->have_new_ctrl = true;
+ }
+}
+
/* Initialize the RDT group's allocations. */
static int rdtgroup_init_alloc(struct rdtgroup *rdtgrp)
{
@@ -4416,6 +4429,10 @@ static int rdtgroup_init_alloc(struct rdtgroup *rdtgrp)
for_each_enabled_ctrl(ctrl, r) {
if (r->rid == RDT_RESOURCE_MBA ||
r->rid == RDT_RESOURCE_SMBA) {
+ if (ctrl->name == RESCTRL_CTRL_NAME_MAXHLM) {
+ rdtgroup_init_mb_hlim(ctrl);
+ continue;
+ }
rdtgroup_init_mba(r, ctrl, rdtgrp->closid);
if (is_mba_sc(r, ctrl))
continue;
diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index 3c4675656b51..5711154145f8 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -402,6 +402,7 @@ struct resctrl_mon {
* line is "MB_NODE".
* @RESCTRL_CTRL_NAME_REGION0_MIN: "REGION0_MIN"
* @RESCTRL_CTRL_NAME_REGION0_MAX: "REGION0_MAX"
+ * @RESCTRL_CTRL_NAME_MAXHLM: "MAXHLM"
*/
enum resctrl_ctrl_name {
RESCTRL_CTRL_NAME_DEF,
@@ -410,7 +411,8 @@ enum resctrl_ctrl_name {
RESCTRL_CTRL_NAME_NODE,
RESCTRL_CTRL_NAME_REGION0_MIN,
RESCTRL_CTRL_NAME_REGION0_MAX,
- RESCTRL_CTRL_NAME_LAST = RESCTRL_CTRL_NAME_REGION0_MAX
+ RESCTRL_CTRL_NAME_MAXHLM,
+ RESCTRL_CTRL_NAME_LAST = RESCTRL_CTRL_NAME_MAXHLM
};
/**
@@ -616,6 +618,10 @@ static inline u32 resctrl_get_default_ctrlval(struct resctrl_ctrl *ctrl)
*/
return BIT_MASK(ctrl->bitmap.cbm_len) - 1;
case RESCTRL_CTRL_SCALAR:
+ /* The hard-limit toggle defaults to off. */
+ if (ctrl->name == RESCTRL_CTRL_NAME_MAXHLM)
+ return 0;
+
return ctrl->scalar.reset_val;
}
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH RFC v1 2/6] arm_mpam: resctrl: probe MPAMF_MBW_IDR MAX_LIM and hardlim_rw
2026-11-09 21:29 [PATCH RFC v1 0/6] arm_mpam,resctrl: expose MPAM MBW_MAX hard limit Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 1/6] resctrl: wire MB_MAXHLM schemata parsing and group setup Fenghua Yu
@ 2026-11-09 21:29 ` Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 3/6] arm_mpam: resctrl: track MBW max hard-limit in config Fenghua Yu
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Fenghua Yu @ 2026-11-09 21:29 UTC (permalink / raw)
To: Reinette Chatre, Tony Luck, Ben Horgan, James Morse, Will Deacon,
Dave Martin, Catalin Marinas, Shaopeng Tan, Chen Yu, Babu Moger,
Drew Fustini, Vikram Sethi, Shanker Donthineni, Newton Liu,
Richard Cheng
Cc: linux-kernel, linux-arm-kernel, Fenghua Yu
Before resctrl can expose MB_MAXHLM, the driver must discover
whether the platform supports HARDLIM read/write and what
MPAMF_MBW_IDR.MAX_LIM reports. MSCs on the same system may differ;
inconsistent merge would expose controls on hardware that cannot honour
them.
Read mbw_max_lim from MPAMF_MBW_IDR when MBW_MAX is present, derive
mpam_feat_mbw_max_hardlim_rw when soft and hard limiting are both
supported, merge capability across MSCs, and keep the merged view in
sync via mpam_props_sync_mbw_max_hardlim_rw().
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
drivers/resctrl/mpam_devices.c | 40 ++++++++++++++++++++++++++++++++-
drivers/resctrl/mpam_internal.h | 7 ++++++
2 files changed, 46 insertions(+), 1 deletion(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index cb6096e9d2cf..1e56e8f3858e 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -830,6 +830,19 @@ static bool mpam_ris_hw_probe_csu_nrdy(struct mpam_msc_ris *ris)
return (!can_set || !can_clear);
}
+/* Align mpam_feat_mbw_max_hardlim_rw with MPAMF_MBW_IDR.MAX_LIM and mbw_max. */
+static void mpam_props_sync_mbw_max_hardlim_rw(struct mpam_props *props)
+{
+ if (!mpam_has_feature(mpam_feat_mbw_max, props)) {
+ mpam_clear_feature(mpam_feat_mbw_max_hardlim_rw, props);
+ return;
+ }
+ if (props->mbw_max_lim == 0)
+ mpam_set_feature(mpam_feat_mbw_max_hardlim_rw, props);
+ else
+ mpam_clear_feature(mpam_feat_mbw_max_hardlim_rw, props);
+}
+
static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
{
int err;
@@ -877,6 +890,8 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) {
u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR);
+ props->mbw_max_lim = 0;
+
/* portion bitmap resolution */
props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features);
if (props->mbw_pbm_bits &&
@@ -891,14 +906,18 @@ static void 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)) {
mpam_set_feature(mpam_feat_mbw_max, props);
+ props->mbw_max_lim = FIELD_GET(MPAMF_MBW_IDR_MAX_LIM, mbw_features);
+ }
if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MIN, mbw_features))
mpam_set_feature(mpam_feat_mbw_min, props);
if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_PROP, mbw_features))
mpam_set_feature(mpam_feat_mbw_prop, props);
+
+ mpam_props_sync_mbw_max_hardlim_rw(props);
}
/* Priority partitioning */
@@ -2252,12 +2271,31 @@ static void __props_mismatch(struct mpam_props *parent,
if (alias && !mpam_has_bwa_wd_feature(parent) &&
mpam_has_bwa_wd_feature(child)) {
parent->bwa_wd = child->bwa_wd;
+ parent->mbw_max_lim = child->mbw_max_lim;
+ if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, child))
+ mpam_set_feature(mpam_feat_mbw_max_hardlim_rw, parent);
+ else
+ mpam_clear_feature(mpam_feat_mbw_max_hardlim_rw, parent);
} else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature,
bwa_wd, alias)) {
pr_debug("took the min bwa_wd\n");
parent->bwa_wd = min(parent->bwa_wd, child->bwa_wd);
}
+ if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_max, alias)) {
+ parent->mbw_max_lim = child->mbw_max_lim;
+ if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, child))
+ mpam_set_feature(mpam_feat_mbw_max_hardlim_rw, parent);
+ else
+ mpam_clear_feature(mpam_feat_mbw_max_hardlim_rw, parent);
+ } else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_max,
+ mbw_max_lim, alias)) {
+ pr_debug("%s mbw_max_lim mismatch, clearing mbw_max\n", __func__);
+ mpam_clear_feature(mpam_feat_mbw_max, parent);
+ parent->mbw_max_lim = 0;
+ mpam_props_sync_mbw_max_hardlim_rw(parent);
+ }
+
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,
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 1c9e0e8b78a9..6be32b2cb0ab 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -172,6 +172,7 @@ enum mpam_device_features {
mpam_feat_mbw_part,
mpam_feat_mbw_min,
mpam_feat_mbw_max,
+ mpam_feat_mbw_max_hardlim_rw,
mpam_feat_mbw_prop,
mpam_feat_intpri_part,
mpam_feat_intpri_part_0_low,
@@ -203,6 +204,11 @@ struct mpam_props {
u16 dspri_wd;
u16 num_csu_mon;
u16 num_mbwu_mon;
+ /*
+ * MPAMF_MBW_IDR.MAX_LIM [1:0] when mpam_feat_mbw_max; else 0.
+ * 0 = soft+hard, 1 = soft only, 2 = hard only, 3 = reserved.
+ */
+ u8 mbw_max_lim;
/*
* Kunit tests use memset() to set up feature combinations that should be
@@ -585,6 +591,7 @@ static inline void mpam_resctrl_teardown_class(struct mpam_class *class) { }
/* 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)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH RFC v1 3/6] arm_mpam: resctrl: track MBW max hard-limit in config
2026-11-09 21:29 [PATCH RFC v1 0/6] arm_mpam,resctrl: expose MPAM MBW_MAX hard limit Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 1/6] resctrl: wire MB_MAXHLM schemata parsing and group setup Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 2/6] arm_mpam: resctrl: probe MPAMF_MBW_IDR MAX_LIM and hardlim_rw Fenghua Yu
@ 2026-11-09 21:29 ` Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 4/6] arm_mpam: resctrl: add MAXHLM control on the MBA resource Fenghua Yu
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Fenghua Yu @ 2026-11-09 21:29 UTC (permalink / raw)
To: Reinette Chatre, Tony Luck, Ben Horgan, James Morse, Will Deacon,
Dave Martin, Catalin Marinas, Shaopeng Tan, Chen Yu, Babu Moger,
Drew Fustini, Vikram Sethi, Shanker Donthineni, Newton Liu,
Richard Cheng
Cc: linux-kernel, linux-arm-kernel, Fenghua Yu
Once userspace sets MB_MAXHLM schemata, the driver must program the
HARDLIM bit in MPAMCFG_MBW_MAX. Without tracking the per-domain hard-
limit state in mpam_config, bandwidth reprogramming would ignore the
schemata and leave hard limits unchanged in hardware.
Add mbw_max_hardlim to mpam_config and set MPAMCFG_MBW_MAX bit 31
(HARDLIM) when reprogramming MBW_MAX on hardware that supports hard
limiting.
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
drivers/resctrl/mpam_devices.c | 27 ++++++++++++++++++++++++---
drivers/resctrl/mpam_internal.h | 1 +
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 1e56e8f3858e..7dee6617b134 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1664,10 +1664,29 @@ static void 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))
- mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max);
- else
+ if (mpam_has_feature(mpam_feat_mbw_max, cfg) ||
+ mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, cfg)) {
+ u32 mbw_val;
+
+ /*
+ * HARDLIM shares MPAMCFG_MBW_MAX with the bandwidth
+ * field. A hard-limit update does not program
+ * bandwidth: the shadow value is not live until an
+ * MB write, and reset leaves the register at
+ * MPAMCFG_MBW_MAX_MAX. Keep that programmed value.
+ */
+ if (mpam_has_feature(mpam_feat_mbw_max, cfg))
+ mbw_val = cfg->mbw_max;
+ else
+ mbw_val = MPAMCFG_MBW_MAX_MAX;
+
+ if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, cfg) &&
+ cfg->mbw_max_hardlim)
+ mbw_val |= MPAMCFG_MBW_MAX_HARDLIM;
+ mpam_write_partsel_reg(msc, MBW_MAX, mbw_val);
+ } else {
mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX);
+ }
}
if (mpam_has_feature(mpam_feat_mbw_prop, rprops))
@@ -2986,6 +3005,8 @@ 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_mbw_max_hardlim_rw, newcfg,
+ mbw_max_hardlim, has_changes);
return has_changes;
}
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 6be32b2cb0ab..59d04cb663da 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -324,6 +324,7 @@ struct mpam_config {
u32 cpbm;
u32 mbw_pbm;
u16 mbw_max;
+ bool mbw_max_hardlim;
struct mpam_garbage garbage;
};
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH RFC v1 4/6] arm_mpam: resctrl: add MAXHLM control on the MBA resource
2026-11-09 21:29 [PATCH RFC v1 0/6] arm_mpam,resctrl: expose MPAM MBW_MAX hard limit Fenghua Yu
` (2 preceding siblings ...)
2026-11-09 21:29 ` [PATCH RFC v1 3/6] arm_mpam: resctrl: track MBW max hard-limit in config Fenghua Yu
@ 2026-11-09 21:29 ` Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 5/6] arm_mpam: Add MB_MAXHLM_NODE and Node scope Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 6/6] Documentation: resctrl: document MB_MAXHLM for MPAM MBA Fenghua Yu
5 siblings, 0 replies; 7+ messages in thread
From: Fenghua Yu @ 2026-11-09 21:29 UTC (permalink / raw)
To: Reinette Chatre, Tony Luck, Ben Horgan, James Morse, Will Deacon,
Dave Martin, Catalin Marinas, Shaopeng Tan, Chen Yu, Babu Moger,
Drew Fustini, Vikram Sethi, Shanker Donthineni, Newton Liu,
Richard Cheng
Cc: linux-kernel, linux-arm-kernel, Fenghua Yu
MB_MAXHLM toggles the HARDLIM field of the same MPAM bandwidth
hardware as MBA. Create that control on the MBA resource when
mpam_feat_mbw_max_hardlim_rw is present, and read and write it
through resctrl_arch_get_config() and resctrl_arch_update_one().
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
drivers/resctrl/mpam_resctrl.c | 154 ++++++++++++++++++++++++++-------
1 file changed, 121 insertions(+), 33 deletions(-)
diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 8a169676c4ac..ea1fb7fc7e01 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -1266,20 +1266,95 @@ void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *
resctrl_arch_reset_cntr(r, d, closid, rmid, cntr_id, evtid);
}
+static void _mpam_resctrl_ctrl_init_mba(struct mpam_resctrl_ctrl *mpam_ctrl,
+ struct mpam_props *cprops,
+ enum resctrl_ctrl_name name)
+{
+ mpam_ctrl->r_ctrl.type = RESCTRL_CTRL_SCALAR;
+ mpam_ctrl->r_ctrl.name = name;
+ INIT_LIST_HEAD_RCU(&mpam_ctrl->r_ctrl.domains);
+
+ /*
+ * emulated_by is left empty so legacy and native mode expose
+ * the same schemata line. A memory-class MSC is node-scoped,
+ * so that line is MB_NODE. A cache MSC keeps the MB line.
+ */
+ INIT_LIST_HEAD(&mpam_ctrl->r_ctrl.emulated_by);
+
+ mpam_ctrl->r_ctrl.scalar.min = get_mba_min(cprops);
+ mpam_ctrl->r_ctrl.scalar.max = MAX_MBA_BW;
+ mpam_ctrl->r_ctrl.scalar.reset_val = MAX_MBA_BW;
+ mpam_ctrl->r_ctrl.scalar.gran = get_mba_granularity(cprops);
+
+ __set_bit(RESCTRL_SCALAR_FLAG_LINEAR, mpam_ctrl->r_ctrl.scalar.flags);
+}
+
+static int mpam_resctrl_ctrl_init_mba(struct rdt_resource *r,
+ struct mpam_props *cprops)
+{
+ struct mpam_resctrl_ctrl *mpam_ctrl, *ctrl_maxhlm = NULL;
+ struct mpam_resctrl_res *res;
+ struct mpam_class *class;
+
+ mpam_ctrl = kzalloc_obj(*mpam_ctrl);
+ if (!mpam_ctrl)
+ return -ENOMEM;
+
+ r->ctrl_scope = RESCTRL_L3_CACHE;
+ r->ctrl_mode = RESCTRL_CTRL_MODE_LEGACY;
+ res = container_of(r, struct mpam_resctrl_res, resctrl_res);
+ class = res->class;
+ if (mpam_class_memory(class)) {
+ _mpam_resctrl_ctrl_init_mba(mpam_ctrl, cprops,
+ RESCTRL_CTRL_NAME_NODE);
+ } else {
+ _mpam_resctrl_ctrl_init_mba(mpam_ctrl, cprops,
+ RESCTRL_CTRL_NAME_DEF);
+ }
+
+ /*
+ * Allocate the hard-limit control before either control is
+ * linked. mpam_resctrl_setup() does not free r->controls when
+ * this fails.
+ */
+ if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, cprops)) {
+ ctrl_maxhlm = kzalloc_obj(*ctrl_maxhlm);
+ if (!ctrl_maxhlm) {
+ kfree(mpam_ctrl);
+ return -ENOMEM;
+ }
+
+ _mpam_resctrl_ctrl_init_mba(ctrl_maxhlm, cprops,
+ RESCTRL_CTRL_NAME_MAXHLM);
+ }
+
+ /*
+ * Link the bandwidth control first. list_add() inserts at the
+ * head, so a combined schemata write programs HARDLIM before
+ * the bandwidth field.
+ */
+ list_add(&mpam_ctrl->r_ctrl.entry, &r->controls);
+ if (ctrl_maxhlm)
+ list_add(&ctrl_maxhlm->r_ctrl.entry, &r->controls);
+
+ return 0;
+}
+
static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
{
struct mpam_class *class = res->class;
struct mpam_props *cprops = &class->props;
struct rdt_resource *r = &res->resctrl_res;
struct mpam_resctrl_ctrl *mpam_ctrl;
-
- mpam_ctrl = kzalloc_obj(*mpam_ctrl);
- if (!mpam_ctrl)
- return -ENOMEM;
+ int ret = 0;
switch (r->rid) {
case RDT_RESOURCE_L2:
case RDT_RESOURCE_L3:
+ mpam_ctrl = kzalloc_obj(*mpam_ctrl);
+ if (!mpam_ctrl)
+ return -ENOMEM;
+
mpam_ctrl->r_ctrl.type = RESCTRL_CTRL_BITMAP;
mpam_ctrl->r_ctrl.name = RESCTRL_CTRL_NAME_DEF;
INIT_LIST_HEAD_RCU(&mpam_ctrl->r_ctrl.domains);
@@ -1310,29 +1385,11 @@ static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
r->alloc_capable = true;
break;
case RDT_RESOURCE_MBA:
- r->ctrl_scope = RESCTRL_L3_CACHE;
- /*
- * control_mode stays legacy. emulated_by is left empty so
- * legacy and native mode expose the same schemata line.
- * A memory-class MSC is node-scoped, so that line is
- * MB_NODE. A cache MSC keeps the MB line.
- */
- r->ctrl_mode = RESCTRL_CTRL_MODE_LEGACY;
- mpam_ctrl->r_ctrl.type = RESCTRL_CTRL_SCALAR;
- if (mpam_class_memory(class))
- mpam_ctrl->r_ctrl.name = RESCTRL_CTRL_NAME_NODE;
- else
- mpam_ctrl->r_ctrl.name = RESCTRL_CTRL_NAME_DEF;
- INIT_LIST_HEAD_RCU(&mpam_ctrl->r_ctrl.domains);
- INIT_LIST_HEAD(&mpam_ctrl->r_ctrl.emulated_by);
+ ret = mpam_resctrl_ctrl_init_mba(r, cprops);
+ if (ret)
+ return ret;
r->bw_throttle_mode = THREAD_THROTTLE_UNDEFINED;
- __set_bit(RESCTRL_SCALAR_FLAG_LINEAR, mpam_ctrl->r_ctrl.scalar.flags);
- mpam_ctrl->r_ctrl.scalar.min = get_mba_min(cprops);
- mpam_ctrl->r_ctrl.scalar.max = MAX_MBA_BW;
- mpam_ctrl->r_ctrl.scalar.reset_val = MAX_MBA_BW;
- mpam_ctrl->r_ctrl.scalar.gran = get_mba_granularity(cprops);
- list_add(&mpam_ctrl->r_ctrl.entry, &r->controls);
r->name = "MB";
r->alloc_capable = true;
@@ -1524,11 +1581,19 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct resctrl_ctrl *ctrl,
configured_by = mpam_feat_cpor_part;
break;
case RDT_RESOURCE_MBA:
- if (mpam_has_feature(mpam_feat_mbw_max, cprops)) {
- configured_by = mpam_feat_mbw_max;
+ switch (ctrl->name) {
+ case RESCTRL_CTRL_NAME_DEF:
+ case RESCTRL_CTRL_NAME_NODE:
+ if (mpam_has_feature(mpam_feat_mbw_max, cprops))
+ configured_by = mpam_feat_mbw_max;
+ break;
+ case RESCTRL_CTRL_NAME_MAXHLM:
+ configured_by = mpam_feat_mbw_max_hardlim_rw;
+ break;
+ default:
break;
}
- fallthrough;
+ break;
default:
return resctrl_get_default_ctrlval(ctrl);
}
@@ -1542,6 +1607,8 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct resctrl_ctrl *ctrl,
return cfg->cpbm;
case mpam_feat_mbw_max:
return mbw_max_to_percent(cfg->mbw_max, cprops);
+ case mpam_feat_mbw_max_hardlim_rw:
+ return cfg->mbw_max_hardlim;
default:
return resctrl_get_default_ctrlval(ctrl);
}
@@ -1594,12 +1661,33 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct resctrl_ctrl *ctrl,
mpam_set_feature(mpam_feat_cpor_part, &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);
- mpam_set_feature(mpam_feat_mbw_max, &cfg);
- break;
+ switch (ctrl->name) {
+ case RESCTRL_CTRL_NAME_DEF:
+ case RESCTRL_CTRL_NAME_NODE:
+ 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);
+ break;
+ }
+ return -EINVAL;
+ case RESCTRL_CTRL_NAME_MAXHLM:
+ if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, cprops) &&
+ mpam_has_feature(mpam_feat_mbw_max, cprops)) {
+ /*
+ * Submit HARDLIM only. Marking mbw_max here
+ * would program the shadow bandwidth into the
+ * same register and replace the live value.
+ * A previous MB write is already in cfg.
+ */
+ cfg.mbw_max_hardlim = cfg_val != 0;
+ mpam_set_feature(mpam_feat_mbw_max_hardlim_rw, &cfg);
+ break;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
}
- fallthrough;
+ break;
default:
return -EINVAL;
}
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH RFC v1 5/6] arm_mpam: Add MB_MAXHLM_NODE and Node scope
2026-11-09 21:29 [PATCH RFC v1 0/6] arm_mpam,resctrl: expose MPAM MBW_MAX hard limit Fenghua Yu
` (3 preceding siblings ...)
2026-11-09 21:29 ` [PATCH RFC v1 4/6] arm_mpam: resctrl: add MAXHLM control on the MBA resource Fenghua Yu
@ 2026-11-09 21:29 ` Fenghua Yu
2026-11-09 21:29 ` [PATCH RFC v1 6/6] Documentation: resctrl: document MB_MAXHLM for MPAM MBA Fenghua Yu
5 siblings, 0 replies; 7+ messages in thread
From: Fenghua Yu @ 2026-11-09 21:29 UTC (permalink / raw)
To: Reinette Chatre, Tony Luck, Ben Horgan, James Morse, Will Deacon,
Dave Martin, Catalin Marinas, Shaopeng Tan, Chen Yu, Babu Moger,
Drew Fustini, Vikram Sethi, Shanker Donthineni, Newton Liu,
Richard Cheng
Cc: linux-kernel, linux-arm-kernel, Fenghua Yu
Memory-level MSCs expose their domains as NUMA nodes, so their
hard-limit control must be named MB_MAXHLM_NODE rather than MB_MAXHLM.
Add RESCTRL_CTRL_NAME_MAXHLM_NODE and create it for memory classes.
Set the MBA resource ctrl_scope to RESCTRL_NODE for those classes.
Add resctrl_ctrl_maxhlm() so the schemata parser, group setup, default
value, and resctrl_arch_get_config()/resctrl_arch_update_one() accept
both hard-limit names.
The hard-limit control is a 0/1 HARDLIM toggle. Set its scalar range
to 0..1 with a reset value of 0 instead of inheriting the bandwidth
percentage range.
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
drivers/resctrl/mpam_resctrl.c | 47 ++++++++++++++++++++++++++++++----
fs/resctrl/ctrlmondata.c | 3 ++-
fs/resctrl/rdtgroup.c | 2 +-
include/linux/resctrl.h | 12 +++++++--
4 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index ea1fb7fc7e01..f1325ffb6343 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -1266,10 +1266,14 @@ void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *
resctrl_arch_reset_cntr(r, d, closid, rmid, cntr_id, evtid);
}
-static void _mpam_resctrl_ctrl_init_mba(struct mpam_resctrl_ctrl *mpam_ctrl,
+static void _mpam_resctrl_ctrl_init_mba(struct rdt_resource *r,
+ struct mpam_resctrl_ctrl *mpam_ctrl,
struct mpam_props *cprops,
enum resctrl_ctrl_name name)
{
+ struct mpam_resctrl_res *res;
+ struct mpam_class *class;
+
mpam_ctrl->r_ctrl.type = RESCTRL_CTRL_SCALAR;
mpam_ctrl->r_ctrl.name = name;
INIT_LIST_HEAD_RCU(&mpam_ctrl->r_ctrl.domains);
@@ -1287,6 +1291,35 @@ static void _mpam_resctrl_ctrl_init_mba(struct mpam_resctrl_ctrl *mpam_ctrl,
mpam_ctrl->r_ctrl.scalar.gran = get_mba_granularity(cprops);
__set_bit(RESCTRL_SCALAR_FLAG_LINEAR, mpam_ctrl->r_ctrl.scalar.flags);
+
+ /*
+ * The hard-limit control is a 0/1 HARDLIM toggle. It must not
+ * inherit the bandwidth percentage range.
+ */
+ if (resctrl_ctrl_maxhlm(&mpam_ctrl->r_ctrl)) {
+ mpam_ctrl->r_ctrl.scalar.min = 0;
+ mpam_ctrl->r_ctrl.scalar.max = 1;
+ mpam_ctrl->r_ctrl.scalar.reset_val = 0;
+ mpam_ctrl->r_ctrl.scalar.gran = 1;
+ }
+
+ res = container_of(r, struct mpam_resctrl_res, resctrl_res);
+ class = res->class;
+ if (mpam_class_memory(class))
+ r->ctrl_scope = RESCTRL_NODE;
+}
+
+static enum resctrl_ctrl_name get_ctrl_name_maxhlm(struct rdt_resource *r)
+{
+ struct mpam_resctrl_res *res;
+ struct mpam_class *class;
+
+ res = container_of(r, struct mpam_resctrl_res, resctrl_res);
+ class = res->class;
+ if (mpam_class_memory(class))
+ return RESCTRL_CTRL_NAME_MAXHLM_NODE;
+
+ return RESCTRL_CTRL_NAME_MAXHLM;
}
static int mpam_resctrl_ctrl_init_mba(struct rdt_resource *r,
@@ -1305,10 +1338,10 @@ static int mpam_resctrl_ctrl_init_mba(struct rdt_resource *r,
res = container_of(r, struct mpam_resctrl_res, resctrl_res);
class = res->class;
if (mpam_class_memory(class)) {
- _mpam_resctrl_ctrl_init_mba(mpam_ctrl, cprops,
+ _mpam_resctrl_ctrl_init_mba(r, mpam_ctrl, cprops,
RESCTRL_CTRL_NAME_NODE);
} else {
- _mpam_resctrl_ctrl_init_mba(mpam_ctrl, cprops,
+ _mpam_resctrl_ctrl_init_mba(r, mpam_ctrl, cprops,
RESCTRL_CTRL_NAME_DEF);
}
@@ -1318,14 +1351,16 @@ static int mpam_resctrl_ctrl_init_mba(struct rdt_resource *r,
* this fails.
*/
if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, cprops)) {
+ enum resctrl_ctrl_name name;
+
ctrl_maxhlm = kzalloc_obj(*ctrl_maxhlm);
if (!ctrl_maxhlm) {
kfree(mpam_ctrl);
return -ENOMEM;
}
- _mpam_resctrl_ctrl_init_mba(ctrl_maxhlm, cprops,
- RESCTRL_CTRL_NAME_MAXHLM);
+ name = get_ctrl_name_maxhlm(r);
+ _mpam_resctrl_ctrl_init_mba(r, ctrl_maxhlm, cprops, name);
}
/*
@@ -1588,6 +1623,7 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct resctrl_ctrl *ctrl,
configured_by = mpam_feat_mbw_max;
break;
case RESCTRL_CTRL_NAME_MAXHLM:
+ case RESCTRL_CTRL_NAME_MAXHLM_NODE:
configured_by = mpam_feat_mbw_max_hardlim_rw;
break;
default:
@@ -1671,6 +1707,7 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct resctrl_ctrl *ctrl,
}
return -EINVAL;
case RESCTRL_CTRL_NAME_MAXHLM:
+ case RESCTRL_CTRL_NAME_MAXHLM_NODE:
if (mpam_has_feature(mpam_feat_mbw_max_hardlim_rw, cprops) &&
mpam_has_feature(mpam_feat_mbw_max, cprops)) {
/*
diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c
index 9111b0130783..352a3f847b4b 100644
--- a/fs/resctrl/ctrlmondata.c
+++ b/fs/resctrl/ctrlmondata.c
@@ -189,7 +189,7 @@ static int parse_bw(struct rdt_parse_data *data, struct rdt_resource_final *f,
}
/* Parse mb_maxhlm line */
- if (ctrl->name == RESCTRL_CTRL_NAME_MAXHLM) {
+ if (resctrl_ctrl_maxhlm(ctrl)) {
if (!mb_maxhlm_validate(data->buf, &bw_val, r, ctrl))
return -EINVAL;
@@ -397,6 +397,7 @@ static const char * const resctrl_ctrl_name[] = {
[RESCTRL_CTRL_NAME_REGION0_MIN] = "REGION0_MIN",
[RESCTRL_CTRL_NAME_REGION0_MAX] = "REGION0_MAX",
[RESCTRL_CTRL_NAME_MAXHLM] = "MAXHLM",
+ [RESCTRL_CTRL_NAME_MAXHLM_NODE] = "MAXHLM_NODE",
};
const char *resctrl_ctrl_name_str(enum resctrl_ctrl_name name)
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 01e69d0d9e96..1a6dbad15fd5 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -4429,7 +4429,7 @@ static int rdtgroup_init_alloc(struct rdtgroup *rdtgrp)
for_each_enabled_ctrl(ctrl, r) {
if (r->rid == RDT_RESOURCE_MBA ||
r->rid == RDT_RESOURCE_SMBA) {
- if (ctrl->name == RESCTRL_CTRL_NAME_MAXHLM) {
+ if (resctrl_ctrl_maxhlm(ctrl)) {
rdtgroup_init_mb_hlim(ctrl);
continue;
}
diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index 5711154145f8..1c75ac68d68f 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -403,6 +403,7 @@ struct resctrl_mon {
* @RESCTRL_CTRL_NAME_REGION0_MIN: "REGION0_MIN"
* @RESCTRL_CTRL_NAME_REGION0_MAX: "REGION0_MAX"
* @RESCTRL_CTRL_NAME_MAXHLM: "MAXHLM"
+ * @RESCTRL_CTRL_NAME_MAXHLM_NODE:"MAXHLM_NODE"
*/
enum resctrl_ctrl_name {
RESCTRL_CTRL_NAME_DEF,
@@ -412,7 +413,8 @@ enum resctrl_ctrl_name {
RESCTRL_CTRL_NAME_REGION0_MIN,
RESCTRL_CTRL_NAME_REGION0_MAX,
RESCTRL_CTRL_NAME_MAXHLM,
- RESCTRL_CTRL_NAME_LAST = RESCTRL_CTRL_NAME_MAXHLM
+ RESCTRL_CTRL_NAME_MAXHLM_NODE,
+ RESCTRL_CTRL_NAME_LAST = RESCTRL_CTRL_NAME_MAXHLM_NODE
};
/**
@@ -603,6 +605,12 @@ int resctrl_arch_control_mode_set(struct rdt_resource *r,
*/
void resctrl_arch_sync_cpu_closid_rmid(void *info);
+static inline bool resctrl_ctrl_maxhlm(struct resctrl_ctrl *ctrl)
+{
+ return ctrl->name == RESCTRL_CTRL_NAME_MAXHLM ||
+ ctrl->name == RESCTRL_CTRL_NAME_MAXHLM_NODE;
+}
+
/**
* resctrl_get_default_ctrlval() - Return the default control value for this
* control.
@@ -619,7 +627,7 @@ static inline u32 resctrl_get_default_ctrlval(struct resctrl_ctrl *ctrl)
return BIT_MASK(ctrl->bitmap.cbm_len) - 1;
case RESCTRL_CTRL_SCALAR:
/* The hard-limit toggle defaults to off. */
- if (ctrl->name == RESCTRL_CTRL_NAME_MAXHLM)
+ if (resctrl_ctrl_maxhlm(ctrl))
return 0;
return ctrl->scalar.reset_val;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH RFC v1 6/6] Documentation: resctrl: document MB_MAXHLM for MPAM MBA
2026-11-09 21:29 [PATCH RFC v1 0/6] arm_mpam,resctrl: expose MPAM MBW_MAX hard limit Fenghua Yu
` (4 preceding siblings ...)
2026-11-09 21:29 ` [PATCH RFC v1 5/6] arm_mpam: Add MB_MAXHLM_NODE and Node scope Fenghua Yu
@ 2026-11-09 21:29 ` Fenghua Yu
5 siblings, 0 replies; 7+ messages in thread
From: Fenghua Yu @ 2026-11-09 21:29 UTC (permalink / raw)
To: Reinette Chatre, Tony Luck, Ben Horgan, James Morse, Will Deacon,
Dave Martin, Catalin Marinas, Shaopeng Tan, Chen Yu, Babu Moger,
Drew Fustini, Vikram Sethi, Shanker Donthineni, Newton Liu,
Richard Cheng
Cc: linux-kernel, linux-arm-kernel, Fenghua Yu
The hard-limit interface is the MB_MAXHLM or MB_MAXHLM_NODE schemata
line. HARDLIM is read/write only when MPAMF_MBW_IDR.MAX_LIM is zero.
Existing resctrl documentation covers percentage MBA only.
Document both schemata lines (0/1 per domain) and how they relate to
MPAM MBW_MAX, HARDLIM, and MPAMF_MBW_IDR.MAX_LIM.
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
Documentation/filesystems/resctrl.rst | 28 +++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/Documentation/filesystems/resctrl.rst b/Documentation/filesystems/resctrl.rst
index b7ffedc02b41..2335fb91e5c6 100644
--- a/Documentation/filesystems/resctrl.rst
+++ b/Documentation/filesystems/resctrl.rst
@@ -1198,6 +1198,34 @@ When MB domains are NUMA nodes::
MB_NODE:<node_id0>=bw_MiBps0;<node_id1>=bw_MiBps1;...
+MBW maximum hard limit (ARM MPAM)
+---------------------------------
+On some ARM systems, resctrl memory bandwidth allocation uses MPAM
+maximum bandwidth (MBW_MAX). When ``MPAMF_MBW_IDR.MAX_LIM`` is 0, an
+additional schemata line selects the ``HARDLIM`` bit for
+``MPAMCFG_MBW_MAX`` independently of the numeric limit on the bandwidth
+line (``MB`` or ``MB_NODE``).
+
+The line uses the same domain indices as the bandwidth line. On a
+cache MSC those indices are cache ids and the lines are ``MB`` and
+``MB_MAXHLM``. On a memory-level MSC those indices are NUMA node ids
+and the lines are ``MB_NODE`` and ``MB_MAXHLM_NODE``. Each hard-limit
+value must be ``0`` or ``1``: ``0`` clears HARDLIM (soft-limit behaviour
+for the max), ``1`` sets HARDLIM (hard limit). When ``MAX_LIM`` is
+nonzero or the platform does not support the hard-limit control, the
+line is omitted from ``schemata``.
+
+The control is listed under ``info/MB/schemata/`` as ``MB_MAXHLM`` or
+``MB_MAXHLM_NODE``.
+
+Format when MB domains are caches::
+
+ MB_MAXHLM:<cache_id0>=0|1;<cache_id1>=0|1;...
+
+Format when MB domains are NUMA nodes::
+
+ MB_MAXHLM_NODE:<node_id0>=0|1;<node_id1>=0|1;...
+
Slow Memory Bandwidth Allocation (SMBA)
---------------------------------------
AMD hardware supports Slow Memory Bandwidth Allocation (SMBA).
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread