mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl
@ 2026-10-23  2:48 Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 01/10] arm_mpam: Treat adopting a config feature as a change Jinjie Ruan
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

Hi all,

Resctrl today only exposes CBM and the MBA bandwidth percentage.
But MPAM MSCs also have per-PARTID fields for internal priority, capacity
limits, a bandwidth minimum and the MBW_MAX hard-limit bit. The driver
already writes these fields, but resctrl has no schemata line for them,
so userspace can't choose the values.

So this series adds those schemata lines:

- Patch 1 fixes maybe_update_config(): a feature set with value zero
  counts as no change, so the register is not written again.
  The controls below can be zero, so this comes first.

- Patches 2-4 add mbw_min/max_limit, cmax/cmin and intpri to
  mpam_config and write them from mpam_reprogram_ris_partid().

- Patch 5 adds the resource levels, selects the range path by schema_fmt
  instead of the MBA/SMBA rid, skips the new resources in info/, and sets
  their default value when a group is created.

- Patches 6-7 adjust the fixed-point helpers and clamp cmax_wd.

- Patches 8-10 add the schemata lines:

  L2MAX/L3MAX       CMAX, percent
  L2MIN/L3MIN       CMIN, percent
  MBMIN             MBW_MIN, percent
  L2PRI/L3PRI/MBPRI INTPRI, raw value
  MBHDL             HARDLIM, 0 or 1
  MBOPT             MBW_MAX, raw fraction

The new resources reuse the class, domains and components of L2/L3/MB; only
the mpam_config field written differs. MBOPT and MBA both write MBW_MAX.

So before this series userspace can only set CBM and the MBA percentage.
After it, you can do:

  # echo "L3MAX:33751040=50" > schemata
  # echo "MBHDL:33751040=1" > schemata

A new group starts from each resource's default value, so the new controls
stay out of the way until schemata is written.

Tested this with a QEMU that has one L2 MSC, one L3 MSC and one
memory-controller MSC (CMAX_WD 4, INTPRI_WD 2, BWA_WD 16). The guest writes
every new line and reads it back, for example:

  WRITE-OK   [L3MAX:33751040=50]
  SCHEMATA: L3MAX:33751040=50

Then reads the MSC registers:

  L3 MSC  partid0: PRI(IN)=2  CMIN=0x3000  CMAX=0x7000
  mem MSC partid0: PRI(IN)=1  MBW_MIN=0x3fff  MBW_MAX=0x1fff  HARDLIM=Y

CMIN/CMAX are 4-bit fractions (3/16 for 25%, 7/16 for 50%); MBW_MIN is a
16-bit fraction (0x3fff for 25%); MBOPT writes the raw fraction (8191).

QEMU branch: https://github.com/ruanjinjie-eng/qemu/tree/mpam
Test cases: https://github.com/ruanjinjie-eng/mpam_testcase

This series is also available at:
	https://github.com/ruanjinjie-eng/linux/tree/mpam-extra-controls

Jinjie Ruan (10):
  arm_mpam: Treat adopting a config feature as a change
  arm_mpam: Carry mbw_min and the hard limit in mpam_config
  arm_mpam: Carry cmax and cmin in mpam_config
  arm_mpam: Carry intpri in mpam_config
  fs/resctrl: Prepare for range-format pseudo resources
  arm_mpam: Clamp cmax_wd to 16
  arm_mpam: Generalize the fixed-point conversion helpers
  arm_mpam: resctrl: Expose the capacity controls as pseudo resources
  arm_mpam: resctrl: Expose the priority controls as pseudo resources
  arm_mpam: resctrl: Expose the bandwidth minimum and limit as pseudo
    resources

 drivers/resctrl/mpam_devices.c  |  59 +++++++--
 drivers/resctrl/mpam_internal.h |   8 ++
 drivers/resctrl/mpam_resctrl.c  | 215 +++++++++++++++++++++++++++++++-
 fs/resctrl/rdtgroup.c           |  15 ++-
 include/linux/resctrl.h         |  33 ++++-
 5 files changed, 307 insertions(+), 23 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 01/10] arm_mpam: Treat adopting a config feature as a change
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 02/10] arm_mpam: Carry mbw_min and the hard limit in mpam_config Jinjie Ruan
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

maybe_update_config() only latched a feature when the new value differed
from the old one. Zero is a valid value for several controls, so a
configuration that adopts a feature with the reset value would be
treated as unchanged and the register would never be reprogrammed.

Always latch a feature the old config does not have, and reprogram when
the value changes as before. This is needed by the controls that follow,
which can legitimately be programmed with zero.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 drivers/resctrl/mpam_devices.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index dd422c56fbb1..0fbf3464e744 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -2897,8 +2897,15 @@ void mpam_enable(struct work_struct *work)
 		mpam_enable_once();
 }
 
+/*
+ * Adopting a feature is itself a change: 0 is a valid value for several
+ * controls, and writing it must reprogram the reset value, not just merge
+ * the (equal) number.
+ */
 #define maybe_update_config(cfg, feature, newcfg, member, changes) do { \
