mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors
@ 2026-10-22  8:48 Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 1/7] fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources Jinjie Ruan
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

MPAM resource monitors hang off a partitionable resource in an MSC, not
off a particular cache level. resctrl only knows about the L3 scope, so
the L2 cache monitors that MPAM platforms implement are discovered by
the driver but never show up in resctrl.

An L2 MSC has the same CSU and MBWU counters as an L3 MSC; the cache
level comes from the ACPI MPAM table's cache locator. So this is mostly
a resctrl scope problem, not a hardware one.

Extend resctrl's monitor scope to L2: register the L2 occupancy and
bandwidth events against the L2 resource, pick the L2 classes for them,
and borrow a CSU/MBWU monitor for the duration of a read. With CDP the
two PARTIDs need separate accumulating monitors, so the borrowed-monitor
context becomes one monitor per PARTID.

Patches:

  1-2  Generalise the per-domain read path, add l2c_occupancy and
       mbm_core_bytes, update the documentation.

  3    Split the borrowed-monitor context into data and code entries.

  4    Do the CDP num_rmid halving for every mon_capable resource.

  5-7  Pick the L2 classes, describe the events with the L2 resource,
       add the read paths.

Tested on QEMU with an L2/L3/memory-controller MSC topology. The L2_MON
files show up and are readable; l2c_occupancy reports the modelled
occupancy, and mbm_core_bytes reports the modelled bandwidth.

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

Jinjie Ruan (7):
  fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources
  fs/resctrl: Add the L2 monitor events
  arm_mpam: resctrl: Split monitor context for code/data
  arm_mpam: resctrl: Halve num_rmid for every mon_capable resource on
    CDP
  arm_mpam: resctrl: Describe the L2 monitor events with the L2 resource
  arm_mpam: resctrl: Pick L2 classes for the L2 monitor events
  arm_mpam: resctrl: Add the L2 monitor read paths

 Documentation/arch/arm64/mpam.rst |  23 +--
 drivers/resctrl/mpam_resctrl.c    | 236 +++++++++++++++++++++++-------
 fs/resctrl/monitor.c              |  15 +-
 include/linux/resctrl_types.h     |  13 +-
 4 files changed, 222 insertions(+), 65 deletions(-)

-- 
2.43.0


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

* [RFC PATCH 1/7] fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 2/7] fs/resctrl: Add the L2 monitor events Jinjie Ruan
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

__l3_mon_event_count() reads a single monitor domain. The L3 resource
name hardcoded in its domain_header_is_valid() check is the only thing
tying it to the L3 - all other access is through rr->r. Drop the
hardcode in favour of rr->r->rid and rename it to
__mon_event_count_one_domain() so other resources can reuse it.

No functional change.

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

diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c
index 73413cb128ea..f9f4d4486c1b 100644
--- a/fs/resctrl/monitor.c
+++ b/fs/resctrl/monitor.c
@@ -422,7 +422,7 @@ static void mbm_cntr_free(struct rdt_l3_mon_domain *d, int cntr_id)
 	memset(&d->cntr_cfg[cntr_id], 0, sizeof(*d->cntr_cfg));
 }
 
-static int __l3_mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr)
+static int __mon_event_count_one_domain(struct rdtgroup *rdtgrp, struct rmid_read *rr)
 {
 	int cpu = smp_processor_id();
 	u32 closid = rdtgrp->closid;
@@ -432,7 +432,7 @@ static int __l3_mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr)
 	struct mbm_state *m;
 	u64 tval = 0;
 
-	if (!domain_header_is_valid(rr->hdr, RESCTRL_MON_DOMAIN, RDT_RESOURCE_L3)) {
+	if (!domain_header_is_valid(rr->hdr, RESCTRL_MON_DOMAIN, rr->r->rid)) {
 		rr->err = -EIO;
 		return -EINVAL;
 	}
@@ -540,7 +540,7 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr)
 	case RDT_RESOURCE_L3:
 		WARN_ON_ONCE(rr->evt->any_cpu);
 		if (rr->hdr)
-			return __l3_mon_event_count(rdtgrp, rr);
+			return __mon_event_count_one_domain(rdtgrp, rr);
 		else
 			return __l3_mon_event_count_sum(rdtgrp, rr);
 	case RDT_RESOURCE_PERF_PKG: {
-- 
2.43.0


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

* [RFC PATCH 2/7] fs/resctrl: Add the L2 monitor events
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 1/7] fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-26 11:50   ` Ben Horgan
  2026-10-22  8:48 ` [RFC PATCH 3/7] arm_mpam: resctrl: Split monitor context for code/data Jinjie Ruan
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

Add the L2 cache occupancy (l2c_occupancy) and L2 bandwidth
(mbm_core_bytes) monitor events, and route RDT_RESOURCE_L2 through the
same per-domain read path as the L3.

As ARM MPAM Specification said, MPAM resource monitors are associated
with a shared partitionable resource in an MSC, not with a particular
cache level: the level comes from firmware's description of the MSC.
L2 monitors were only missing a resctrl scope. The events are
exposed once an architecture reports the L2 resource
mon_capable; x86 never does.

Update Documentation/arch/arm64/mpam.rst to mention L2.

Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
 Documentation/arch/arm64/mpam.rst | 23 ++++++++++++++---------
 fs/resctrl/monitor.c              |  9 +++++++++
 include/linux/resctrl_types.h     | 13 ++++++++++++-
 3 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/Documentation/arch/arm64/mpam.rst b/Documentation/arch/arm64/mpam.rst
index 67fe515ed501..f0697dedde03 100644
--- a/Documentation/arch/arm64/mpam.rst
+++ b/Documentation/arch/arm64/mpam.rst
@@ -32,8 +32,9 @@ Because the user interface to MPAM is via resctrl, only MPAM features that are
 compatible with resctrl can be exposed to user-space.
 
 MSC are considered as a group based on the topology. MSC that correspond with
-the L3 cache are considered together, it is not possible to mix MSC between L2
-and L3 to 'cover' a resctrl schema.
+the L3 cache are considered together, as are MSC that correspond with the L2
+cache; it is not possible to mix MSC between L2 and L3 to 'cover' a resctrl
+schema.
 
 The supported features are:
 
@@ -61,9 +62,11 @@ The supported features are:
   When the MPAM driver finds multiple groups of MSC it can use for the 'MB'
   schema, it prefers the group closest to the L3 cache.
 
