Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC)
@ 2026-05-11 15:41 Ben Horgan
  2026-05-11 15:41 ` [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters Ben Horgan
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

Removing the rfc tag as the resctrl precursors [1] have been queued in tip
x86/cache. Due to that dependency, it would be good for this to also go through
x86/cache.

This series adds support for memory bandwidth monitoring.

Please review and test.

Changelogs in patches.

[1] https://lore.kernel.org/all/20260506082855.3694761-1-ben.horgan@arm.com/

Description from the initial cover letter:

The MPAM counter assignment (ABMC emulation) changes that were dropped from
the resctrl glue series due to some missing precursors in resctrl. Counter
assignment enables bandwidth monitoring in systems that have fewer
monitors than resctrl monitor groups.

rfc v1: https://lore.kernel.org/lkml/20260225205436.3571756-1-ben.horgan@arm.com/
rfc v2: https://lore.kernel.org/lkml/20260319165540.381410-1-ben.horgan@arm.com/

Ben Horgan (2):
  arm_mpam: resctrl: Pre-allocate assignable monitors
  arm64: mpam: Add memory bandwidth usage (MBWU) documentation

James Morse (3):
  arm_mpam: resctrl: Pick classes for use as mbm counters
  arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use
  arm_mpam: resctrl: Add resctrl_arch_cntr_read() &
    resctrl_arch_reset_cntr()

 Documentation/arch/arm64/mpam.rst |  17 ++
 drivers/resctrl/mpam_internal.h   |   6 +-
 drivers/resctrl/mpam_resctrl.c    | 306 +++++++++++++++++++++++++++---
 3 files changed, 306 insertions(+), 23 deletions(-)

-- 
2.43.0



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

* [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters
  2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
@ 2026-05-11 15:41 ` Ben Horgan
  2026-05-12  6:50   ` Shaopeng Tan (Fujitsu)
  2026-05-11 15:41 ` [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors Ben Horgan
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86, Shaopeng Tan

From: James Morse <james.morse@arm.com>

resctrl has two types of counters, NUMA-local and global. MPAM can only
count global either using MSC at the L3 cache or in the memory controllers.
When global and local equate to the same thing continue just to call it
global.

Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Tested-by: Zeng Heng <zengheng4@huawei.com>
Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
Changes since rfc v1:
Move finding any_mon_comp into monitor boilerplate patch
Move mpam_resctrl_get_domain_from_cpu() into monitor boilerplate
Remove free running check
Trim commit message
---
 drivers/resctrl/mpam_resctrl.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 226ff6f532fa..f70fa65d39e4 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -606,6 +606,16 @@ static bool cache_has_usable_csu(struct mpam_class *class)
 	return true;
 }
 
+static bool class_has_usable_mbwu(struct mpam_class *class)
+{
+	struct mpam_props *cprops = &class->props;
+
+	if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
+		return false;
+
+	return true;
+}
+
 /*
  * Calculate the worst-case percentage change from each implemented step
  * in the control.
@@ -983,6 +993,22 @@ static void mpam_resctrl_pick_counters(void)
 				break;
 			}
 		}
+
+		if (class_has_usable_mbwu(class) &&
+		    topology_matches_l3(class) &&
+		    traffic_matches_l3(class)) {
+			pr_debug("class %u has usable MBWU, and matches L3 topology and traffic\n",
+				 class->level);
+
+			/*
+			 * We can't distinguish traffic by destination so
+			 * we don't know if it's staying on the same NUMA
+			 * node. Hence, we can't calculate mbm_local except
+			 * when we only have one L3 and it's equivalent to
+			 * mbm_total and so always use mbm_total.
+			 */
+			counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
+		}
 	}
 }
 
-- 
2.43.0



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

* [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors
  2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
  2026-05-11 15:41 ` [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters Ben Horgan
@ 2026-05-11 15:41 ` Ben Horgan
  2026-05-12  7:16   ` Shaopeng Tan (Fujitsu)
  2026-05-11 15:41 ` [PATCH v3 3/5] arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use Ben Horgan
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

MPAM is able to emulate ABMC, i.e. mbm_event mode, by making memory
bandwidth monitors assignable. Rather than supporting the 'default'
mbm_assign_mode always use 'mbm_event' mode even if there are sufficient
memory bandwidth monitors. The per monitor event configuration is only
provided by resctrl when in 'mbm_event' mode and so only allowing
'mbm_event' mode will make it easier to support per-monitor event
configuration for MPAM. For the moment, the only event supported is
mbm_total_event with no bandwidth type configuration. The 'mbm_assign_mode'
file will still show 'default' when there is no support for memory
bandwidth monitoring.

The monitors need to be allocated from the driver, and mapped to whichever
control/monitor group resctrl wants to use them with.

Add a second array to hold the monitor values indexed by resctrl's cntr_id.

When CDP is in use, two monitors are needed so the available number of
counters halves. Platforms with one monitor will have zero monitors when
CDP is in use.

Co-developed-by: James Morse <james.morse@arm.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
Changes since rfc v1:
abmc enabled even if enough counters
Helpers from dropped free running commits
carry on with zero counters if using cdp
set config bits
use kmalloc_objs
drop tags for rework
Configure mbm_cntr_configurable, mbm_cntr_assign_fixed

Changes since rfc v2:
Don't set mon->assigned_counters to an error pointer
Fix mpam_resctrl_teardown_mon()
Remove free running check
Separate cleanup allocations, e.g. __free(), from the rest
Restrict scope on err in mpam_resctrl_monitor_init()
---
 drivers/resctrl/mpam_internal.h |   6 +-
 drivers/resctrl/mpam_resctrl.c  | 138 +++++++++++++++++++++++++++++++-
 2 files changed, 140 insertions(+), 4 deletions(-)

diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 1914aefdcba9..7a166b395b5a 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -411,7 +411,11 @@ struct mpam_resctrl_res {
 struct mpam_resctrl_mon {
 	struct mpam_class	*class;
 
-	/* per-class data that resctrl needs will live here */
+	/* Array of allocated MBWU monitors, indexed by (closid, rmid). */
+	int			*mbwu_idx_to_mon;
+
+	/* Array of assigned MBWU monitors, indexed by idx argument. */
+	int			*assigned_counters;
 };
 
 static inline int mpam_alloc_csu_mon(struct mpam_class *class)
diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index f70fa65d39e4..cba295621f56 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -75,6 +75,8 @@ static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready);
  */
 static bool resctrl_enabled;
 
+static unsigned int l3_num_allocated_mbwu = ~0;
+
 bool resctrl_arch_alloc_capable(void)
 {
 	struct mpam_resctrl_res *res;
@@ -140,7 +142,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
 
 bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r)
 {
-	return false;
+	return (r == &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res);
 }
 
 int resctrl_arch_mbm_cntr_assign_set(struct rdt_resource *r, bool enable)
@@ -185,6 +187,22 @@ static void resctrl_reset_task_closids(void)
 	read_unlock(&tasklist_lock);
 }
 
+static void mpam_resctrl_monitor_sync_abmc_vals(struct rdt_resource *l3)
+{
+	l3->mon.num_mbm_cntrs = l3_num_allocated_mbwu;
+	if (cdp_enabled)
+		l3->mon.num_mbm_cntrs /= 2;
+
+	/*
+	 * Continue as normal even if there are zero counters to avoid giving
+	 * resctrl mixed messages.
+	 */
+	l3->mon.mbm_cntr_assignable = true;
+	l3->mon.mbm_assign_on_mkdir = true;
+	l3->mon.mbm_cntr_configurable = false;
+	l3->mon.mbm_cntr_assign_fixed = true;
+}
+
 int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
 {
 	u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID;
@@ -244,6 +262,7 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
 	WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current));
 
 	resctrl_reset_task_closids();
+	mpam_resctrl_monitor_sync_abmc_vals(l3);
 
 	for_each_possible_cpu(cpu)
 		mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0);