-	if (mpam_has_feature(feature, newcfg) &&			\
+	if (!mpam_has_feature(feature, newcfg))			\
+		break;							\
+	if (!mpam_has_feature(feature, cfg) ||			\
 	    (newcfg)->member != (cfg)->member) {			\
 		(cfg)->member = (newcfg)->member;			\
 		mpam_set_feature(feature, cfg);				\
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 02/10] arm_mpam: Carry mbw_min and the hard limit in mpam_config
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 01/10] arm_mpam: Treat adopting a config feature as a change Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 03/10] arm_mpam: Carry cmax and cmin " Jinjie Ruan
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

Add mbw_min and max_limit to mpam_config so the bandwidth minimum and
the MBW_MAX hard-limit bit can be programmed per PARTID through resctrl.

The hard limit is a config bit of MPAMCFG_MBW_MAX, not a discoverable
capability, so mpam_feat_max_limit is set alongside mpam_feat_mbw_max.
mpam_reprogram_ris_partid() writes the config's mbw_min when its feature
bit is set, and sets the hard-limit bit from its own feature bit so
MBHDL can be set without MBW_MAX being written.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 drivers/resctrl/mpam_devices.c  | 25 +++++++++++++++++++++----
 drivers/resctrl/mpam_internal.h |  3 +++
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 0fbf3464e744..806bf61b6dbe 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -848,8 +848,11 @@ 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);
+			/* The hard-limit bit is a config bit, not a capability */
+			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);
@@ -1586,6 +1589,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
 	if (mpam_has_feature(mpam_feat_mbw_min, rprops)) {
 		u16 val = 0;
 
+		if (mpam_has_feature(mpam_feat_mbw_min, cfg))
+			val = cfg->mbw_min;
+
 		if (mpam_has_quirk(T241_FORCE_MBW_MIN_TO_ONE, msc)) {
 			u16 min = mpam_wa_t241_force_mbw_min_to_one(rprops);
 
@@ -1597,10 +1603,19 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
 	}
 
 	if (mpam_has_feature(mpam_feat_mbw_max, rprops)) {
+		u32 val = MPAMCFG_MBW_MAX_MAX;
+
 		if (mpam_has_feature(mpam_feat_mbw_max, cfg))
-			mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max);
-		else
-			mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX);
+			val = cfg->mbw_max;
+
+		/*
+		 * The hard-limit bit follows its own feature bit: MBHDL can
+		 * be configured without MBW_MAX ever being written.
+		 */
+		if (mpam_has_feature(mpam_feat_max_limit, cfg) && cfg->max_limit)
+			val |= MPAMCFG_MBW_MAX_HARDLIM;
+
+		mpam_write_partsel_reg(msc, MBW_MAX, val);
 	}
 
 	if (mpam_has_feature(mpam_feat_mbw_prop, rprops))
@@ -2922,6 +2937,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_min, newcfg, mbw_min, 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 def0e3a65c23..f5efebde3c2c 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -188,6 +188,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
 };
 
@@ -318,6 +319,8 @@ struct mpam_config {
 	u32	cpbm;
 	u32	mbw_pbm;
 	u16	mbw_max;
+	u16	mbw_min;
+	bool	max_limit;
 
 	struct mpam_garbage	garbage;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 03/10] arm_mpam: Carry cmax and cmin in mpam_config
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 01/10] arm_mpam: Treat adopting a config feature as a change Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 02/10] arm_mpam: Carry mbw_min and the hard limit in mpam_config Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 04/10] arm_mpam: Carry intpri " Jinjie Ruan
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

Add cmax and cmin to mpam_config so the capacity hard and soft limits
can be programmed per PARTID through resctrl.