-* Cache Storage Usage (CSU) counters can expose the 'llc_occupancy' provided
-  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.
+* Cache Storage Usage (CSU) counters expose the occupancy of the cache they
+  monitor: 'llc_occupancy' for the L3 group and 'l2c_occupancy' for the L2
+  group, provided there is at least one CSU monitor on each MSC that makes up
+  the 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
@@ -82,10 +85,12 @@ The supported features are:
   have a corresponding L3 cache. 'mbm_local_bytes' is not exposed as MPAM
   cannot distinguish local traffic from global traffic.
 
-  All these restrictions based on L3 cache are due to resctrl, currently, only
-  supporting monitoring at the L3 scope. It is expected that going forward more
-  MBWU monitors can be exposed to the user after support for more monitoring
-  scopes is added to resctrl.
+  MBWU monitors on an L2 MSC expose the bandwidth the cache sees as the
+  'mbm_core_bytes' event on the L2 resource.
+
+  These restrictions are due to resctrl only supporting monitoring at the L2
+  and L3 cache scopes. MBWU monitors on caches at other levels, or on devices,
+  are not exposed.
 
 Reporting Bugs
 ==============
diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c
index f9f4d4486c1b..ee296c27c6e8 100644
--- a/fs/resctrl/monitor.c
+++ b/fs/resctrl/monitor.c
@@ -543,6 +543,13 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr)
 			return __mon_event_count_one_domain(rdtgrp, rr);
 		else
 			return __l3_mon_event_count_sum(rdtgrp, rr);
+	case RDT_RESOURCE_L2:
+		WARN_ON_ONCE(rr->evt->any_cpu);
+		if (WARN_ON_ONCE(!rr->hdr)) {
+			rr->err = -EINVAL;
+			return -EINVAL;
+		}
+		return __mon_event_count_one_domain(rdtgrp, rr);
 	case RDT_RESOURCE_PERF_PKG: {
 		u64 tval = 0;
 
@@ -1032,6 +1039,8 @@ struct mon_evt mon_event_all[QOS_NUM_EVENTS] = {
 	MON_EVENT(QOS_L3_OCCUP_EVENT_ID,		"llc_occupancy",	RDT_RESOURCE_L3,	false),
 	MON_EVENT(QOS_L3_MBM_TOTAL_EVENT_ID,		"mbm_total_bytes",	RDT_RESOURCE_L3,	false),
 	MON_EVENT(QOS_L3_MBM_LOCAL_EVENT_ID,		"mbm_local_bytes",	RDT_RESOURCE_L3,	false),
+	MON_EVENT(QOS_L2_OCCUP_EVENT_ID,		"l2c_occupancy",	RDT_RESOURCE_L2,	false),
+	MON_EVENT(QOS_L2_MBM_CORE_EVENT_ID,		"mbm_core_bytes",	RDT_RESOURCE_L2,	false),
 	MON_EVENT(PMT_EVENT_ENERGY,			"core_energy",		RDT_RESOURCE_PERF_PKG,	true),
 	MON_EVENT(PMT_EVENT_ACTIVITY,			"activity",		RDT_RESOURCE_PERF_PKG,	true),
 	MON_EVENT(PMT_EVENT_STALLS_LLC_HIT,		"stalls_llc_hit",	RDT_RESOURCE_PERF_PKG,	false),
diff --git a/include/linux/resctrl_types.h b/include/linux/resctrl_types.h
index a5f56faa18d2..f31b8f2ccb8b 100644
--- a/include/linux/resctrl_types.h
+++ b/include/linux/resctrl_types.h
@@ -37,7 +37,14 @@
 /* Number of memory transactions that an MBM event can be configured with */
 #define NUM_MBM_TRANSACTIONS		7
 
-/* Event IDs */
+/*
+ * Monitor Event IDs, representative a variety of monitoring events:
+ * QOS_L3_OCCUP_EVENT_ID:     L3 Cache Occupancy statistics event
+ * QOS_L3_MBM_TOTAL_EVENT_ID: Global Memory Bandwidth statistics event
+ * QOS_L3_MBM_LOCAL_EVENT_ID: L3 Cache Bandwidth statistics event
+ * QOS_L2_OCCUP_EVENT_ID:     L2 Cache Occupancy statistics event
+ * QOS_L2_MBM_CORE_EVENT_ID:  L2 Cache Bandwidth statistics event
+ */
 enum resctrl_event_id {
 	/* Must match value of first event below */
 	QOS_FIRST_EVENT			= 0x01,
@@ -50,6 +57,10 @@ enum resctrl_event_id {
 	QOS_L3_MBM_TOTAL_EVENT_ID	= 0x02,
 	QOS_L3_MBM_LOCAL_EVENT_ID	= 0x03,
 
+	/* ARM MPAM L2 Events */
+	QOS_L2_OCCUP_EVENT_ID,
+	QOS_L2_MBM_CORE_EVENT_ID,
+
 	/* Intel Telemetry Events */
 	PMT_EVENT_ENERGY,
 	PMT_EVENT_ACTIVITY,
-- 
2.43.0


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

* [RFC PATCH 3/7] arm_mpam: resctrl: Split monitor context for code/data
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 1/7] fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 2/7] fs/resctrl: Add the L2 monitor events Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 4/7] arm_mpam: resctrl: Halve num_rmid for every mon_capable resource on CDP Jinjie Ruan
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

The arch_mon_ctx passed between resctrl and the driver was a single
monitor index. CDP needs one accumulating monitor per PARTID, as
reprogramming one monitor's filter between the code and data PARTIDs
resets the counter on every read.

Turn the context into a u32[2] indexed by MON_CTX_DATA and MON_CTX_CODE
so a caller can borrow one monitor per PARTID. The L3 flows keep using
a single monitor for both entries.

No functional change.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 9d223057953a..9ffee18e657c 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -372,7 +372,18 @@ struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l)
 	return &mpam_resctrl_controls[l].resctrl_res;
 }
 