@@ -613,6 +632,9 @@ static bool class_has_usable_mbwu(struct mpam_class *class)
 	if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
 		return false;
 
+	if (!cprops->num_mbwu_mon)
+		return false;
+
 	return true;
 }
 
@@ -935,6 +957,52 @@ static void mpam_resctrl_pick_mba(void)
 	}
 }
 
+static void __free_mbwu_mon(struct mpam_class *class, int *array,
+			    u16 num_mbwu_mon)
+{
+	for (int i = 0; i < num_mbwu_mon; i++) {
+		if (array[i] < 0)
+			continue;
+
+		mpam_free_mbwu_mon(class, array[i]);
+		array[i] = ~0;
+	}
+}
+
+static int __alloc_mbwu_mon(struct mpam_class *class, int *array,
+			    u16 num_mbwu_mon)
+{
+	for (int i = 0; i < num_mbwu_mon; i++) {
+		int mbwu_mon = mpam_alloc_mbwu_mon(class);
+
+		if (mbwu_mon < 0) {
+			__free_mbwu_mon(class, array, num_mbwu_mon);
+			return mbwu_mon;
+		}
+		array[i] = mbwu_mon;
+	}
+
+	l3_num_allocated_mbwu = min(l3_num_allocated_mbwu, num_mbwu_mon);
+
+	return 0;
+}
+
+static int *__alloc_mbwu_array(struct mpam_class *class, u16 num_mbwu_mon)
+{
+	int err;
+
+	int *array __free(kfree) = kmalloc_objs(*array, num_mbwu_mon);
+	if (!array)
+		return ERR_PTR(-ENOMEM);
+
+	memset(array, -1, num_mbwu_mon * sizeof(*array));
+
+	err = __alloc_mbwu_mon(class, array, num_mbwu_mon);
+	if (err)
+		return ERR_PTR(err);
+	return_ptr(array);
+}
+
 static void counter_update_class(enum resctrl_event_id evt_id,
 				 struct mpam_class *class)
 {
@@ -1089,6 +1157,38 @@ static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp)
 	return comp->comp_id;
 }
 
+/*
+ * This must run after all event counters have been picked so that any free
+ * running counters have already been allocated.
+ */
+static int mpam_resctrl_monitor_init_abmc(struct mpam_resctrl_mon *mon)
+{
+	struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
+	struct rdt_resource *l3 = &res->resctrl_res;
+	struct mpam_class *class = mon->class;
+	u16 num_mbwu_mon;
+	size_t num_rmid = resctrl_arch_system_num_rmid_idx();
+	int *cntrs;
+
+	int *rmid_array __free(kfree) = kmalloc_objs(*rmid_array, num_rmid);
+	if (!rmid_array) {
+		pr_debug("Failed to allocate RMID array\n");
+		return -ENOMEM;
+	}
+	memset(rmid_array, -1, num_rmid * sizeof(*rmid_array));
+
+	num_mbwu_mon = class->props.num_mbwu_mon;
+	cntrs = __alloc_mbwu_array(mon->class, num_mbwu_mon);
+	if (IS_ERR(cntrs))
+		return PTR_ERR(cntrs);
+	mon->assigned_counters = cntrs;
+	mon->mbwu_idx_to_mon = no_free_ptr(rmid_array);
+
+	mpam_resctrl_monitor_sync_abmc_vals(l3);
+
+	return 0;
+}
+
 static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 				     enum resctrl_event_id type)
 {
@@ -1133,8 +1233,21 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 	 */
 	l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
 
-	if (resctrl_enable_mon_event(type, false, 0, NULL))
-		l3->mon_capable = true;
+	if (type == QOS_L3_MBM_TOTAL_EVENT_ID) {
+		int err;
+
+		err = mpam_resctrl_monitor_init_abmc(mon);
+		if (err)
+			return err;
+
+		static_assert(MAX_EVT_CONFIG_BITS == 0x7f);
+		l3->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
+	}
+
+	if (!resctrl_enable_mon_event(type, false, 0, NULL))
+		return -EINVAL;
+
+	l3->mon_capable = true;
 
 	return 0;
 }
@@ -1697,6 +1810,23 @@ void mpam_resctrl_exit(void)
 	resctrl_exit();
 }
 
+static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
+{
+	u32 num_mbwu_mon = l3_num_allocated_mbwu;
+
+	if (mon->mbwu_idx_to_mon)
+		return;
+
+	if (mon->assigned_counters) {
+		__free_mbwu_mon(class, mon->assigned_counters, num_mbwu_mon);
+		kfree(mon->assigned_counters);
+		mon->assigned_counters = NULL;
+	}
+
+	kfree(mon->mbwu_idx_to_mon);
+	mon->mbwu_idx_to_mon = NULL;
+}
+
 /*
  * The driver is detaching an MSC from this class, if resctrl was using it,
  * pull on resctrl_exit().
@@ -1719,6 +1849,8 @@ void mpam_resctrl_teardown_class(struct mpam_class *class)
 	for_each_mpam_resctrl_mon(mon, eventid) {
 		if (mon->class == class) {
 			mon->class = NULL;
+
+			mpam_resctrl_teardown_mon(mon, class);
 			break;
 		}
 	}
-- 
2.43.0



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

* [PATCH v3 3/5] arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use
  2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
  2026-05-11 15:41 ` [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters Ben Horgan
  2026-05-11 15:41 ` [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors Ben Horgan
@ 2026-05-11 15:41 ` Ben Horgan
  2026-05-11 15:41 ` [PATCH v3 4/5] arm_mpam: resctrl: Add resctrl_arch_cntr_read() & resctrl_arch_reset_cntr() Ben Horgan
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

From: James Morse <james.morse@arm.com>

ABMC, mbm_event mode, has a helper resctrl_arch_config_cntr() for changing
the mapping between 'cntr_id' and a CLOSID/RMID pair.

Add the helper.

For MPAM this is done by updating the mon->mbwu_idx_to_mon[] array, and as
usual CDP means it needs doing in three different ways.

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
Changes since new rfc:
Mention mbm_event mode in commit message
---
 drivers/resctrl/mpam_resctrl.c | 43 +++++++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 6 deletions(-)

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index cba295621f56..5e70b0f822c9 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -127,12 +127,6 @@ void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d
 {
 }
 
-void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
-			      enum resctrl_event_id evtid, u32 rmid, u32 closid,
-			      u32 cntr_id, bool assign)
-{
-}
-
 int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
 			   u32 unused, u32 rmid, int cntr_id,
 			   enum resctrl_event_id eventid, u64 *val)
@@ -1080,6 +1074,43 @@ static void mpam_resctrl_pick_counters(void)
 	}
 }
 
+static void __config_cntr(struct mpam_resctrl_mon *mon, u32 cntr_id,
+			  enum resctrl_conf_type cdp_type, u32 closid, u32 rmid,
+			  bool assign)
+{
+	u32 mbwu_idx, mon_idx = resctrl_get_config_index(cntr_id, cdp_type);
+
+	closid = resctrl_get_config_index(closid, cdp_type);
+	mbwu_idx = resctrl_arch_rmid_idx_encode(closid, rmid);
+	WARN_ON_ONCE(mon_idx > l3_num_allocated_mbwu);
+
+	if (assign)
+		mon->mbwu_idx_to_mon[mbwu_idx] = mon->assigned_counters[mon_idx];
+	else
+		mon->mbwu_idx_to_mon[mbwu_idx] = -1;
+}
+
+void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
+			      enum resctrl_event_id evtid, u32 rmid, u32 closid,
+			      u32 cntr_id, bool assign)
+{
+	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[evtid];
+
+	if (!mon->mbwu_idx_to_mon || !mon->assigned_counters) {
+		pr_debug("monitor arrays not allocated\n");
+		return;
+	}
+
+	if (cdp_enabled) {
+		__config_cntr(mon, cntr_id, CDP_CODE, closid, rmid, assign);
+		__config_cntr(mon, cntr_id, CDP_DATA, closid, rmid, assign);
+	} else {
+		__config_cntr(mon, cntr_id, CDP_NONE, closid, rmid, assign);
+	}
+
+	resctrl_arch_reset_rmid(r, d, closid, rmid, evtid);
+}
+
 static int mpam_resctrl_control_init(struct mpam_resctrl_res *res)
 {
 	struct mpam_class *class = res->class;
-- 
2.43.0



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

* [PATCH v3 4/5] arm_mpam: resctrl: Add resctrl_arch_cntr_read() & resctrl_arch_reset_cntr()
  2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
                   ` (2 preceding siblings ...)
  2026-05-11 15:41 ` [PATCH v3 3/5] arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use Ben Horgan
@ 2026-05-11 15:41 ` Ben Horgan
  2026-05-11 15:41 ` [PATCH v3 5/5] arm64: mpam: Add memory bandwidth usage (MBWU) documentation Ben Horgan
  2026-05-11 15:51 ` [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
  5 siblings, 0 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

From: James Morse <james.morse@arm.com>

When used in 'mbm_event' mode, ABMC emulation, resctrl uses arch hooks to
read and reset the memory bandwidth utilization (MBWU) counters.

Add these.

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
Changes since rfc v1:
Move __reset_mon() and reset_mon_cdp_safe() helpers here
support mbwu in __read_mon()
Mention mbm_event mode in commit message
---
 drivers/resctrl/mpam_resctrl.c | 99 +++++++++++++++++++++++++++++-----
 1 file changed, 86 insertions(+), 13 deletions(-)

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 5e70b0f822c9..08eb05031ffc 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -121,19 +121,6 @@ void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_l3_mon_domain *d
 {
 }
 
-void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
-			     u32 closid, u32 rmid, int cntr_id,
-			     enum resctrl_event_id eventid)
-{
-}
-
-int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
-			   u32 unused, u32 rmid, int cntr_id,
-			   enum resctrl_event_id eventid, u64 *val)
-{
-	return -EOPNOTSUPP;
-}
-
 bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r)
 {
 	return (r == &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res);
@@ -467,6 +454,14 @@ static int __read_mon(struct mpam_resctrl_mon *mon, struct mpam_component *mon_c
 	/* Shift closid to account for CDP */
 	closid = resctrl_get_config_index(closid, cdp_type);
 