mpam_reprogram_ris_partid() writes the config's cmax/cmin when their
feature bits are set, and falls back to the reset values otherwise. The
local cmax reset value is no longer needed.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 drivers/resctrl/mpam_devices.c  | 19 ++++++++++++++-----
 drivers/resctrl/mpam_internal.h |  2 ++
 2 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 806bf61b6dbe..f5ac427a0100 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1553,7 +1553,6 @@ static u16 mpam_wa_t241_calc_min_from_max(struct mpam_props *props,
 static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
 				      struct mpam_config *cfg)
 {
-	u16 cmax = MPAMCFG_CMAX_CMAX;
 	struct mpam_msc *msc = ris->vmsc->msc;
 	struct mpam_props *rprops = &ris->props;
 
@@ -1621,11 +1620,19 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
 	if (mpam_has_feature(mpam_feat_mbw_prop, rprops))
 		mpam_write_partsel_reg(msc, MBW_PROP, 0);
 
-	if (mpam_has_feature(mpam_feat_cmax_cmax, rprops))
-		mpam_write_partsel_reg(msc, CMAX, cmax);
+	if (mpam_has_feature(mpam_feat_cmax_cmax, rprops)) {
+		if (mpam_has_feature(mpam_feat_cmax_cmax, cfg))
+			mpam_write_partsel_reg(msc, CMAX, cfg->cmax);
+		else
+			mpam_write_partsel_reg(msc, CMAX, MPAMCFG_CMAX_CMAX);
+	}
 
-	if (mpam_has_feature(mpam_feat_cmax_cmin, rprops))
-		mpam_write_partsel_reg(msc, CMIN, 0);
+	if (mpam_has_feature(mpam_feat_cmax_cmin, rprops)) {
+		if (mpam_has_feature(mpam_feat_cmax_cmin, cfg))
+			mpam_write_partsel_reg(msc, CMIN, cfg->cmin);
+		else
+			mpam_write_partsel_reg(msc, CMIN, 0);
+	}
 
 	if (mpam_has_feature(mpam_feat_cmax_cassoc, rprops))
 		mpam_write_partsel_reg(msc, CASSOC, MPAMCFG_CASSOC_CASSOC);
@@ -2939,6 +2946,8 @@ static bool mpam_update_config(struct mpam_config *cfg,
 	maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, has_changes);
 	maybe_update_config(cfg, mpam_feat_mbw_min, newcfg, mbw_min, has_changes);
 	maybe_update_config(cfg, mpam_feat_max_limit, newcfg, max_limit, has_changes);
+	maybe_update_config(cfg, mpam_feat_cmax_cmax, newcfg, cmax, has_changes);
+	maybe_update_config(cfg, mpam_feat_cmax_cmin, newcfg, cmin, has_changes);
 
 	return has_changes;
 }
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index f5efebde3c2c..77f643cef28d 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -321,6 +321,8 @@ struct mpam_config {
 	u16	mbw_max;
 	u16	mbw_min;
 	bool	max_limit;
+	u16	cmax;
+	u16	cmin;
 
 	struct mpam_garbage	garbage;
 };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 04/10] arm_mpam: Carry intpri in mpam_config
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (2 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 03/10] arm_mpam: Carry cmax and cmin " Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 05/10] fs/resctrl: Prepare for range-format pseudo resources Jinjie Ruan
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

Add intpri to mpam_config so the internal priority can be programmed
per PARTID through resctrl. mpam_reprogram_ris_partid() writes it when
the feature bit is set, keeping the polarity-aware reset value
otherwise.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 drivers/resctrl/mpam_devices.c  | 4 ++++
 drivers/resctrl/mpam_internal.h | 3 +++
 2 files changed, 7 insertions(+)

diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index f5ac427a0100..22169436689a 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1648,6 +1648,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
 			if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops))
 				intpri = 0;
 
+			if (mpam_has_feature(mpam_feat_intpri_part, cfg))
+				intpri = cfg->intpri;
+
 			pri_val |= FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri);
 		}
 		if (mpam_has_feature(mpam_feat_dspri_part, rprops)) {
@@ -2948,6 +2951,7 @@ static bool mpam_update_config(struct mpam_config *cfg,
 	maybe_update_config(cfg, mpam_feat_max_limit, newcfg, max_limit, has_changes);
 	maybe_update_config(cfg, mpam_feat_cmax_cmax, newcfg, cmax, has_changes);
 	maybe_update_config(cfg, mpam_feat_cmax_cmin, newcfg, cmin, has_changes);
+	maybe_update_config(cfg, mpam_feat_intpri_part, newcfg, intpri, has_changes);
 
 	return has_changes;
 }
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 77f643cef28d..5d6a4c06c7f8 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -324,6 +324,9 @@ struct mpam_config {
 	u16	cmax;
 	u16	cmin;
 
+	/* Internal priority */
+	u16	intpri;
+
 	struct mpam_garbage	garbage;
 };
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 05/10] fs/resctrl: Prepare for range-format pseudo resources
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (3 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 04/10] arm_mpam: Carry intpri " Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 06/10] arm_mpam: Clamp cmax_wd to 16 Jinjie Ruan
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

arm_mpam exposes the MPAM priority, capacity and limit controls as
extra resources that reuse the schema machinery but have no resource
of their own: they share the base resource's class, domains and
components, and only the mpam_config field written differs.

Prepare the resctrl core for them:
- Add RDT_RESOURCE_L{2,3}_{MAX,MIN,PRI} and RDT_RESOURCE_MB_{MIN,PRI,
  HDL,OPT} slots after PERF_PKG. The entries are unconditional; an
  architecture only gets dead slots it never initialises.
- Dispatch rdtgroup_init_alloc(), rdtgroup_size_show() and
  rdtgroup_mode_test_exclusive() on schema_fmt instead of the MBA/SMBA
  rids, so every RESCTRL_SCHEMA_RANGE resource takes the percentage path.
- Skip the pseudo rids in fflags_from_resource() and
  rdtgroup_create_info_dir(): they are only reachable through schemata.
- resctrl_get_default_ctrl() returns the defaults for the pseudo rids.