-static int resctrl_arch_mon_ctx_alloc_no_wait(enum resctrl_event_id evtid)
+/*
+ * The arch_mon_ctx for borrowed monitors is a u32[2]: index 0 selects the
+ * monitor for CDP_DATA (and non-CDP) reads, index 1 the monitor for CDP_CODE.
+ * With CDP each partid needs its own accumulating monitor; reprogramming a
+ * single monitor's filter for each sub-read would reset the counter every
+ * time. For the pre-allocated events both entries hold USE_PRE_ALLOCATED.
+ */
+#define MON_CTX_DATA	0
+#define MON_CTX_CODE	1
+
+static int resctrl_arch_mon_ctx_alloc_no_wait(enum resctrl_event_id evtid,
+					      u32 *mon_ctx)
 {
 	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[evtid];
 
@@ -385,9 +396,14 @@ static int resctrl_arch_mon_ctx_alloc_no_wait(enum resctrl_event_id evtid)
 	switch (evtid) {
 	case QOS_L3_OCCUP_EVENT_ID:
 		/* With CDP, one monitor gets used for both code/data reads */
-		return mpam_alloc_csu_mon(mon->class);
+		mon_ctx[MON_CTX_DATA] = mpam_alloc_csu_mon(mon->class);
+		mon_ctx[MON_CTX_CODE] = mon_ctx[MON_CTX_DATA];
+		return mon_ctx[MON_CTX_DATA];
 	case QOS_L3_MBM_LOCAL_EVENT_ID:
 	case QOS_L3_MBM_TOTAL_EVENT_ID:
+		/* Served by the pre-assigned ABMC counters */
+		mon_ctx[MON_CTX_DATA] = USE_PRE_ALLOCATED;
+		mon_ctx[MON_CTX_CODE] = USE_PRE_ALLOCATED;
 		return USE_PRE_ALLOCATED;
 	default:
 		return -EOPNOTSUPP;
@@ -398,16 +414,16 @@ void *resctrl_arch_mon_ctx_alloc(struct rdt_resource *r,
 				 enum resctrl_event_id evtid)
 {
 	DEFINE_WAIT(wait);
-	int *ret;
+	u32 *ret;
 
-	ret = kmalloc_obj(*ret);
+	ret = kmalloc_array(2, sizeof(*ret), GFP_KERNEL);
 	if (!ret)
 		return ERR_PTR(-ENOMEM);
 
 	do {
 		prepare_to_wait(&resctrl_mon_ctx_waiters, &wait,
 				TASK_INTERRUPTIBLE);
-		*ret = resctrl_arch_mon_ctx_alloc_no_wait(evtid);
+		*ret = resctrl_arch_mon_ctx_alloc_no_wait(evtid, ret);
 		if (*ret == -ENOSPC)
 			schedule();
 	} while (*ret == -ENOSPC && !signal_pending(current));
@@ -417,7 +433,7 @@ void *resctrl_arch_mon_ctx_alloc(struct rdt_resource *r,
 }
 
 static void resctrl_arch_mon_ctx_free_no_wait(enum resctrl_event_id evtid,
-					      u32 mon_idx)
+					      const u32 *mon_ctx)
 {
 	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[evtid];
 
@@ -428,7 +444,7 @@ static void resctrl_arch_mon_ctx_free_no_wait(enum resctrl_event_id evtid,
 		return;
 
 	if (evtid == QOS_L3_OCCUP_EVENT_ID)
-		mpam_free_csu_mon(mon->class, mon_idx);
+		mpam_free_csu_mon(mon->class, mon_ctx[MON_CTX_DATA]);
 
 	wake_up(&resctrl_mon_ctx_waiters);
 }
@@ -436,11 +452,9 @@ static void resctrl_arch_mon_ctx_free_no_wait(enum resctrl_event_id evtid,
 void resctrl_arch_mon_ctx_free(struct rdt_resource *r,
 			       enum resctrl_event_id evtid, void *arch_mon_ctx)
 {
-	u32 mon_idx = *(u32 *)arch_mon_ctx;
+	resctrl_arch_mon_ctx_free_no_wait(evtid, arch_mon_ctx);
 
 	kfree(arch_mon_ctx);
-
-	resctrl_arch_mon_ctx_free_no_wait(evtid, mon_idx);
 }
 
 static int __read_mon(struct mpam_resctrl_mon *mon, struct mpam_component *mon_comp,
@@ -481,18 +495,18 @@ static int __read_mon(struct mpam_resctrl_mon *mon, struct mpam_component *mon_c
 
 static int read_mon_cdp_safe(struct mpam_resctrl_mon *mon, struct mpam_component *mon_comp,
 			     enum mpam_device_features mon_type,
-			     int mon_idx, u32 closid, u32 rmid, u64 *val)
+			     const u32 *mon_ctx, u32 closid, u32 rmid, u64 *val)
 {
 	if (cdp_enabled) {
 		u64 code_val = 0, data_val = 0;
 		int err;
 
-		err = __read_mon(mon, mon_comp, mon_type, mon_idx,
+		err = __read_mon(mon, mon_comp, mon_type, mon_ctx[MON_CTX_CODE],
 				 CDP_CODE, closid, rmid, &code_val);
 		if (err)
 			return err;
 
-		err = __read_mon(mon, mon_comp, mon_type, mon_idx,
+		err = __read_mon(mon, mon_comp, mon_type, mon_ctx[MON_CTX_DATA],
 				 CDP_DATA, closid, rmid, &data_val);
 		if (err)
 			return err;
@@ -501,7 +515,7 @@ static int read_mon_cdp_safe(struct mpam_resctrl_mon *mon, struct mpam_component
 		return 0;
 	}
 
-	return __read_mon(mon, mon_comp, mon_type, mon_idx,
+	return __read_mon(mon, mon_comp, mon_type, mon_ctx[MON_CTX_DATA],
 			  CDP_NONE, closid, rmid, val);
 }
 
@@ -512,7 +526,6 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr,
 {
 	struct mpam_resctrl_dom *l3_dom;
 	struct mpam_component *mon_comp;
-	u32 mon_idx = *(u32 *)arch_mon_ctx;
 	enum mpam_device_features mon_type;
 	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[eventid];
 
@@ -532,7 +545,7 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr,
 
 	mon_type = mpam_feat_msmon_csu;
 
-	return read_mon_cdp_safe(mon, mon_comp, mon_type, mon_idx,
+	return read_mon_cdp_safe(mon, mon_comp, mon_type, arch_mon_ctx,
 				 closid, rmid, val);
 }
 
@@ -544,6 +557,8 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
 	struct mpam_resctrl_mon *mon = &mpam_resctrl_counters[eventid];
 	struct mpam_resctrl_dom *l3_dom;
 	struct mpam_component *mon_comp;
+	/* The pre-allocated counters resolve their monitor in __read_mon() */
+	u32 mon_ctx_prealloc[2] = { USE_PRE_ALLOCATED, USE_PRE_ALLOCATED };
 
 	if (!mpam_is_enabled())
 		return -EINVAL;
@@ -555,7 +570,7 @@ int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domain *d,
 	mon_comp = l3_dom->mon_comp[eventid];
 
 	return read_mon_cdp_safe(mon, mon_comp, mpam_feat_msmon_mbwu,
-				 USE_PRE_ALLOCATED, closid, rmid, val);
+				 mon_ctx_prealloc, closid, rmid, val);
 }
 
 static void __reset_mon(struct mpam_resctrl_mon *mon, struct mpam_component *mon_comp,
-- 
2.43.0


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

* [RFC PATCH 4/7] arm_mpam: resctrl: Halve num_rmid for every mon_capable resource on CDP
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
                   ` (2 preceding siblings ...)
  2026-10-22  8:48 ` [RFC PATCH 3/7] arm_mpam: resctrl: Split monitor context for code/data Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 5/7] arm_mpam: resctrl: Describe the L2 monitor events with the L2 resource Jinjie Ruan
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

CDP emulation consumes two PARTIDs per closid, halving the PARTIDs
available for monitoring. resctrl_arch_set_cdp_enabled() only updated
the L3 resource's num_rmid, which is fine today but leaves the L2
monitor's num_rmids stale once L2 monitoring exists.

Loop over all mon_capable resources and halve each one's num_rmid.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 9ffee18e657c..efb9ce76acb4 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -189,8 +189,9 @@ static void mpam_resctrl_monitor_sync_abmc_vals(struct rdt_resource *l3)
 int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
 {
 	u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID;
-	struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
-	struct rdt_resource *l3 = &res->resctrl_res;
+	struct rdt_resource *l3 = &mpam_resctrl_controls[RDT_RESOURCE_L3].resctrl_res;
+	struct mpam_resctrl_res *res;
+	enum resctrl_res_level i;
 	int cpu;
 
 	if (!IS_ENABLED(CONFIG_EXPERT) && enable) {
@@ -212,10 +213,22 @@ int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable)
 	cdp_enabled = enable;
 	mpam_resctrl_controls[rid].cdp_enabled = enable;
 
-	if (enable)
-		l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx() / 2;
-	else
-		l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
+	/*
+	 * CDP emulation consumes two PARTIDs per closid, halving the PARTIDs
+	 * available for monitoring - no matter which resource the mount
+	 * option was for. Update num_rmid for all mon_capable resources,
+	 * e.g. the L2 monitors as well as the L3.
+	 */
+	for_each_mpam_resctrl_control(res, i) {
+		struct rdt_resource *r = &res->resctrl_res;
+
+		if (!r->mon_capable)
+			continue;
+
+		r->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
+		if (enable)
+			r->mon.num_rmid /= 2;
+	}
 
 	/* The mbw_max feature can't hide cdp as it's a per-partid maximum. */
 	if (cdp_enabled && !mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled)
-- 
2.43.0


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

* [RFC PATCH 5/7] arm_mpam: resctrl: Describe the L2 monitor events with the L2 resource
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
                   ` (3 preceding siblings ...)
  2026-10-22  8:48 ` [RFC PATCH 4/7] arm_mpam: resctrl: Halve num_rmid for every mon_capable resource on CDP Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 6/7] arm_mpam: resctrl: Pick L2 classes for the L2 monitor events Jinjie Ruan
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

mpam_resctrl_monitor_init() forced every event onto the L3 resource.
Describe the L2 events with the L2 resource instead: the occupancy and
bandwidth events of an L2 cache belong to the L2 resource, which keeps
info/L2_MON in step with the hardware it measures.

No behaviour change yet: the events are only initialised once the caller
starts iterating over them.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index efb9ce76acb4..50e5035dcc9d 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -1336,26 +1336,47 @@ static int mpam_resctrl_monitor_init_abmc(struct mpam_resctrl_mon *mon)
 static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 				     enum resctrl_event_id type)
 {
-	struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
-	struct rdt_resource *l3 = &res->resctrl_res;
+	enum resctrl_res_level res_level;
+	struct mpam_resctrl_res *res;
+	struct rdt_resource *r;
+	int cache_level;
 
 	lockdep_assert_cpus_held();
 
+	switch (type) {
+	case QOS_L2_OCCUP_EVENT_ID:
+	case QOS_L2_MBM_CORE_EVENT_ID:
+		cache_level = 2;
+		res_level = RDT_RESOURCE_L2;
+		break;
+	case QOS_L3_OCCUP_EVENT_ID:
+	case QOS_L3_MBM_LOCAL_EVENT_ID:
+	case QOS_L3_MBM_TOTAL_EVENT_ID:
+		cache_level = 3;
+		res_level = RDT_RESOURCE_L3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	res = &mpam_resctrl_controls[res_level];
+	r = &res->resctrl_res;
+
 	/*
-	 * There also needs to be an L3 cache present.
+	 * There also needs to be a cache at the level we're describing.
 	 * The check just requires any online CPU and it can't go offline as we
 	 * hold the cpu lock.
 	 */
-	if (get_cpu_cacheinfo_id(raw_smp_processor_id(), 3) == -1)
+	if (get_cpu_cacheinfo_id(raw_smp_processor_id(), cache_level) == -1)
 		return 0;
 
 	/*
-	 * If there are no MPAM resources on L3, force it into existence.
-	 * topology_matches_l3() already ensures this looks like the L3.
-	 * The domain-ids will be fixed up by mpam_resctrl_domain_hdr_init().
+	 * If there are no MPAM resources at this level, force it into
+	 * existence so the counters have somewhere to live. The domain-ids
+	 * will be fixed up by mpam_resctrl_domain_hdr_init().
 	 */
 	if (!res->class) {
-		pr_warn_once("Faking L3 MSC to enable counters.\n");
+		pr_warn_once("Faking L%d MSC to enable counters.\n", cache_level);
 		res->class = mpam_resctrl_counters[type].class;
 	}
 
@@ -1364,8 +1385,13 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 	 * monitoring class.
 	 * Setting name is necessary on monitor only platforms.
 	 */
-	l3->name = "L3";
-	l3->mon_scope = RESCTRL_L3_CACHE;
+	if (cache_level == 3) {
+		r->name = "L3";
+		r->mon_scope = RESCTRL_L3_CACHE;
+	} else {
+		r->name = "L2";
+		r->mon_scope = RESCTRL_L2_CACHE;
+	}
 
 	/*
 	 * num-rmid is the upper bound for the number of monitoring groups that
@@ -1375,7 +1401,7 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 	 * this does mean userspace needs to know the architecture to correctly
 	 * interpret this value.
 	 */
-	l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
+	r->mon.num_rmid = resctrl_arch_system_num_rmid_idx();
 
 	if (type == QOS_L3_MBM_TOTAL_EVENT_ID) {
 		int err;
@@ -1385,13 +1411,13 @@ static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon,
 			return err;
 
 		static_assert(MAX_EVT_CONFIG_BITS == 0x7f);
-		l3->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
+		r->mon.mbm_cfg_mask = MAX_EVT_CONFIG_BITS;
 	}
 
 	if (!resctrl_enable_mon_event(type, false, 0, NULL))
 		return -EINVAL;
 
-	l3->mon_capable = true;
+	r->mon_capable = true;
 
 	return 0;
 }
-- 
2.43.0


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

* [RFC PATCH 6/7] arm_mpam: resctrl: Pick L2 classes for the L2 monitor events
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
                   ` (4 preceding siblings ...)
  2026-10-22  8:48 ` [RFC PATCH 5/7] arm_mpam: resctrl: Describe the L2 monitor events with the L2 resource Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-22  8:48 ` [RFC PATCH 7/7] arm_mpam: resctrl: Add the L2 monitor read paths Jinjie Ruan
  2026-10-26 11:46 ` [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Ben Horgan
  7 siblings, 0 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

mpam_resctrl_pick_counters() previously skipped every cache class that
was not the L3. Allow L2 classes through, route the CSU pick to the
l2c_occupancy event by cache level, and pick a class for the L2
bandwidth event from any L2 class with usable MBWU counters.

Grow MPAM_MAX_EVENT so the counters array, the monitor-init loop and
the per-domain mon_comp array cover the L2 events. Once a platform has
an L2 class the L2 events are described and enabled; the read paths
come next.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 50e5035dcc9d..293122c0d0d9 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -37,15 +37,16 @@ static struct mpam_resctrl_res mpam_resctrl_controls[RDT_NUM_RESOURCES];
 	     rid++, res = &mpam_resctrl_controls[rid])
 
 /*
- * The classes we've picked to map to resctrl events.
- * Resctrl believes all the worlds a Xeon, and these are all on the L3. This
- * array lets us find the actual class backing the event counters. e.g.
- * the only memory bandwidth counters may be on the memory controller, but to
- * make use of them, we pretend they are on L3. Restrict the events considered
- * to those supported by MPAM.
+ * The classes we've picked to map to resctrl events, indexed by event id.
+ * This array lets us find the actual class backing the event counters: the
+ * L2 events use the L2 class, while the L3 bandwidth counters may sit on the
+ * memory controller or another class close to the L3.
+ *
+ * The range also covers QOS_L3_MBM_LOCAL_EVENT_ID, which is left classless:
+ * MPAM cannot distinguish local from remote traffic.
  * Class pointer may be NULL.
  */
-#define MPAM_MAX_EVENT QOS_L3_MBM_TOTAL_EVENT_ID
+#define MPAM_MAX_EVENT QOS_L2_MBM_CORE_EVENT_ID
 static struct mpam_resctrl_mon mpam_resctrl_counters[MPAM_MAX_EVENT + 1];
 
 #define for_each_mpam_resctrl_mon(mon, eventid)					\
@@ -93,7 +94,10 @@ bool resctrl_arch_mon_capable(void)
 	struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3];
 	struct rdt_resource *l3 = &res->resctrl_res;
 
-	/* All monitors are presented as being on the L3 cache */
+	/*
+	 * resctrl monitoring is anchored on the L3 group; L2 monitors are
+	 * additional resources, and monitor-only platforms fake the L3 group.
+	 */
 	return l3->mon_capable;
 }
 
@@ -1128,9 +1132,13 @@ static void mpam_resctrl_pick_counters(void)
 	guard(srcu)(&mpam_srcu);
 	list_for_each_entry_srcu(class, &mpam_classes, classes_list,
 				 srcu_read_lock_held(&mpam_srcu)) {
-		/* The name of the resource is L3... */
-		if (class->type == MPAM_CLASS_CACHE && class->level != 3) {
-			pr_debug("class %u is a cache but not the L3", class->level);
+		/*
+		 * Cache counters are presented as the L2 or L3 resource,
+		 * matching the cache the MSCs were built with.
+		 */
+		if (class->type == MPAM_CLASS_CACHE && class->level != 2 &&
+		    class->level != 3) {
+			pr_debug("class %u is a cache but not L2 or L3", class->level);
 			continue;
 		}
 
@@ -1150,7 +1158,10 @@ static void mpam_resctrl_pick_counters(void)
 				if (update_rmid_limits(class))
 					break;
 
-				counter_update_class(QOS_L3_OCCUP_EVENT_ID, class);
+				counter_update_class(class->level == 2 ?
+						     QOS_L2_OCCUP_EVENT_ID :
+						     QOS_L3_OCCUP_EVENT_ID,
+						     class);
 				break;
 			default:
 				break;
@@ -1174,6 +1185,19 @@ static void mpam_resctrl_pick_counters(void)
 			 */
 			counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
 		}
+
+		/*
+		 * An L2 MSC measures the bandwidth of the traffic its cache
+		 * sees from the CPUs that share it, which is what resctrl
+		 * presents as the per-L2 mbm_core_bytes event.
+		 */
+		if (class->type == MPAM_CLASS_CACHE && class->level == 2 &&
+		    class_has_usable_mbwu(class)) {
+			pr_debug("class %u has usable MBWU for the L2 resource\n",
+				 class->level);
+
+			counter_update_class(QOS_L2_MBM_CORE_EVENT_ID, class);
+		}
 	}
 }
 
-- 
2.43.0


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

* [RFC PATCH 7/7] arm_mpam: resctrl: Add the L2 monitor read paths
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
                   ` (5 preceding siblings ...)
  2026-10-22  8:48 ` [RFC PATCH 6/7] arm_mpam: resctrl: Pick L2 classes for the L2 monitor events Jinjie Ruan
@ 2026-10-22  8:48 ` Jinjie Ruan
  2026-10-26 11:46 ` [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Ben Horgan
  7 siblings, 0 replies; 10+ messages in thread
From: Jinjie Ruan @ 2026-10-22  8:48 UTC (permalink / raw)
  To: catalin.marinas, will, mark.rutland, corbet, skhan, rdunlap,
	james.morse, ben.horgan, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel
  Cc: ruanjinjie

Wire the L2 events through the borrowed-monitor paths:
l2c_occupancy borrows a CSU monitor like the L3 occupancy event, while
mbm_core_bytes borrows a bandwidth monitor. The L2 bandwidth event is
outside resctrl's L3 MBM event range, so the assignable-counter
machinery never applies to it; with CDP, borrow one monitor per partid
so code and data both accumulate.

Accept both events in resctrl_arch_rmid_read() and skip them in the
assignable-counter paths.

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

diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index 293122c0d0d9..3aa7ea996511 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -412,6 +412,7 @@ static int resctrl_arch_mon_ctx_alloc_no_wait(enum resctrl_event_id evtid,
 
 	switch (evtid) {
 	case QOS_L3_OCCUP_EVENT_ID:
+	case QOS_L2_OCCUP_EVENT_ID:
 		/* With CDP, one monitor gets used for both code/data reads */
 		mon_ctx[MON_CTX_DATA] = mpam_alloc_csu_mon(mon->class);
 		mon_ctx[MON_CTX_CODE] = mon_ctx[MON_CTX_DATA];
@@ -422,6 +423,31 @@ static int resctrl_arch_mon_ctx_alloc_no_wait(enum resctrl_event_id evtid,
 		mon_ctx[MON_CTX_DATA] = USE_PRE_ALLOCATED;
 		mon_ctx[MON_CTX_CODE] = USE_PRE_ALLOCATED;
 		return USE_PRE_ALLOCATED;
+	case QOS_L2_MBM_CORE_EVENT_ID:
+		/*
+		 * The L2 event is not part of resctrl's L3 MBM event range,
+		 * so the assignable-counter machinery never applies: borrow a
+		 * bandwidth monitor for the duration of the read, like the
+		 * occupancy events do with CSU monitors. With CDP, borrow one
+		 * monitor per partid so code and data both accumulate.
+		 */
+		mon_ctx[MON_CTX_DATA] = mpam_alloc_mbwu_mon(mon->class);
+		if (mon_ctx[MON_CTX_DATA] < 0)
+			return mon_ctx[MON_CTX_DATA];
+
+		if (!cdp_enabled) {
+			mon_ctx[MON_CTX_CODE] = mon_ctx[MON_CTX_DATA];
+			return mon_ctx[MON_CTX_DATA];
+		}
+
+		mon_ctx[MON_CTX_CODE] = mpam_alloc_mbwu_mon(mon->class);
+		if (mon_ctx[MON_CTX_CODE] < 0) {
+			int err = mon_ctx[MON_CTX_CODE];
+
+			mpam_free_mbwu_mon(mon->class, mon_ctx[MON_CTX_DATA]);
+			return err;
+		}
+		return mon_ctx[MON_CTX_DATA];
 	default:
 		return -EOPNOTSUPP;
 	}
@@ -460,8 +486,24 @@ static void resctrl_arch_mon_ctx_free_no_wait(enum resctrl_event_id evtid,
 	if (!mon->class)
 		return;
 
-	if (evtid == QOS_L3_OCCUP_EVENT_ID)
+	switch (evtid) {
+	case QOS_L3_OCCUP_EVENT_ID:
+	case QOS_L2_OCCUP_EVENT_ID:
+		/* With CDP, one monitor is used for both code/data reads */
 		mpam_free_csu_mon(mon->class, mon_ctx[MON_CTX_DATA]);
+		break;
+	case QOS_L3_MBM_LOCAL_EVENT_ID:
+	case QOS_L3_MBM_TOTAL_EVENT_ID:
+		/* Served by the pre-assigned ABMC counters */
+		break;
+	case QOS_L2_MBM_CORE_EVENT_ID:
+		mpam_free_mbwu_mon(mon->class, mon_ctx[MON_CTX_DATA]);
+		if (mon_ctx[MON_CTX_CODE] != mon_ctx[MON_CTX_DATA])
+			mpam_free_mbwu_mon(mon->class, mon_ctx[MON_CTX_CODE]);
+		break;
+	default:
+		break;
+	}
 
 	wake_up(&resctrl_mon_ctx_waiters);
 }
@@ -557,10 +599,22 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *hdr,
 	l3_dom = container_of(hdr, struct mpam_resctrl_dom, resctrl_mon_dom.hdr);
 	mon_comp = l3_dom->mon_comp[eventid];
 
-	if (eventid != QOS_L3_OCCUP_EVENT_ID)
+	switch (eventid) {
+	case QOS_L3_OCCUP_EVENT_ID:
+	case QOS_L2_OCCUP_EVENT_ID:
+		mon_type = mpam_feat_msmon_csu;
+		break;
+	case QOS_L2_MBM_CORE_EVENT_ID:
+		/*
+		 * Served by a bandwidth monitor borrowed for the read via
+		 * resctrl_arch_mon_ctx_alloc(); the L3 MBWU events use the
+		 * ABMC resctrl_arch_cntr_read() path instead.
+		 */
+		mon_type = mpam_feat_msmon_mbwu;
+		break;
+	default:
 		return -EINVAL;
-
-	mon_type = mpam_feat_msmon_csu;
+	}
 
 	return read_mon_cdp_safe(mon, mon_comp, mon_type, arch_mon_ctx,
 				 closid, rmid, val);
-- 
2.43.0


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

* Re: [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors
  2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
                   ` (6 preceding siblings ...)
  2026-10-22  8:48 ` [RFC PATCH 7/7] arm_mpam: resctrl: Add the L2 monitor read paths Jinjie Ruan
@ 2026-10-26 11:46 ` Ben Horgan
  7 siblings, 0 replies; 10+ messages in thread
From: Ben Horgan @ 2026-10-26 11:46 UTC (permalink / raw)
  To: Jinjie Ruan, catalin.marinas, will, mark.rutland, corbet, skhan,
	rdunlap, james.morse, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel

Hi Jinjie,

On 22/10/2026 09:48, Jinjie Ruan wrote:
> MPAM resource monitors hang off a partitionable resource in an MSC, not
> off a particular cache level. resctrl only knows about the L3 scope, so
> the L2 cache monitors that MPAM platforms implement are discovered by
> the driver but never show up in resctrl.

Yes, it would be good to add support for more monitoring scopes. This is not just relevant for MPAM.
RISC-V can also have monitors at many places in the system and we should coordinate so we have a
uniform solution across architectures.

> 
> An L2 MSC has the same CSU and MBWU counters as an L3 MSC; the cache
> level comes from the ACPI MPAM table's cache locator. So this is mostly
> a resctrl scope problem, not a hardware one.
> 
> Extend resctrl's monitor scope to L2: register the L2 occupancy and
> bandwidth events against the L2 resource, pick the L2 classes for them,
> and borrow a CSU/MBWU monitor for the duration of a read. With CDP the

Borrowing an MBWU monitor doesn't sound right. The MBWU monitor needs to be configured while the
traffic is happening so that it can keep count of bytes. The CSU is different as it can measure the
occupancy without having to keep any state.

Thanks,

Ben

> two PARTIDs need separate accumulating monitors, so the borrowed-monitor
> context becomes one monitor per PARTID.
> 
> Patches:
> 
>   1-2  Generalise the per-domain read path, add l2c_occupancy and
>        mbm_core_bytes, update the documentation.
> 
>   3    Split the borrowed-monitor context into data and code entries.
> 
>   4    Do the CDP num_rmid halving for every mon_capable resource.
> 
>   5-7  Pick the L2 classes, describe the events with the L2 resource,
>        add the read paths.
> 
> Tested on QEMU with an L2/L3/memory-controller MSC topology. The L2_MON
> files show up and are readable; l2c_occupancy reports the modelled
> occupancy, and mbm_core_bytes reports the modelled bandwidth.
> 
> QEMU branch: https://github.com/ruanjinjie-eng/qemu/tree/mpam
> Test cases: https://github.com/ruanjinjie-eng/mpam_testcase
> 
> Jinjie Ruan (7):
>   fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources
>   fs/resctrl: Add the L2 monitor events
>   arm_mpam: resctrl: Split monitor context for code/data
>   arm_mpam: resctrl: Halve num_rmid for every mon_capable resource on
>     CDP
>   arm_mpam: resctrl: Describe the L2 monitor events with the L2 resource
>   arm_mpam: resctrl: Pick L2 classes for the L2 monitor events
>   arm_mpam: resctrl: Add the L2 monitor read paths
> 
>  Documentation/arch/arm64/mpam.rst |  23 +--
>  drivers/resctrl/mpam_resctrl.c    | 236 +++++++++++++++++++++++-------
>  fs/resctrl/monitor.c              |  15 +-
>  include/linux/resctrl_types.h     |  13 +-
>  4 files changed, 222 insertions(+), 65 deletions(-)
> 


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

* Re: [RFC PATCH 2/7] fs/resctrl: Add the L2 monitor events
  2026-10-22  8:48 ` [RFC PATCH 2/7] fs/resctrl: Add the L2 monitor events Jinjie Ruan
@ 2026-10-26 11:50   ` Ben Horgan
  0 siblings, 0 replies; 10+ messages in thread
From: Ben Horgan @ 2026-10-26 11:50 UTC (permalink / raw)
  To: Jinjie Ruan, catalin.marinas, will, mark.rutland, corbet, skhan,
	rdunlap, james.morse, reinette.chatre, fenghuay, tony.luck,
	Dave.Martin, babu.moger, x86, linux-arm-kernel, linux-doc,
	linux-kernel

Hi Jinjie,

On 22/10/2026 09:48, Jinjie Ruan wrote:
> Add the L2 cache occupancy (l2c_occupancy) and L2 bandwidth
> (mbm_core_bytes) monitor events, and route RDT_RESOURCE_L2 through the
> same per-domain read path as the L3.
> 
> As ARM MPAM Specification said, MPAM resource monitors are associated
> with a shared partitionable resource in an MSC, not with a particular
> cache level: the level comes from firmware's description of the MSC.
> L2 monitors were only missing a resctrl scope. The events are
> exposed once an architecture reports the L2 resource
> mon_capable; x86 never does.
> 
> Update Documentation/arch/arm64/mpam.rst to mention L2.
> 
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
>  Documentation/arch/arm64/mpam.rst | 23 ++++++++++++++---------
>  fs/resctrl/monitor.c              |  9 +++++++++
>  include/linux/resctrl_types.h     | 13 ++++++++++++-
>  3 files changed, 35 insertions(+), 10 deletions(-)
> 
> diff --git a/Documentation/arch/arm64/mpam.rst b/Documentation/arch/arm64/mpam.rst
> index 67fe515ed501..f0697dedde03 100644
> --- a/Documentation/arch/arm64/mpam.rst
> +++ b/Documentation/arch/arm64/mpam.rst
> @@ -32,8 +32,9 @@ Because the user interface to MPAM is via resctrl, only MPAM features that are
>  compatible with resctrl can be exposed to user-space.
>  
>  MSC are considered as a group based on the topology. MSC that correspond with
> -the L3 cache are considered together, it is not possible to mix MSC between L2
> -and L3 to 'cover' a resctrl schema.
> +the L3 cache are considered together, as are MSC that correspond with the L2
> +cache; it is not possible to mix MSC between L2 and L3 to 'cover' a resctrl
> +schema.
>  
>  The supported features are:
>  
> @@ -61,9 +62,11 @@ The supported features are:
>    When the MPAM driver finds multiple groups of MSC it can use for the 'MB'
>    schema, it prefers the group closest to the L3 cache.
>  
> -* Cache Storage Usage (CSU) counters can expose the 'llc_occupancy' provided
> -  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.
> +* Cache Storage Usage (CSU) counters expose the occupancy of the cache they
> +  monitor: 'llc_occupancy' for the L3 group and 'l2c_occupancy' for the L2
> +  group, provided there is at least one CSU monitor on each MSC that makes up
> +  the 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
> @@ -82,10 +85,12 @@ The supported features are:
>    have a corresponding L3 cache. 'mbm_local_bytes' is not exposed as MPAM
>    cannot distinguish local traffic from global traffic.
>  
> -  All these restrictions based on L3 cache are due to resctrl, currently, only
> -  supporting monitoring at the L3 scope. It is expected that going forward more
> -  MBWU monitors can be exposed to the user after support for more monitoring
> -  scopes is added to resctrl.
> +  MBWU monitors on an L2 MSC expose the bandwidth the cache sees as the
> +  'mbm_core_bytes' event on the L2 resource.
> +
> +  These restrictions are due to resctrl only supporting monitoring at the L2
> +  and L3 cache scopes. MBWU monitors on caches at other levels, or on devices,
> +  are not exposed.
>  
>  Reporting Bugs
>  ==============
> diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c
> index f9f4d4486c1b..ee296c27c6e8 100644
> --- a/fs/resctrl/monitor.c
> +++ b/fs/resctrl/monitor.c
> @@ -543,6 +543,13 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr)
>  			return __mon_event_count_one_domain(rdtgrp, rr);
>  		else
>  			return __l3_mon_event_count_sum(rdtgrp, rr);
> +	case RDT_RESOURCE_L2:
> +		WARN_ON_ONCE(rr->evt->any_cpu);
> +		if (WARN_ON_ONCE(!rr->hdr)) {
> +			rr->err = -EINVAL;
> +			return -EINVAL;
> +		}
> +		return __mon_event_count_one_domain(rdtgrp, rr);
>  	case RDT_RESOURCE_PERF_PKG: {
>  		u64 tval = 0;
>  
> @@ -1032,6 +1039,8 @@ struct mon_evt mon_event_all[QOS_NUM_EVENTS] = {
>  	MON_EVENT(QOS_L3_OCCUP_EVENT_ID,		"llc_occupancy",	RDT_RESOURCE_L3,	false),
>  	MON_EVENT(QOS_L3_MBM_TOTAL_EVENT_ID,		"mbm_total_bytes",	RDT_RESOURCE_L3,	false),
>  	MON_EVENT(QOS_L3_MBM_LOCAL_EVENT_ID,		"mbm_local_bytes",	RDT_RESOURCE_L3,	false),
> +	MON_EVENT(QOS_L2_OCCUP_EVENT_ID,		"l2c_occupancy",	RDT_RESOURCE_L2,	false),
> +	MON_EVENT(QOS_L2_MBM_CORE_EVENT_ID,		"mbm_core_bytes",	RDT_RESOURCE_L2,	false),
>  	MON_EVENT(PMT_EVENT_ENERGY,			"core_energy",		RDT_RESOURCE_PERF_PKG,	true),
>  	MON_EVENT(PMT_EVENT_ACTIVITY,			"activity",		RDT_RESOURCE_PERF_PKG,	true),
>  	MON_EVENT(PMT_EVENT_STALLS_LLC_HIT,		"stalls_llc_hit",	RDT_RESOURCE_PERF_PKG,	false),
> diff --git a/include/linux/resctrl_types.h b/include/linux/resctrl_types.h
> index a5f56faa18d2..f31b8f2ccb8b 100644
> --- a/include/linux/resctrl_types.h
> +++ b/include/linux/resctrl_types.h
> @@ -37,7 +37,14 @@
>  /* Number of memory transactions that an MBM event can be configured with */
>  #define NUM_MBM_TRANSACTIONS		7
>  
> -/* Event IDs */
> +/*
> + * Monitor Event IDs, representative a variety of monitoring events:
> + * QOS_L3_OCCUP_EVENT_ID:     L3 Cache Occupancy statistics event
> + * QOS_L3_MBM_TOTAL_EVENT_ID: Global Memory Bandwidth statistics event
> + * QOS_L3_MBM_LOCAL_EVENT_ID: L3 Cache Bandwidth statistics event
> + * QOS_L2_OCCUP_EVENT_ID:     L2 Cache Occupancy statistics event
> + * QOS_L2_MBM_CORE_EVENT_ID:  L2 Cache Bandwidth statistics event
> + */
>  enum resctrl_event_id {
>  	/* Must match value of first event below */
>  	QOS_FIRST_EVENT			= 0x01,
> @@ -50,6 +57,10 @@ enum resctrl_event_id {
>  	QOS_L3_MBM_TOTAL_EVENT_ID	= 0x02,
>  	QOS_L3_MBM_LOCAL_EVENT_ID	= 0x03,
>  
> +	/* ARM MPAM L2 Events */
> +	QOS_L2_OCCUP_EVENT_ID,
> +	QOS_L2_MBM_CORE_EVENT_ID,

These are essentially, L2 versions of L3 events. Does it not make sense to generalize the existing
events to apply in different scopes?

Thanks,

Ben

> +
>  	/* Intel Telemetry Events */
>  	PMT_EVENT_ENERGY,
>  	PMT_EVENT_ACTIVITY,


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

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

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22  8:48 [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Jinjie Ruan
2026-10-22  8:48 ` [RFC PATCH 1/7] fs/resctrl: Generalize __l3_mon_event_count for non-L3 resources Jinjie Ruan
2026-10-22  8:48 ` [RFC PATCH 2/7] fs/resctrl: Add the L2 monitor events Jinjie Ruan
2026-10-26 11:50   ` Ben Horgan
2026-10-22  8:48 ` [RFC PATCH 3/7] arm_mpam: resctrl: Split monitor context for code/data Jinjie Ruan
2026-10-22  8:48 ` [RFC PATCH 4/7] arm_mpam: resctrl: Halve num_rmid for every mon_capable resource on CDP Jinjie Ruan
2026-10-22  8:48 ` [RFC PATCH 5/7] arm_mpam: resctrl: Describe the L2 monitor events with the L2 resource Jinjie Ruan
2026-10-22  8:48 ` [RFC PATCH 6/7] arm_mpam: resctrl: Pick L2 classes for the L2 monitor events Jinjie Ruan
2026-10-22  8:48 ` [RFC PATCH 7/7] arm_mpam: resctrl: Add the L2 monitor read paths Jinjie Ruan
2026-10-26 11:46 ` [RFC PATCH 0/7] arm_mpam: Add L2 cache monitors Ben Horgan

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®