+	if (mon_idx == USE_PRE_ALLOCATED) {
+		int mbwu_idx = resctrl_arch_rmid_idx_encode(closid, rmid);
+
+		mon_idx = mon->mbwu_idx_to_mon[mbwu_idx];
+		if (mon_idx == -1)
+			return -ENOENT;
+	}
+
 	if (irqs_disabled()) {
 		/* Check if we can access this domain without an IPI */
 		return -EIO;
@@ -539,6 +534,84 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr,
 				 closid, rmid, val);
 }
 
+/* MBWU counters when in ABMC mode */
+int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
+			   u32 closid, u32 rmid, int mon_idx,
+			   enum resctrl_event_id eventid, u64 *val)
+{
+	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[eventid];
+	struct mpam_resctrl_dom *l3_dom;
+	struct mpam_component *mon_comp;
+
+	if (!mpam_is_enabled())
+		return -EINVAL;
+
+	if (eventid == QOS_L3_OCCUP_EVENT_ID || !mon->class)
+		return -EINVAL;
+
+	l3_dom = container_of(d, struct mpam_resctrl_dom, resctrl_mon_dom);
+	mon_comp = l3_dom->mon_comp[eventid];
+
+	return read_mon_cdp_safe(mon, mon_comp, mpam_feat_msmon_mbwu, mon_idx,
+				 closid, rmid, val);
+}
+
+static void __reset_mon(struct mpam_resctrl_mon *mon, struct mpam_component *mon_comp,
+			int mon_idx,
+			enum resctrl_conf_type cdp_type, u32 closid, u32 rmid)
+{
+	struct mon_cfg cfg = { };
+
+	if (!mpam_is_enabled())
+		return;
+
+	/* Shift closid to account for CDP */
+	closid = resctrl_get_config_index(closid, cdp_type);
+
+	if (mon_idx == USE_PRE_ALLOCATED) {
+		int mbwu_idx = resctrl_arch_rmid_idx_encode(closid, rmid);
+
+		mon_idx = mon->mbwu_idx_to_mon[mbwu_idx];
+	}
+
+	if (mon_idx == -1)
+		return;
+	cfg.mon = mon_idx;
+	mpam_msmon_reset_mbwu(mon_comp, &cfg);
+}
+
+static void reset_mon_cdp_safe(struct mpam_resctrl_mon *mon, struct mpam_component *mon_comp,
+			       int mon_idx, u32 closid, u32 rmid)
+{
+	if (cdp_enabled) {
+		__reset_mon(mon, mon_comp, mon_idx, CDP_CODE, closid, rmid);
+		__reset_mon(mon, mon_comp, mon_idx, CDP_DATA, closid, rmid);
+	} else {
+		__reset_mon(mon, mon_comp, mon_idx, CDP_NONE, closid, rmid);
+	}
+}
+
+/* Reset an assigned counter */
+void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
+			     u32 closid, u32 rmid, int cntr_id,
+			     enum resctrl_event_id eventid)
+{
+	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[eventid];
+	struct mpam_resctrl_dom *l3_dom;
+	struct mpam_component *mon_comp;
+
+	if (!mpam_is_enabled())
+		return;
+
+	if (eventid == QOS_L3_OCCUP_EVENT_ID || !mon->class)
+		return;
+
+	l3_dom = container_of(d, struct mpam_resctrl_dom, resctrl_mon_dom);
+	mon_comp = l3_dom->mon_comp[eventid];
+
+	reset_mon_cdp_safe(mon, mon_comp, USE_PRE_ALLOCATED, closid, rmid);
+}
+
 /*
  * The rmid realloc threshold should be for the smallest cache exposed to
  * resctrl.
-- 
2.43.0



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

* [PATCH v3 5/5] arm64: mpam: Add memory bandwidth usage (MBWU) documentation
  2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
                   ` (3 preceding siblings ...)
  2026-05-11 15:41 ` [PATCH v3 4/5] arm_mpam: resctrl: Add resctrl_arch_cntr_read() & resctrl_arch_reset_cntr() Ben Horgan
@ 2026-05-11 15:41 ` Ben Horgan
  2026-05-11 15:51 ` [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
  5 siblings, 0 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:41 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

Memory bandwidth monitoring make uses of MBWU monitors and is now exposed
to the user via resctrl. Add some documentation so the user knows what to
expect.

Co-developed-by: James Morse <james.morse@arm.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
---
 Documentation/arch/arm64/mpam.rst | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/Documentation/arch/arm64/mpam.rst b/Documentation/arch/arm64/mpam.rst
index 570f51a8d4eb..208ff17068c4 100644
--- a/Documentation/arch/arm64/mpam.rst
+++ b/Documentation/arch/arm64/mpam.rst
@@ -65,6 +65,23 @@ The supported features are:
   there is at least one CSU monitor on each MSC that makes up the L3 group.
   Exposing CSU counters from other caches or devices is not supported.
 
+* Memory Bandwidth Usage (MBWU) on or after the L3 cache.  resctrl uses the
+  L3 cache-id to identify where the memory bandwidth is measured. For this
+  reason the platform must have an L3 cache with cache-id's supplied by
+  firmware. (It doesn't need to support MPAM.)
+
+  Memory bandwidth monitoring makes use of MBWU monitors in each MSC that
+  makes up the L3 group. If the memory bandwidth monitoring is on the memory
+  rather than the L3 then there must be a single global L3 as otherwise it
+  is unknown which L3 the traffic came from.
+
+  To expose 'mbm_total_bytes', the topology of the group of MSC chosen must
+  match the topology of the L3 cache so that the cache-id's can be
+  repainted. For example: Platforms with Memory bandwidth monitors on
+  CPU-less NUMA nodes cannot expose 'mbm_total_bytes' as these nodes do not
+  have a corresponding L3 cache. 'mbm_local_bytes' is not exposed as MPAM
+  cannot distinguish local traffic from global traffic.
+
 Reporting Bugs
 ==============
 If you are not seeing the counters or controls you expect please share the
-- 
2.43.0



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

* Re: [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC)
  2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
                   ` (4 preceding siblings ...)
  2026-05-11 15:41 ` [PATCH v3 5/5] arm64: mpam: Add memory bandwidth usage (MBWU) documentation Ben Horgan
@ 2026-05-11 15:51 ` Ben Horgan
  5 siblings, 0 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-11 15:51 UTC (permalink / raw)
  To: ben.horgan
  Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
	dfustini, fenghuay, gshan, james.morse, jonathan.cameron, kobak,
	lcherian, linux-arm-kernel, linux-kernel, peternewman,
	punit.agrawal, quic_jiles, reinette.chatre, rohit.mathew, scott,
	sdonthineni, tan.shaopeng, xhao, zengheng4, x86

I forgot to say, the code can be found at:

https://gitlab.arm.com/linux-arm/linux-bh.git mpam_abmc_v3

On 5/11/26 16:41, Ben Horgan wrote:
> Removing the rfc tag as the resctrl precursors [1] have been queued in tip
> x86/cache. Due to that dependency, it would be good for this to also go through
> x86/cache.
> 
> This series adds support for memory bandwidth monitoring.
> 
> Please review and test.
> 
> Changelogs in patches.
> 
> [1] https://lore.kernel.org/all/20260506082855.3694761-1-ben.horgan@arm.com/
> 
> Description from the initial cover letter:
> 
> The MPAM counter assignment (ABMC emulation) changes that were dropped from
> the resctrl glue series due to some missing precursors in resctrl. Counter
> assignment enables bandwidth monitoring in systems that have fewer
> monitors than resctrl monitor groups.
> 
> rfc v1: https://lore.kernel.org/lkml/20260225205436.3571756-1-ben.horgan@arm.com/
> rfc v2: https://lore.kernel.org/lkml/20260319165540.381410-1-ben.horgan@arm.com/
> 
> Ben Horgan (2):
>   arm_mpam: resctrl: Pre-allocate assignable monitors
>   arm64: mpam: Add memory bandwidth usage (MBWU) documentation
> 
> James Morse (3):
>   arm_mpam: resctrl: Pick classes for use as mbm counters
>   arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use
>   arm_mpam: resctrl: Add resctrl_arch_cntr_read() &
>     resctrl_arch_reset_cntr()
> 
>  Documentation/arch/arm64/mpam.rst |  17 ++
>  drivers/resctrl/mpam_internal.h   |   6 +-
>  drivers/resctrl/mpam_resctrl.c    | 306 +++++++++++++++++++++++++++---
>  3 files changed, 306 insertions(+), 23 deletions(-)
> 



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

* Re: [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters
  2026-05-11 15:41 ` [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters Ben Horgan
@ 2026-05-12  6:50   ` Shaopeng Tan (Fujitsu)
  2026-05-12  9:21     ` Ben Horgan
  0 siblings, 1 reply; 11+ messages in thread
From: Shaopeng Tan (Fujitsu) @ 2026-05-12  6:50 UTC (permalink / raw)
  To: Ben Horgan
  Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
	baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
	dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
	fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
	jonathan.cameron@huawei.com, kobak@nvidia.com,
	lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, peternewman@google.com,
	punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
	reinette.chatre@intel.com, rohit.mathew@arm.com,
	scott@os.amperecomputing.com, sdonthineni@nvidia.com,
	xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org

Hello Ben,

> From: James Morse <james.morse@arm.com>
> 
> resctrl has two types of counters, NUMA-local and global. MPAM can only
> count global either using MSC at the L3 cache or in the memory controllers.
> When global and local equate to the same thing continue just to call it
> global.
> 
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc v1:
> Move finding any_mon_comp into monitor boilerplate patch
> Move mpam_resctrl_get_domain_from_cpu() into monitor boilerplate
> Remove free running check
> Trim commit message
> ---
>  drivers/resctrl/mpam_resctrl.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
> index 226ff6f532fa..f70fa65d39e4 100644
> --- a/drivers/resctrl/mpam_resctrl.c
> +++ b/drivers/resctrl/mpam_resctrl.c
> @@ -606,6 +606,16 @@ static bool cache_has_usable_csu(struct mpam_class *class)
>          return true;
>  }
>  
> +static bool class_has_usable_mbwu(struct mpam_class *class)
> +{
> +       struct mpam_props *cprops = &class->props;
> +
> +       if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
> +               return false;
> +
> +       return true;
> +}
> +
>  /*
>   * Calculate the worst-case percentage change from each implemented step
>   * in the control.
> @@ -983,6 +993,22 @@ static void mpam_resctrl_pick_counters(void)
>                                  break;
>                          }
>                  }
> +
> +               if (class_has_usable_mbwu(class) &&
> +                   topology_matches_l3(class) &&
> +                   traffic_matches_l3(class)) {
> +                       pr_debug("class %u has usable MBWU, and matches L3 topology and traffic\n",
> +                                class->level);
> +
> +                       /*
> +                        * We can't distinguish traffic by destination so
> +                        * we don't know if it's staying on the same NUMA
> +                        * node. Hence, we can't calculate mbm_local except
> +                        * when we only have one L3 and it's equivalent to
> +                        * mbm_total and so always use mbm_total.
> +                        */
> +                       counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
> +               }
>          }
>  }
>  
> -- 
> 2.43.0