No behaviour change until an architecture populates the slots.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 fs/resctrl/rdtgroup.c   | 15 ++++++++++-----
 include/linux/resctrl.h | 33 +++++++++++++++++++++++++++++++--
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 5dcbb0a964e8..bdc568bb13d6 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -1491,7 +1491,8 @@ static bool rdtgroup_mode_test_exclusive(struct rdtgroup *rdtgrp)
 
 	list_for_each_entry(s, &resctrl_schema_all, list) {
 		r = s->res;
-		if (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_SMBA)
+		/* Only the bitmap resources are caches with CBM semantics */
+		if (r->schema_fmt == RESCTRL_SCHEMA_RANGE)
 			continue;
 		has_cache = true;
 		list_for_each_entry_rcu(d, &r->ctrl_domains, hdr.list, lockdep_is_cpus_held()) {
@@ -1696,8 +1697,7 @@ static int rdtgroup_size_show(struct kernfs_open_file *of,
 					ctrl = resctrl_arch_get_config(r, d,
 								       closid,
 								       type);
-				if (r->rid == RDT_RESOURCE_MBA ||
-				    r->rid == RDT_RESOURCE_SMBA)
+				if (r->schema_fmt == RESCTRL_SCHEMA_RANGE)
 					size = ctrl;
 				else
 					size = rdtgroup_cbm_to_size(r, d, ctrl);
@@ -2497,6 +2497,9 @@ static unsigned long fflags_from_resource(struct rdt_resource *r)
 		return RFTYPE_RES_MB;
 	case RDT_RESOURCE_PERF_PKG:
 		return RFTYPE_RES_PERF_PKG;
+	default:
+		/* The pseudo resources have no info directory of their own */
+		break;
 	}
 
 	return WARN_ON_ONCE(1);
@@ -2522,6 +2525,9 @@ static int rdtgroup_create_info_dir(struct kernfs_node *parent_kn)
 	/* loop over enabled controls, these are all alloc_capable */
 	list_for_each_entry(s, &resctrl_schema_all, list) {
 		r = s->res;
+		/* The pseudo resources are only reachable through schemata */
+		if (r->rid >= RDT_RESOURCE_L3_MAX)
+			continue;
 		fflags = fflags_from_resource(r) | RFTYPE_CTRL_INFO;
 		ret = rdtgroup_mkdir_info_resdir(s, s->name, fflags);
 		if (ret)
@@ -3859,8 +3865,7 @@ static int rdtgroup_init_alloc(struct rdtgroup *rdtgrp)
 
 	list_for_each_entry(s, &resctrl_schema_all, list) {
 		r = s->res;
-		if (r->rid == RDT_RESOURCE_MBA ||
-		    r->rid == RDT_RESOURCE_SMBA) {
+		if (r->schema_fmt == RESCTRL_SCHEMA_RANGE) {
 			rdtgroup_init_mba(r, rdtgrp->closid);
 			if (is_mba_sc(r))
 				continue;
diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index dd09c2ce9a0f..33af56f953c0 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -54,7 +54,23 @@ enum resctrl_res_level {
 	RDT_RESOURCE_MBA,
 	RDT_RESOURCE_SMBA,
 	RDT_RESOURCE_PERF_PKG,
-	RDT_RESOURCE_LAST = RDT_RESOURCE_PERF_PKG
+	/*
+	 * Pseudo resources re-using the class picked for the resource above
+	 * to expose the MPAM priority/capacity/limit controls. They have no
+	 * class of their own, share the base resource's domains, and never
+	 * appear in info/.
+	 */
+	RDT_RESOURCE_L3_MAX,
+	RDT_RESOURCE_L2_MAX,
+	RDT_RESOURCE_L3_MIN,
+	RDT_RESOURCE_L2_MIN,
+	RDT_RESOURCE_MB_MIN,
+	RDT_RESOURCE_L3_PRI,
+	RDT_RESOURCE_L2_PRI,
+	RDT_RESOURCE_MB_PRI,
+	RDT_RESOURCE_MB_HDL,
+	RDT_RESOURCE_MB_OPT,
+	RDT_RESOURCE_LAST = RDT_RESOURCE_MB_OPT
 };
 
 #define RDT_NUM_RESOURCES (RDT_RESOURCE_LAST + 1)
@@ -408,7 +424,20 @@ static inline u32 resctrl_get_default_ctrl(struct rdt_resource *r)
 	case RESCTRL_SCHEMA_BITMAP:
 		return BIT_MASK(r->cache.cbm_len) - 1;
 	case RESCTRL_SCHEMA_RANGE:
-		return r->membw.max_bw;
+		switch (r->rid) {
+		case RDT_RESOURCE_L3_MIN:
+		case RDT_RESOURCE_L2_MIN:
+		case RDT_RESOURCE_MB_MIN:
+		case RDT_RESOURCE_L3_PRI:
+		case RDT_RESOURCE_L2_PRI:
+			return 0;
+		case RDT_RESOURCE_MB_PRI:
+			return 3;
+		case RDT_RESOURCE_MB_HDL:
+			return 1;
+		default:
+			return r->membw.max_bw;
+		}
 	}
 
 	return WARN_ON_ONCE(1);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 06/10] arm_mpam: Clamp cmax_wd to 16
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (4 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 05/10] fs/resctrl: Prepare for range-format pseudo resources Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 07/10] arm_mpam: Generalize the fixed-point conversion helpers Jinjie Ruan
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

The CMAX_WD field of MPAMF_CCAP_IDR describes the width of a 16-bit
register field, as BWA_WD does, but can report up to 63. The
fixed-point conversion of a CMAX/CMIN value shifts by 16 - wd, so a
width above 16 would shift the wrong way.

Clamp it the same way bwa_wd is clamped.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 drivers/resctrl/mpam_devices.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 22169436689a..0bed6c430af7 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -803,6 +803,8 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
 		u32 ccap_features = mpam_read_partsel_reg(msc, CCAP_IDR);
 
 		props->cmax_wd = FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features);
+		/* As with bwa_wd, the field reports a 16-bit register field */
+		props->cmax_wd = min(props->cmax_wd, 16);
 		if (props->cmax_wd &&
 		    FIELD_GET(MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM, ccap_features))
 			mpam_set_feature(mpam_feat_cmax_softlim, props);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 07/10] arm_mpam: Generalize the fixed-point conversion helpers
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (5 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 06/10] arm_mpam: Clamp cmax_wd to 16 Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 08/10] arm_mpam: resctrl: Expose the capacity controls as pseudo resources Jinjie Ruan
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

mbw_max_to_percent() and percent_to_mbw_max() open-code the width in
cprops->bwa_wd. Split the conversion into a helper that takes the width
and keep the mbw wrappers, so the CMAX/CMIN controls, which use the
same fixed-point format but are cmax_wd bits wide, can reuse it.

No functional change.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 drivers/resctrl/mpam_resctrl.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 9d223057953a..dcdbf0404f07 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -739,18 +739,23 @@ static u32 get_mba_granularity(struct mpam_props *cprops)
  *
  * Find the nearest percentage value to the upper bound of the selected band:
  */
-static u32 mbw_max_to_percent(u16 mbw_max, struct mpam_props *cprops)
+static u32 __mbw_max_to_percent(u16 mbw_max, u8 wd)
 {
 	u32 val = mbw_max;
 
-	val >>= 16 - cprops->bwa_wd;
+	val >>= 16 - wd;
 	val += 1;
 	val *= MAX_MBA_BW;
-	val = DIV_ROUND_CLOSEST(val, 1 << cprops->bwa_wd);
+	val = DIV_ROUND_CLOSEST(val, 1 << wd);
 
 	return val;
 }
 
+static u32 mbw_max_to_percent(u16 mbw_max, struct mpam_props *cprops)
+{
+	return __mbw_max_to_percent(mbw_max, cprops->bwa_wd);
+}
+
 /*
  * Find the band whose upper bound is closest to the specified percentage.
  *
@@ -761,18 +766,23 @@ static u32 mbw_max_to_percent(u16 mbw_max, struct mpam_props *cprops)
  * percentages) and over-commit (where the total of the converted
  * allocations is greater than expected).
  */
-static u16 percent_to_mbw_max(u8 pc, struct mpam_props *cprops)
+static u16 __percent_to_mbw_max(u8 pc, u8 wd)
 {
 	u32 val = pc;
 
-	val <<= cprops->bwa_wd;
+	val <<= wd;
 	val = DIV_ROUND_CLOSEST(val, MAX_MBA_BW);
 	val = max(val, 1) - 1;
-	val <<= 16 - cprops->bwa_wd;
+	val <<= 16 - wd;
 
 	return val;
 }
 
+static u16 percent_to_mbw_max(u8 pc, struct mpam_props *cprops)
+{
+	return __percent_to_mbw_max(pc, cprops->bwa_wd);
+}
+
 static u32 get_mba_min(struct mpam_props *cprops)
 {
 	if (!mba_class_use_mbw_max(cprops)) {
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 08/10] arm_mpam: resctrl: Expose the capacity controls as pseudo resources
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (6 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 07/10] arm_mpam: Generalize the fixed-point conversion helpers Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 09/10] arm_mpam: resctrl: Expose the priority " Jinjie Ruan
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

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@huawei.com>
---
 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


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 09/10] arm_mpam: resctrl: Expose the priority controls as pseudo resources
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (7 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 08/10] arm_mpam: resctrl: Expose the capacity controls as pseudo resources Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23  2:48 ` [RFC PATCH 10/10] arm_mpam: resctrl: Expose the bandwidth minimum and limit " Jinjie Ruan
  2026-10-23 15:11 ` [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Reinette Chatre
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

Expose the MPAM internal priority control as L3PRI/L2PRI/MBPRI: the
cache pseudo resources share the class picked for their base cache,
while MBPRI shares the class picked for MBA. Each is gated on the
intpri feature bit.

control_init() describes them as RESCTRL_SCHEMA_RANGE, with the range
derived from intpri_wd. update_one() and get_config() pass intpri
through mpam_config raw.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 5f423b7a8e41..008db81e734b 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -999,6 +999,9 @@ static void mpam_resctrl_pick_caches(void)
 		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;
+		if (mpam_has_feature(mpam_feat_intpri_part, &class->props))
+			mpam_resctrl_controls[class->level == 2 ?
+					RDT_RESOURCE_L2_PRI : RDT_RESOURCE_L3_PRI].class = class;
 	}
 }
 