https://lore.kernel.org/lkml/599617aa-aade-4fde-9efa-79d592f1ff3f@arm.com/

This concerns the comment I received last time. 
I may not have fully understood it, so I'd like to clarify it once more.

Even if the system as a whole has multiple L3 caches and multiple NUMA nodes,
ABMC will be enabled as long as there is a single L3 cache and a single corresponding NUMA node.
Is my understanding correct?
If my understanding is correct, within the 'traffic_matches_l3()' function,
ABMC is enabled only when the entire system has a single NUMA node and a single L3 cache.

 870 static bool traffic_matches_l3(struct mpam_class *class)
 871 {
...
 901
 902         if (!cpumask_equal(tmp_cpumask, cpu_possible_mask)) {
 903                 pr_debug("There is more than one L3\n");
 904                 return false; *
 905         }
...
 912
 913         if (num_possible_nodes() > 1) {
 914                 pr_debug("There is more than one numa node\n");
 915                 return false;  *
 916         }
 917
...
 926 }


Also, I'd also like to confirm one more thing.
The mpam_resctrl_pick_mba() function also calls traffic_matches_l3(). 
This suggests that, except in scenarios where the entire system has a single L3 cache and a single NUMA node (ABMC is disabled),
the Memory Bandwidth allocation will also be disabled.
Is this the intended behavior? If so, could you explain why?

Best regards,
Shaopeng TAN





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