@@ -1053,10 +1056,16 @@ static void mpam_resctrl_pick_mba(void)
 	}
 
 	if (candidate_class) {
+		struct mpam_props *cprops = &candidate_class->props;
+
 		pr_debug("selected class %u to back MBA\n",
 			 candidate_class->level);
 		res = &mpam_resctrl_controls[RDT_RESOURCE_MBA];
 		res->class = candidate_class;
+
+		/* As with the caches, re-use the class for the pseudo resources */
+		if (mpam_has_feature(mpam_feat_intpri_part, cprops))
+			mpam_resctrl_controls[RDT_RESOURCE_MB_PRI].class = candidate_class;
 	}
 }
 
@@ -1308,6 +1317,23 @@ static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
 		r->name = (r->rid == RDT_RESOURCE_L2_MIN) ? "L2MIN" : "L3MIN";
 		r->alloc_capable = true;
 		break;
+	case RDT_RESOURCE_L3_PRI:
+	case RDT_RESOURCE_L2_PRI:
+	case RDT_RESOURCE_MB_PRI:
+		r->schema_fmt = RESCTRL_SCHEMA_RANGE;
+		r->ctrl_scope = (r->rid == RDT_RESOURCE_L2_PRI) ?
+			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 = GENMASK(cprops->intpri_wd - 1, 0);
+		r->membw.bw_gran = 1;
+
+		r->name = (r->rid == RDT_RESOURCE_MB_PRI) ? "MBPRI" :
+			((r->rid == RDT_RESOURCE_L2_PRI) ? "L2PRI" : "L3PRI");
+		r->alloc_capable = true;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -1480,6 +1506,11 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 	case RDT_RESOURCE_L2_MIN:
 		configured_by = mpam_feat_cmax_cmin;
 		break;
+	case RDT_RESOURCE_L3_PRI:
+	case RDT_RESOURCE_L2_PRI:
+	case RDT_RESOURCE_MB_PRI:
+		configured_by = mpam_feat_intpri_part;
+		break;
 	case RDT_RESOURCE_MBA:
 		if (mpam_has_feature(mpam_feat_mbw_max, cprops)) {
 			configured_by = mpam_feat_mbw_max;
@@ -1501,6 +1532,8 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 		return ca_max_to_percent(cfg->cmax, cprops);
 	case mpam_feat_cmax_cmin:
 		return ca_max_to_percent(cfg->cmin, cprops);
+	case mpam_feat_intpri_part:
+		return cfg->intpri;
 	case mpam_feat_mbw_max:
 		return mbw_max_to_percent(cfg->mbw_max, cprops);
 	default:
@@ -1563,6 +1596,12 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 		cfg.cmin = percent_to_ca_max(cfg_val, cprops);
 		mpam_set_feature(mpam_feat_cmax_cmin, &cfg);
 		break;
+	case RDT_RESOURCE_L3_PRI:
+	case RDT_RESOURCE_L2_PRI:
+	case RDT_RESOURCE_MB_PRI:
+		cfg.intpri = cfg_val;
+		mpam_set_feature(mpam_feat_intpri_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);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 10/10] arm_mpam: resctrl: Expose the bandwidth minimum and limit as pseudo resources
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (8 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 09/10] arm_mpam: resctrl: Expose the priority " Jinjie Ruan
@ 2026-10-23  2:48 ` Jinjie Ruan
  2026-10-23 15:11 ` [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Reinette Chatre
  10 siblings, 0 replies; 12+ messages in thread
From: Jinjie Ruan @ 2026-10-23  2:48 UTC (permalink / raw)
  To: james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel
  Cc: ruanjinjie

Expose MBMIN, MBHDL and MBOPT, re-using the class picked for MBA.

MBMIN carries the bandwidth minimum as a percentage; MBHDL toggles the
MBW_MAX hard-limit bit; MBOPT exposes the raw MBW_MAX fraction without
the percentage scaling.

control_init() describes them as RESCTRL_SCHEMA_RANGE, with the ranges
derived from bwa_wd. update_one() and get_config() carry the values
through the mpam_config fields added earlier.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 008db81e734b..346ee3729ddf 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -1064,8 +1064,14 @@ static void mpam_resctrl_pick_mba(void)
 		res->class = candidate_class;
 
 		/* As with the caches, re-use the class for the pseudo resources */
+		if (mpam_has_feature(mpam_feat_mbw_min, cprops))
+			mpam_resctrl_controls[RDT_RESOURCE_MB_MIN].class = candidate_class;
 		if (mpam_has_feature(mpam_feat_intpri_part, cprops))
 			mpam_resctrl_controls[RDT_RESOURCE_MB_PRI].class = candidate_class;
+		if (mpam_has_feature(mpam_feat_max_limit, cprops))
+			mpam_resctrl_controls[RDT_RESOURCE_MB_HDL].class = candidate_class;
+		if (mba_class_use_mbw_max(cprops))
+			mpam_resctrl_controls[RDT_RESOURCE_MB_OPT].class = candidate_class;
 	}
 }
 
@@ -1317,6 +1323,19 @@ static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
 		r->name = (r->rid == RDT_RESOURCE_L2_MIN) ? "L2MIN" : "L3MIN";
 		r->alloc_capable = true;
 		break;
+	case RDT_RESOURCE_MB_MIN:
+		r->schema_fmt = RESCTRL_SCHEMA_RANGE;
+		r->ctrl_scope = 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 = "MBMIN";
+		r->alloc_capable = true;
+		break;
 	case RDT_RESOURCE_L3_PRI:
 	case RDT_RESOURCE_L2_PRI:
 	case RDT_RESOURCE_MB_PRI:
@@ -1334,6 +1353,32 @@ static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
 			((r->rid == RDT_RESOURCE_L2_PRI) ? "L2PRI" : "L3PRI");
 		r->alloc_capable = true;
 		break;
+	case RDT_RESOURCE_MB_HDL:
+		r->schema_fmt = RESCTRL_SCHEMA_RANGE;
+		r->ctrl_scope = RESCTRL_L3_CACHE;
+
+		r->membw.delay_linear = true;
+		r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
+		r->membw.min_bw = 0;
+		r->membw.max_bw = 1;
+		r->membw.bw_gran = 1;
+
+		r->name = "MBHDL";
+		r->alloc_capable = true;
+		break;
+	case RDT_RESOURCE_MB_OPT:
+		r->schema_fmt = RESCTRL_SCHEMA_RANGE;
+		r->ctrl_scope = RESCTRL_L3_CACHE;
+
+		r->membw.delay_linear = true;
+		r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
+		r->membw.min_bw = 1;
+		r->membw.max_bw = GENMASK(cprops->bwa_wd - 1, 0);
+		r->membw.bw_gran = 1;
+
+		r->name = "MBOPT";
+		r->alloc_capable = true;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -1511,6 +1556,15 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 	case RDT_RESOURCE_MB_PRI:
 		configured_by = mpam_feat_intpri_part;
 		break;