* Re: [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors
  2026-05-11 15:41 ` [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors Ben Horgan
@ 2026-05-12  7:16   ` Shaopeng Tan (Fujitsu)
  2026-05-12  9:43     ` Ben Horgan
  0 siblings, 1 reply; 11+ messages in thread
From: Shaopeng Tan (Fujitsu) @ 2026-05-12  7:16 UTC (permalink / raw)
  To: Ben Horgan
  Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
	baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
	dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
	fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
	jonathan.cameron@huawei.com, kobak@nvidia.com,
	lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, peternewman@google.com,
	punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
	reinette.chatre@intel.com, rohit.mathew@arm.com,
	scott@os.amperecomputing.com, sdonthineni@nvidia.com,
	xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org

Hello Ben,


> MPAM is able to emulate ABMC, i.e. mbm_event mode, by making memory
> bandwidth monitors assignable. Rather than supporting the 'default'
> mbm_assign_mode always use 'mbm_event' mode even if there are sufficient
> memory bandwidth monitors. The per monitor event configuration is only
> provided by resctrl when in 'mbm_event' mode and so only allowing
> 'mbm_event' mode will make it easier to support per-monitor event
> configuration for MPAM. For the moment, the only event supported is
> mbm_total_event with no bandwidth type configuration. The 'mbm_assign_mode'
> file will still show 'default' when there is no support for memory
> bandwidth monitoring.
> 
> The monitors need to be allocated from the driver, and mapped to whichever
> control/monitor group resctrl wants to use them with.
> 
> Add a second array to hold the monitor values indexed by resctrl's cntr_id.
> 
> When CDP is in use, two monitors are needed so the available number of
> counters halves. Platforms with one monitor will have zero monitors when
> CDP is in use.
> 
> Co-developed-by: James Morse <james.morse@arm.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc v1:
> abmc enabled even if enough counters
> Helpers from dropped free running commits
> carry on with zero counters if using cdp
> set config bits
> use kmalloc_objs
> drop tags for rework
> Configure mbm_cntr_configurable, mbm_cntr_assign_fixed
> 
> Changes since rfc v2:
> Don't set mon->assigned_counters to an error pointer
> Fix mpam_resctrl_teardown_mon()
> Remove free running check
> Separate cleanup allocations, e.g. __free(), from the rest
> Restrict scope on err in mpam_resctrl_monitor_init()
> ---
>  drivers/resctrl/mpam_internal.h |   6 +-
>  drivers/resctrl/mpam_resctrl.c  | 138 +++++++++++++++++++++++++++++++-
>  2 files changed, 140 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index 1914aefdcba9..7a166b395b5a 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -411,7 +411,11 @@ struct mpam_resctrl_res {
>  struct mpam_resctrl_mon {
>          struct mpam_class       *class;
>  
> -       /* per-class data that resctrl needs will live here */
> +       /* Array of allocated MBWU monitors, indexed by (closid, rmid). */
> +       int                     *mbwu_idx_to_mon;
> +
> +       /* Array of assigned MBWU monitors, indexed by idx argument. */
> +       int                     *assigned_counters;
>  };
>  
>  static inline int mpam_alloc_csu_mon(struct mpam_class *class)
> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
> index f70fa65d39e4..cba295621f56 100644
> --- a/drivers/resctrl/mpam_resctrl.c
> +++ b/drivers/resctrl/mpam_resctrl.c
> @@ -75,6 +75,8 @@ static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready);
>   */
>  static bool resctrl_enabled;
>  
> +static unsigned int l3_num_allocated_mbwu = ~0;
> +
>  bool resctrl_arch_alloc_capable(void)
>  {
>          struct mpam_resctrl_res *res;
> @@ -140,7 +142,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
>  
>  bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r)
>  {
> -       return false;
> +       return (r == &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res);
>  }
>  
>  int resctrl_arch_mbm_cntr_assign_set(struct rdt_resource *r, bool enable)
> @@ -185,6 +187,22 @@ static void resctrl_reset_task_closids(void)
>          read_unlock(&tasklist_lock);
>  }
>  
> +static void mpam_resctrl_monitor_sync_abmc_vals(struct rdt_resource *l3)
> +{
> +       l3->mon.num_mbm_cntrs = l3_num_allocated_mbwu;
> +       if (cdp_enabled)
> +               l3->mon.num_mbm_cntrs /= 2;
> +
> +       /*
> +        * Continue as normal even if there are zero counters to avoid giving
> +        * resctrl mixed messages.
> +        */
> +       l3->mon.mbm_cntr_assignable = true;
> +       l3->mon.mbm_assign_on_mkdir = true;
> +       l3->mon.mbm_cntr_configurable = false;
> +       l3->mon.mbm_cntr_assign_fixed = true;
> +}
> +
>  int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
>  {
>          u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID;
> @@ -244,6 +262,7 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
>          WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current));
>  
>          resctrl_reset_task_closids();
> +       mpam_resctrl_monitor_sync_abmc_vals(l3);
>  
>          for_each_possible_cpu(cpu)
>                  mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0);
> @@ -613,6 +632,9 @@ static bool class_has_usable_mbwu(struct mpam_class *class)
>          if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
>                  return false;
>  
> +       if (!cprops->num_mbwu_mon)
> +               return false;
> +
>          return true;
>  }
>  
> @@ -935,6 +957,52 @@ static void mpam_resctrl_pick_mba(void)
>          }
>  }
>  
> +static void __free_mbwu_mon(struct mpam_class *class, int *array,
> +                           u16 num_mbwu_mon)
> +{
> +       for (int i = 0; i < num_mbwu_mon; i++) {
> +               if (array[i] < 0)
> +                       continue;
> +
> +               mpam_free_mbwu_mon(class, array[i]);
> +               array[i] = ~0;
> +       }
> +}
> +
> +static int __alloc_mbwu_mon(struct mpam_class *class, int *array,
> +                           u16 num_mbwu_mon)
> +{
> +       for (int i = 0; i < num_mbwu_mon; i++) {
> +               int mbwu_mon = mpam_alloc_mbwu_mon(class);
> +
> +               if (mbwu_mon < 0) {
> +                       __free_mbwu_mon(class, array, num_mbwu_mon);
> +                       return mbwu_mon;
> +               }
> +               array[i] = mbwu_mon;
> +       }
> +
> +       l3_num_allocated_mbwu = min(l3_num_allocated_mbwu, num_mbwu_mon);
> +
> +       return 0;
> +}
> +
> +static int *__alloc_mbwu_array(struct mpam_class *class, u16 num_mbwu_mon)
> +{
> +       int err;
> +
> +       int *array __free(kfree) = kmalloc_objs(*array, num_mbwu_mon);
> +       if (!array)
> +               return ERR_PTR(-ENOMEM);
> +
> +       memset(array, -1, num_mbwu_mon * sizeof(*array));
> +
> +       err = __alloc_mbwu_mon(class, array, num_mbwu_mon);
> +       if (err)
> +               return ERR_PTR(err);
> +       return_ptr(array);
> +}
> +
>  static void counter_update_class(enum resctrl_event_id evt_id,
>                                   struct mpam_class *class)
>  {
> @@ -1089,6 +1157,38 @@ static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp)
>          return comp->comp_id;
>  }
>  
> +/*
> + * This must run after all event counters have been picked so that any free
> + * running counters have already been allocated.
> + */
> +static int mpam_resctrl_monitor_init_abmc(struct mpam_resctrl_mon *mon)
> +{
> +       struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
> +       struct rdt_resource *l3 = &res->resctrl_res;
> +       struct mpam_class *class = mon->class;
> +       u16 num_mbwu_mon;
> +       size_t num_rmid = resctrl_arch_system_num_rmid_idx();
> +       int *cntrs;

Wouldn't a reverse tree format be better?

> +       int *rmid_array __free(kfree) = kmalloc_objs(*rmid_array, num_rmid);
> +       if (!rmid_array) {
> +               pr_debug("Failed to allocate RMID array\n");
> +               return -ENOMEM;
> +       }
> +       memset(rmid_array, -1, num_rmid * sizeof(*rmid_array));
> +
> +       num_mbwu_mon = class->props.num_mbwu_mon;
> +       cntrs = __alloc_mbwu_array(mon->class, num_mbwu_mon);
> +       if (IS_ERR(cntrs))
> +               return PTR_ERR(cntrs);
> +       mon->assigned_counters = cntrs;
> +       mon->mbwu_idx_to_mon = no_free_ptr(rmid_array);
> +
> +       mpam_resctrl_monitor_sync_abmc_vals(l3);
> +
> +       return 0;
> +}
> +
>  static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
>                                       enum resctrl_event_id type)
>  {
> @@ -1133,8 +1233,21 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
>           */
>          l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
>  
> -       if (resctrl_enable_mon_event(type, false, 0, NULL))
> -               l3->mon_capable = true;
> +       if (type == QOS_L3_MBM_TOTAL_EVENT_ID) {
> +               int err;
> +
> +               err = mpam_resctrl_monitor_init_abmc(mon);
> +               if (err)
> +                       return err;
> +
> +               static_assert(MAX_EVT_CONFIG_BITS == 0x7f);
> +               l3->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
> +       }
> +
> +       if (!resctrl_enable_mon_event(type, false, 0, NULL))
> +               return -EINVAL;
> +
> +       l3->mon_capable = true;
>  
>          return 0;
>  }
> @@ -1697,6 +1810,23 @@ void mpam_resctrl_exit(void)
>          resctrl_exit();
>  }
>  
> +static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
> +{
> +       u32 num_mbwu_mon = l3_num_allocated_mbwu;
> +
> +       if (mon->mbwu_idx_to_mon)
> +               return;

 Isn't it 'if (mon->mbwu_idx_to_mon == NULL)' ?
 
 
 Best regards,
 Shaopeng TAN

> +       if (mon->assigned_counters) {
> +               __free_mbwu_mon(class, mon->assigned_counters, num_mbwu_mon);
> +               kfree(mon->assigned_counters);
> +               mon->assigned_counters = NULL;
> +       }
> +
> +       kfree(mon->mbwu_idx_to_mon);
> +       mon->mbwu_idx_to_mon = NULL;
> +}
> +
>  /*
>   * The driver is detaching an MSC from this class, if resctrl was using it,
>   * pull on resctrl_exit().
> @@ -1719,6 +1849,8 @@ void mpam_resctrl_teardown_class(struct mpam_class *class)
>          for_each_mpam_resctrl_mon(mon, eventid) {
>                  if (mon->class == class) {
>                          mon->class = NULL;
> +
> +                       mpam_resctrl_teardown_mon(mon, class);
>                          break;
>                  }
>          }
> --
> 2.43.0


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

* Re: [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters
  2026-05-12  6:50   ` Shaopeng Tan (Fujitsu)
@ 2026-05-12  9:21     ` Ben Horgan
  0 siblings, 0 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-12  9:21 UTC (permalink / raw)
  To: Shaopeng Tan (Fujitsu)
  Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
	baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
	dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
	fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
	jonathan.cameron@huawei.com, kobak@nvidia.com,
	lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, peternewman@google.com,
	punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
	reinette.chatre@intel.com, rohit.mathew@arm.com,
	scott@os.amperecomputing.com, sdonthineni@nvidia.com,
	xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org

Hi Shaopeng,

On 5/12/26 07:50, Shaopeng Tan (Fujitsu) wrote:
> Hello Ben,
> 
>> From: James Morse <james.morse@arm.com>
>>
>> resctrl has two types of counters, NUMA-local and global. MPAM can only
>> count global either using MSC at the L3 cache or in the memory controllers.
>> When global and local equate to the same thing continue just to call it
>> global.
>>
>> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
>> Tested-by: Zeng Heng <zengheng4@huawei.com>
>> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
>> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
>> Signed-off-by: James Morse <james.morse@arm.com>
>> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
>> ---
>> Changes since rfc v1:
>> Move finding any_mon_comp into monitor boilerplate patch
>> Move mpam_resctrl_get_domain_from_cpu() into monitor boilerplate
>> Remove free running check
>> Trim commit message
>> ---
>>  drivers/resctrl/mpam_resctrl.c | 26 ++++++++++++++++++++++++++
>>  1 file changed, 26 insertions(+)
>>
>> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
>> index 226ff6f532fa..f70fa65d39e4 100644
>> --- a/drivers/resctrl/mpam_resctrl.c
>> +++ b/drivers/resctrl/mpam_resctrl.c
>> @@ -606,6 +606,16 @@ static bool cache_has_usable_csu(struct mpam_class *class)
>>          return true;
>>  }
>>  
>> +static bool class_has_usable_mbwu(struct mpam_class *class)
>> +{
>> +       struct mpam_props *cprops = &class->props;
>> +
>> +       if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
>> +               return false;
>> +
>> +       return true;
>> +}
>> +
>>  /*
>>   * Calculate the worst-case percentage change from each implemented step
>>   * in the control.
>> @@ -983,6 +993,22 @@ static void mpam_resctrl_pick_counters(void)
>>                                  break;
>>                          }
>>                  }
>> +
>> +               if (class_has_usable_mbwu(class) &&
>> +                   topology_matches_l3(class) &&
>> +                   traffic_matches_l3(class)) {
>> +                       pr_debug("class %u has usable MBWU, and matches L3 topology and traffic\n",
>> +                                class->level);
>> +
>> +                       /*
>> +                        * We can't distinguish traffic by destination so
>> +                        * we don't know if it's staying on the same NUMA
>> +                        * node. Hence, we can't calculate mbm_local except
>> +                        * when we only have one L3 and it's equivalent to
>> +                        * mbm_total and so always use mbm_total.
>> +                        */
>> +                       counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
>> +               }
>>          }
>>  }
>>  
>> -- 
>> 2.43.0
> 
> https://lore.kernel.org/lkml/599617aa-aade-4fde-9efa-79d592f1ff3f@arm.com/
> 
> This concerns the comment I received last time. 
> I may not have fully understood it, so I'd like to clarify it once more.

I'll try and explain better.

> 
> Even if the system as a whole has multiple L3 caches and multiple NUMA nodes,
> ABMC will be enabled as long as there is a single L3 cache and a single corresponding NUMA node.
> Is my understanding correct?
> If my understanding is correct, within the 'traffic_matches_l3()' function,
> ABMC is enabled only when the entire system has a single NUMA node and a single L3 cache.


These restrictions only apply when the MSC containing the bandwidth counters is
at the memory, as advertised by ACPI. When the counters are on the L3 cache
there can be multiple L3 and multiple NUMA nodes and a domain with AMBC memory
bandwidth counters will be exposed for each instance of the L3 cache.

As resctrl, currently, expects all monitors to be on the L3 cache we can only
use counters if they can be considered to be at the L3. The user just sees an
L3_MON directory. When the monitors/counters are at the memory we can only
pretend they are at the L3 when there is a single L3 and a single NUMA node.
This is because the topology needs to match, the same cpus are affine to the L3
instance and corresponding NUMA instance and the traffic measured also needs to
match. If there is multiple NUMA and L3 then cross NUMA traffic means that the
traffic seen at the NUMA node is different from what is seen at the L3.

If a workload runs on cpus affine to the L3, instance A, but allows cross NUMA
traffic then the memory bandwidth leaving the L3, instance A, will be different
from that entering NUMA instance A.

L3_A --> NUMA_A
     \
      \
       🡖
L3_B     NUMA_B

This is still the case if the traffic goes via the L3 to the other NUMA node.

L3_A --> NUMA_A
   |
   |
   ⌄
L3_B --> NUMA_B

The future plan, is to add support for monitoring scoped to the NUMA node in
resctrl. This means we can we can more accurately expose the counters later on
without being held back by inaccurate descriptions. Ideally, we would have added
proper support for monitors at the memory scoped by NUMA node rather than adding
traffic_matches_l3() and topology_matches_l3(), which are there to allow us to
support platforms where the traffic entering the memory controller is the same
as that leaving the L3. To cope with the case when memory is powered down we
need to introduce memory hotplug locking to resctrl as well as the support for
understanding the NUMA scope.

> 
>  870 static bool traffic_matches_l3(struct mpam_class *class)
>  871 {
> ...
>  901
>  902         if (!cpumask_equal(tmp_cpumask, cpu_possible_mask)) {
>  903                 pr_debug("There is more than one L3\n");
>  904                 return false; *
>  905         }
> ...
>  912
>  913         if (num_possible_nodes() > 1) {
>  914                 pr_debug("There is more than one numa node\n");
>  915                 return false;  *
>  916         }
>  917
> ...
>  926 }
> 
> 
> Also, I'd also like to confirm one more thing.
> The mpam_resctrl_pick_mba() function also calls traffic_matches_l3(). 
> This suggests that, except in scenarios where the entire system has a single L3 cache and a single NUMA node (ABMC is disabled),
> the Memory Bandwidth allocation will also be disabled.
> Is this the intended behavior? If so, could you explain why?

Yes, but only when the memory allocation control, mbw_max, is on the memory
controller and not the L3 cache. There is no restriction when the MSC is on the
L3 cache. This is for the same reasons as for the memory bandwidth counters. The
traffic that we say is coming from the L3 may not be the same as that entering
the memory controller and resctrl assumes that the MBA is at the L3. Memory
hotplug locking in resctrl would also be needed here.

Reinete is creating a proof of concept for a structured way to add new schemata
into resctrl, some discussion of initial ideas [1]. I'm also looking to see how
the generic schemata ideas can fit with MPAM and what resctrl support we'd require.

Zeng has indicated [2] that he might look into adding the MB support at NUMA nodes.

I hope this makes things a bit clearer.

[1] https://lore.kernel.org/lkml/fb1e2686-237b-4536-acd6-15159abafcba@intel.com/
[2]
https://lore.kernel.org/linux-arm-kernel/f6f865bc-319c-8944-9989-4fd83a59d4b8@huawei.com/

Thanks,

Ben

> 
> Best regards,
> Shaopeng TAN
> 
> 
> 
> 



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

* Re: [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors
  2026-05-12  7:16   ` Shaopeng Tan (Fujitsu)
@ 2026-05-12  9:43     ` Ben Horgan
  0 siblings, 0 replies; 11+ messages in thread
From: Ben Horgan @ 2026-05-12  9:43 UTC (permalink / raw)
  To: Shaopeng Tan (Fujitsu)
  Cc: amitsinght@marvell.com, baisheng.gao@unisoc.com,
	baolin.wang@linux.alibaba.com, carl@os.amperecomputing.com,
	dave.martin@arm.com, david@kernel.org, dfustini@baylibre.com,
	fenghuay@nvidia.com, gshan@redhat.com, james.morse@arm.com,
	jonathan.cameron@huawei.com, kobak@nvidia.com,
	lcherian@marvell.com, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, peternewman@google.com,
	punit.agrawal@oss.qualcomm.com, quic_jiles@quicinc.com,
	reinette.chatre@intel.com, rohit.mathew@arm.com,
	scott@os.amperecomputing.com, sdonthineni@nvidia.com,
	xhao@linux.alibaba.com, zengheng4@huawei.com, x86@kernel.org

Hi Shaopeng,

Thanks for the quick review.

On 5/12/26 08:16, Shaopeng Tan (Fujitsu) wrote:
> Hello Ben,
> 
> 
>> MPAM is able to emulate ABMC, i.e. mbm_event mode, by making memory
>> bandwidth monitors assignable. Rather than supporting the 'default'
>> mbm_assign_mode always use 'mbm_event' mode even if there are sufficient
>> memory bandwidth monitors. The per monitor event configuration is only
>> provided by resctrl when in 'mbm_event' mode and so only allowing
>> 'mbm_event' mode will make it easier to support per-monitor event
>> configuration for MPAM. For the moment, the only event supported is
>> mbm_total_event with no bandwidth type configuration. The 'mbm_assign_mode'
>> file will still show 'default' when there is no support for memory
>> bandwidth monitoring.
>>
>> The monitors need to be allocated from the driver, and mapped to whichever
>> control/monitor group resctrl wants to use them with.
>>
>> Add a second array to hold the monitor values indexed by resctrl's cntr_id.
>>
>> When CDP is in use, two monitors are needed so the available number of
>> counters halves. Platforms with one monitor will have zero monitors when
>> CDP is in use.
>>
>> Co-developed-by: James Morse <james.morse@arm.com>
>> Signed-off-by: James Morse <james.morse@arm.com>
>> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
>> ---
>> Changes since rfc v1:
>> abmc enabled even if enough counters
>> Helpers from dropped free running commits
>> carry on with zero counters if using cdp
>> set config bits
>> use kmalloc_objs
>> drop tags for rework
>> Configure mbm_cntr_configurable, mbm_cntr_assign_fixed
>>
>> Changes since rfc v2:
>> Don't set mon->assigned_counters to an error pointer
>> Fix mpam_resctrl_teardown_mon()
>> Remove free running check
>> Separate cleanup allocations, e.g. __free(), from the rest
>> Restrict scope on err in mpam_resctrl_monitor_init()
>> ---
>>  drivers/resctrl/mpam_internal.h |   6 +-
>>  drivers/resctrl/mpam_resctrl.c  | 138 +++++++++++++++++++++++++++++++-
>>  2 files changed, 140 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
>> index 1914aefdcba9..7a166b395b5a 100644
>> --- a/drivers/resctrl/mpam_internal.h
>> +++ b/drivers/resctrl/mpam_internal.h
>> @@ -411,7 +411,11 @@ struct mpam_resctrl_res {
>>  struct mpam_resctrl_mon {
>>          struct mpam_class       *class;
>>  
>> -       /* per-class data that resctrl needs will live here */
>> +       /* Array of allocated MBWU monitors, indexed by (closid, rmid). */
>> +       int                     *mbwu_idx_to_mon;
>> +
>> +       /* Array of assigned MBWU monitors, indexed by idx argument. */
>> +       int                     *assigned_counters;
>>  };
>>  
>>  static inline int mpam_alloc_csu_mon(struct mpam_class *class)
>> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
>> index f70fa65d39e4..cba295621f56 100644
>> --- a/drivers/resctrl/mpam_resctrl.c
>> +++ b/drivers/resctrl/mpam_resctrl.c
>> @@ -75,6 +75,8 @@ static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready);
>>   */
>>  static bool resctrl_enabled;
>>  
>> +static unsigned int l3_num_allocated_mbwu = ~0;
>> +
>>  bool resctrl_arch_alloc_capable(void)
>>  {
>>          struct mpam_resctrl_res *res;
>> @@ -140,7 +142,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
>>  
>>  bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r)
>>  {
>> -       return false;
>> +       return (r == &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res);
>>  }
>>  
>>  int resctrl_arch_mbm_cntr_assign_set(struct rdt_resource *r, bool enable)
>> @@ -185,6 +187,22 @@ static void resctrl_reset_task_closids(void)
>>          read_unlock(&tasklist_lock);
>>  }
>>  
>> +static void mpam_resctrl_monitor_sync_abmc_vals(struct rdt_resource *l3)
>> +{
>> +       l3->mon.num_mbm_cntrs = l3_num_allocated_mbwu;
>> +       if (cdp_enabled)
>> +               l3->mon.num_mbm_cntrs /= 2;
>> +
>> +       /*
>> +        * Continue as normal even if there are zero counters to avoid giving
>> +        * resctrl mixed messages.
>> +        */
>> +       l3->mon.mbm_cntr_assignable = true;
>> +       l3->mon.mbm_assign_on_mkdir = true;
>> +       l3->mon.mbm_cntr_configurable = false;
>> +       l3->mon.mbm_cntr_assign_fixed = true;
>> +}
>> +
>>  int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
>>  {
>>          u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID;
>> @@ -244,6 +262,7 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
>>          WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current));
>>  
>>          resctrl_reset_task_closids();
>> +       mpam_resctrl_monitor_sync_abmc_vals(l3);
>>  
>>          for_each_possible_cpu(cpu)
>>                  mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0);
>> @@ -613,6 +632,9 @@ static bool class_has_usable_mbwu(struct mpam_class *class)
>>          if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
>>                  return false;
>>  
>> +       if (!cprops->num_mbwu_mon)
>> +               return false;
>> +
>>          return true;
>>  }
>>  
>> @@ -935,6 +957,52 @@ static void mpam_resctrl_pick_mba(void)
>>          }
>>  }
>>  
>> +static void __free_mbwu_mon(struct mpam_class *class, int *array,
>> +                           u16 num_mbwu_mon)
>> +{
>> +       for (int i = 0; i < num_mbwu_mon; i++) {
>> +               if (array[i] < 0)
>> +                       continue;
>> +
>> +               mpam_free_mbwu_mon(class, array[i]);
>> +               array[i] = ~0;
>> +       }
>> +}
>> +
>> +static int __alloc_mbwu_mon(struct mpam_class *class, int *array,
>> +                           u16 num_mbwu_mon)
>> +{
>> +       for (int i = 0; i < num_mbwu_mon; i++) {
>> +               int mbwu_mon = mpam_alloc_mbwu_mon(class);
>> +
>> +               if (mbwu_mon < 0) {
>> +                       __free_mbwu_mon(class, array, num_mbwu_mon);
>> +                       return mbwu_mon;
>> +               }
>> +               array[i] = mbwu_mon;
>> +       }
>> +
>> +       l3_num_allocated_mbwu = min(l3_num_allocated_mbwu, num_mbwu_mon);
>> +
>> +       return 0;
>> +}
>> +
>> +static int *__alloc_mbwu_array(struct mpam_class *class, u16 num_mbwu_mon)
>> +{
>> +       int err;
>> +
>> +       int *array __free(kfree) = kmalloc_objs(*array, num_mbwu_mon);
>> +       if (!array)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       memset(array, -1, num_mbwu_mon * sizeof(*array));
>> +
>> +       err = __alloc_mbwu_mon(class, array, num_mbwu_mon);
>> +       if (err)
>> +               return ERR_PTR(err);
>> +       return_ptr(array);
>> +}
>> +
>>  static void counter_update_class(enum resctrl_event_id evt_id,
>>                                   struct mpam_class *class)
>>  {
>> @@ -1089,6 +1157,38 @@ static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp)
>>          return comp->comp_id;
>>  }
>>  
>> +/*
>> + * This must run after all event counters have been picked so that any free
>> + * running counters have already been allocated.
>> + */
>> +static int mpam_resctrl_monitor_init_abmc(struct mpam_resctrl_mon *mon)
>> +{
>> +       struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
>> +       struct rdt_resource *l3 = &res->resctrl_res;
>> +       struct mpam_class *class = mon->class;
>> +       u16 num_mbwu_mon;
>> +       size_t num_rmid = resctrl_arch_system_num_rmid_idx();
>> +       int *cntrs;
> 
> Wouldn't a reverse tree format be better?

Do you mean just moving the num_rmid line up? If so, yes, that is a bit neater.

	struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
	size_t num_rmid = resctrl_arch_system_num_rmid_idx();
	struct rdt_resource *l3 = &res->resctrl_res;
	struct mpam_class *class = mon->class;
	u16 num_mbwu_mon;
	int *cntrs;

> 
>> +       int *rmid_array __free(kfree) = kmalloc_objs(*rmid_array, num_rmid);
>> +       if (!rmid_array) {
>> +               pr_debug("Failed to allocate RMID array\n");
>> +               return -ENOMEM;
>> +       }
>> +       memset(rmid_array, -1, num_rmid * sizeof(*rmid_array));
>> +
>> +       num_mbwu_mon = class->props.num_mbwu_mon;
>> +       cntrs = __alloc_mbwu_array(mon->class, num_mbwu_mon);
>> +       if (IS_ERR(cntrs))
>> +               return PTR_ERR(cntrs);
>> +       mon->assigned_counters = cntrs;
>> +       mon->mbwu_idx_to_mon = no_free_ptr(rmid_array);
>> +
>> +       mpam_resctrl_monitor_sync_abmc_vals(l3);
>> +
>> +       return 0;
>> +}
>> +
>>  static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
>>                                       enum resctrl_event_id type)
>>  {
>> @@ -1133,8 +1233,21 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
>>           */
>>          l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
>>  
>> -       if (resctrl_enable_mon_event(type, false, 0, NULL))
>> -               l3->mon_capable = true;
>> +       if (type == QOS_L3_MBM_TOTAL_EVENT_ID) {
>> +               int err;
>> +
>> +               err = mpam_resctrl_monitor_init_abmc(mon);
>> +               if (err)
>> +                       return err;
>> +
>> +               static_assert(MAX_EVT_CONFIG_BITS == 0x7f);
>> +               l3->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
>> +       }
>> +
>> +       if (!resctrl_enable_mon_event(type, false, 0, NULL))
>> +               return -EINVAL;
>> +
>> +       l3->mon_capable = true;
>>  
>>          return 0;
>>  }
>> @@ -1697,6 +1810,23 @@ void mpam_resctrl_exit(void)
>>          resctrl_exit();
>>  }
>>  
>> +static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
>> +{
>> +       u32 num_mbwu_mon = l3_num_allocated_mbwu;
>> +
>> +       if (mon->mbwu_idx_to_mon)
>> +               return;
> 
>  Isn't it 'if (mon->mbwu_idx_to_mon == NULL)' ?

Oops... indeed.

Thanks,

Ben

>  
>  
>  Best regards,
>  Shaopeng TAN
> 
>> +       if (mon->assigned_counters) {
>> +               __free_mbwu_mon(class, mon->assigned_counters, num_mbwu_mon);
>> +               kfree(mon->assigned_counters);
>> +               mon->assigned_counters = NULL;
>> +       }
>> +
>> +       kfree(mon->mbwu_idx_to_mon);
>> +       mon->mbwu_idx_to_mon = NULL;
>> +}
>> +
>>  /*
>>   * The driver is detaching an MSC from this class, if resctrl was using it,
>>   * pull on resctrl_exit().
>> @@ -1719,6 +1849,8 @@ void mpam_resctrl_teardown_class(struct mpam_class *class)
>>          for_each_mpam_resctrl_mon(mon, eventid) {
>>                  if (mon->class == class) {
>>                          mon->class = NULL;
>> +
>> +                       mpam_resctrl_teardown_mon(mon, class);
>>                          break;
>>                  }
>>          }
>> --
>> 2.43.0



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

end of thread, other threads:[~2026-05-12  9:43 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-11 15:41 [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan
2026-05-11 15:41 ` [PATCH v3 1/5] arm_mpam: resctrl: Pick classes for use as mbm counters Ben Horgan
2026-05-12  6:50   ` Shaopeng Tan (Fujitsu)
2026-05-12  9:21     ` Ben Horgan
2026-05-11 15:41 ` [PATCH v3 2/5] arm_mpam: resctrl: Pre-allocate assignable monitors Ben Horgan
2026-05-12  7:16   ` Shaopeng Tan (Fujitsu)
2026-05-12  9:43     ` Ben Horgan
2026-05-11 15:41 ` [PATCH v3 3/5] arm_mpam: resctrl: Add resctrl_arch_config_cntr() for ABMC use Ben Horgan
2026-05-11 15:41 ` [PATCH v3 4/5] arm_mpam: resctrl: Add resctrl_arch_cntr_read() & resctrl_arch_reset_cntr() Ben Horgan
2026-05-11 15:41 ` [PATCH v3 5/5] arm64: mpam: Add memory bandwidth usage (MBWU) documentation Ben Horgan
2026-05-11 15:51 ` [PATCH v3 0/5] arm_mpam: resctrl: Counter Assignment (ABMC) Ben Horgan

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