+	case RDT_RESOURCE_MB_MIN:
+		configured_by = mpam_feat_mbw_min;
+		break;
+	case RDT_RESOURCE_MB_HDL:
+		configured_by = mpam_feat_max_limit;
+		break;
+	case RDT_RESOURCE_MB_OPT:
+		configured_by = mpam_feat_mbw_max;
+		break;
 	case RDT_RESOURCE_MBA:
 		if (mpam_has_feature(mpam_feat_mbw_max, cprops)) {
 			configured_by = mpam_feat_mbw_max;
@@ -1534,7 +1588,14 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 		return ca_max_to_percent(cfg->cmin, cprops);
 	case mpam_feat_intpri_part:
 		return cfg->intpri;
+	case mpam_feat_mbw_min:
+		return mbw_max_to_percent(cfg->mbw_min, cprops);
+	case mpam_feat_max_limit:
+		return cfg->max_limit;
 	case mpam_feat_mbw_max:
+		/* MB_OPT exposes the raw fraction, without the percent scaling */
+		if (r->rid == RDT_RESOURCE_MB_OPT)
+			return cfg->mbw_max >> (16 - cprops->bwa_wd);
 		return mbw_max_to_percent(cfg->mbw_max, cprops);
 	default:
 		return resctrl_get_default_ctrl(r);
@@ -1602,6 +1663,18 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 		cfg.intpri = cfg_val;
 		mpam_set_feature(mpam_feat_intpri_part, &cfg);
 		break;
+	case RDT_RESOURCE_MB_MIN:
+		cfg.mbw_min = percent_to_mbw_max(cfg_val, cprops);
+		mpam_set_feature(mpam_feat_mbw_min, &cfg);
+		break;
+	case RDT_RESOURCE_MB_HDL:
+		cfg.max_limit = !!cfg_val;
+		mpam_set_feature(mpam_feat_max_limit, &cfg);
+		break;
+	case RDT_RESOURCE_MB_OPT:
+		cfg.mbw_max = cfg_val << (16 - cprops->bwa_wd);
+		mpam_set_feature(mpam_feat_mbw_max, &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


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl
  2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
                   ` (9 preceding siblings ...)
  2026-10-23  2:48 ` [RFC PATCH 10/10] arm_mpam: resctrl: Expose the bandwidth minimum and limit " Jinjie Ruan
@ 2026-10-23 15:11 ` Reinette Chatre
  10 siblings, 0 replies; 12+ messages in thread
From: Reinette Chatre @ 2026-10-23 15:11 UTC (permalink / raw)
  To: Jinjie Ruan, james.morse, ben.horgan, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-kernel

Hi Jinjie,

On 10/22/26 7:48 PM, Jinjie Ruan wrote:
> Hi all,
> 
> Resctrl today only exposes CBM and the MBA bandwidth percentage.
> But MPAM MSCs also have per-PARTID fields for internal priority, capacity
> limits, a bandwidth minimum and the MBW_MAX hard-limit bit. The driver
> already writes these fields, but resctrl has no schemata line for them,
> so userspace can't choose the values.
> 
> So this series adds those schemata lines:
> 
> - Patch 1 fixes maybe_update_config(): a feature set with value zero
>   counts as no change, so the register is not written again.
>   The controls below can be zero, so this comes first.
> 
> - Patches 2-4 add mbw_min/max_limit, cmax/cmin and intpri to
>   mpam_config and write them from mpam_reprogram_ris_partid().
> 
> - Patch 5 adds the resource levels, selects the range path by schema_fmt
>   instead of the MBA/SMBA rid, skips the new resources in info/, and sets
>   their default value when a group is created.
> 
> - Patches 6-7 adjust the fixed-point helpers and clamp cmax_wd.
> 
> - Patches 8-10 add the schemata lines:
> 
>   L2MAX/L3MAX       CMAX, percent
>   L2MIN/L3MIN       CMIN, percent
>   MBMIN             MBW_MIN, percent
>   L2PRI/L3PRI/MBPRI INTPRI, raw value
>   MBHDL             HARDLIM, 0 or 1
>   MBOPT             MBW_MAX, raw fraction
> 
> The new resources reuse the class, domains and components of L2/L3/MB; only
> the mpam_config field written differs. MBOPT and MBA both write MBW_MAX.
> 
> So before this series userspace can only set CBM and the MBA percentage.
> After it, you can do:
> 
>   # echo "L3MAX:33751040=50" > schemata
>   # echo "MBHDL:33751040=1" > schemata
> 
> A new group starts from each resource's default value, so the new controls
> stay out of the way until schemata is written.
> 
> Tested this with a QEMU that has one L2 MSC, one L3 MSC and one
> memory-controller MSC (CMAX_WD 4, INTPRI_WD 2, BWA_WD 16). The guest writes
> every new line and reads it back, for example:
> 
>   WRITE-OK   [L3MAX:33751040=50]
>   SCHEMATA: L3MAX:33751040=50
> 
> Then reads the MSC registers:
> 
>   L3 MSC  partid0: PRI(IN)=2  CMIN=0x3000  CMAX=0x7000
>   mem MSC partid0: PRI(IN)=1  MBW_MIN=0x3fff  MBW_MAX=0x1fff  HARDLIM=Y
> 
> CMIN/CMAX are 4-bit fractions (3/16 for 25%, 7/16 for 50%); MBW_MIN is a
> 16-bit fraction (0x3fff for 25%); MBOPT writes the raw fraction (8191).

How does user space know what the valid control values are? What if a new
platform comes along with the same control but different properties of the
control value?


Please consider the ongoing work around adding support for multiple controls
to resctrl. Most recent RFC can be found at 

	https://lore.kernel.org/lkml/d258a32f-12d5-464d-abe9-4720fb3e44b3@intel.com/

An update on this can be found in LPC slides:

	https://lpc.events/event/20/contributions/2368/attachments/2066/4625/resctrl_2026_combined.pdf

The work aims to make the distinction between a resource and its control clear.
It will expose the controls in the schemata file but also support it
with a hierarchy in the info/ directory that makes clear to user space how the
control is associated with the resource while also providing an interface from which
user space can learn what the valid control values are as well as what a domain value
represents.

Reinette

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-10-23 15:11 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-23  2:48 [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 01/10] arm_mpam: Treat adopting a config feature as a change Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 02/10] arm_mpam: Carry mbw_min and the hard limit in mpam_config Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 03/10] arm_mpam: Carry cmax and cmin " Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 04/10] arm_mpam: Carry intpri " Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 05/10] fs/resctrl: Prepare for range-format pseudo resources Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 06/10] arm_mpam: Clamp cmax_wd to 16 Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 07/10] arm_mpam: Generalize the fixed-point conversion helpers Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 08/10] arm_mpam: resctrl: Expose the capacity controls as pseudo resources Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 09/10] arm_mpam: resctrl: Expose the priority " Jinjie Ruan
2026-10-23  2:48 ` [RFC PATCH 10/10] arm_mpam: resctrl: Expose the bandwidth minimum and limit " Jinjie Ruan
2026-10-23 15:11 ` [RFC PATCH 00/10] arm_mpam: Add extra MPAM controls in resctrl Reinette Chatre

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®