[PATCH 12/23] resctrl: Add node-scope MBM total event

From: Fenghua Yu

Date: Thu Jul 16 2026 - 17:07:11 EST


Some MPAM systems count total memory bandwidth on a memory-side MSC that
is scoped to a NUMA node rather than the L3 cache. Add a new
QOS_NODE_MBM_TOTAL_EVENT_ID event for this node-scoped total, backed by
the MBA resource, and register it in the mon_event_all[] table.

Keep the new event id contiguous with the existing L3 MBM events so the
for_each_mbm_event_id() range and the per-domain software MBM state
arrays continue to be sized from the first to the last MBM event. Extend
resctrl_is_mbm_event() to recognise it, and add helpers
resctrl_is_mbm_total_event() and resctrl_mbm_total_event_id() so callers
can select the node-scoped total when it is enabled and fall back to the
L3 total otherwise.

Signed-off-by: Fenghua Yu <fenghuay@xxxxxxxxxx>
---
fs/resctrl/monitor.c | 1 +
include/linux/resctrl.h | 27 +++++++++++++++++++++++----
include/linux/resctrl_types.h | 15 ++++++++++++++-
3 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c
index db19e5eac1f9..25b5038b3e16 100644
--- a/fs/resctrl/monitor.c
+++ b/fs/resctrl/monitor.c
@@ -990,6 +990,7 @@ 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_NODE_MBM_TOTAL_EVENT_ID, "mbm_total_bytes", RDT_RESOURCE_MBA, 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.h b/include/linux/resctrl.h
index 307803ea1b09..3fb236c459dc 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -536,16 +536,35 @@ bool resctrl_arch_is_evt_configurable(enum resctrl_event_id evt);

static inline bool resctrl_is_mbm_event(enum resctrl_event_id eventid)
{
- return (eventid >= QOS_L3_MBM_TOTAL_EVENT_ID &&
- eventid <= QOS_L3_MBM_LOCAL_EVENT_ID);
+ return ((eventid >= QOS_L3_MBM_TOTAL_EVENT_ID &&
+ eventid <= QOS_L3_MBM_LOCAL_EVENT_ID) ||
+ eventid == QOS_NODE_MBM_TOTAL_EVENT_ID);
+}
+
+static inline bool resctrl_is_mbm_total_event(enum resctrl_event_id eventid)
+{
+ return eventid == QOS_L3_MBM_TOTAL_EVENT_ID ||
+ eventid == QOS_NODE_MBM_TOTAL_EVENT_ID;
+}
+
+static inline enum resctrl_event_id resctrl_mbm_total_event_id(void)
+{
+ if (resctrl_is_mon_event_enabled(QOS_NODE_MBM_TOTAL_EVENT_ID))
+ return QOS_NODE_MBM_TOTAL_EVENT_ID;
+
+ return QOS_L3_MBM_TOTAL_EVENT_ID;
}

u32 resctrl_get_mon_evt_cfg(enum resctrl_event_id eventid);

-/* Iterate over all memory bandwidth events */
+/*
+ * Iterate over all memory bandwidth events. The node-scope total event is
+ * contiguous with the L3 events, so it is covered by the same range. Loop
+ * bodies must skip events that are not enabled.
+ */
#define for_each_mbm_event_id(eventid) \
for (eventid = QOS_L3_MBM_TOTAL_EVENT_ID; \
- eventid <= QOS_L3_MBM_LOCAL_EVENT_ID; eventid++)
+ eventid <= QOS_NODE_MBM_TOTAL_EVENT_ID; eventid++)

/* Iterate over memory bandwidth arrays in domain structures */
#define for_each_mbm_idx(idx) \
diff --git a/include/linux/resctrl_types.h b/include/linux/resctrl_types.h
index a5f56faa18d2..996ae8763b0a 100644
--- a/include/linux/resctrl_types.h
+++ b/include/linux/resctrl_types.h
@@ -50,6 +50,13 @@ enum resctrl_event_id {
QOS_L3_MBM_TOTAL_EVENT_ID = 0x02,
QOS_L3_MBM_LOCAL_EVENT_ID = 0x03,

+ /*
+ * Total memory bandwidth counted on a NUMA node scope MSC (e.g. a
+ * memory-side MSC above the L3). Kept contiguous with the other MPAM
+ * events so arch code can size per-event arrays by the last MPAM event.
+ */
+ QOS_NODE_MBM_TOTAL_EVENT_ID = 0x04,
+
/* Intel Telemetry Events */
PMT_EVENT_ENERGY,
PMT_EVENT_ACTIVITY,
@@ -65,7 +72,13 @@ enum resctrl_event_id {
QOS_NUM_EVENTS,
};

-#define QOS_NUM_L3_MBM_EVENTS (QOS_L3_MBM_LOCAL_EVENT_ID - QOS_L3_MBM_TOTAL_EVENT_ID + 1)
+/*
+ * Number of software MBM state slots per monitoring domain. The MBM events
+ * (L3 total, L3 local and the node-scope total) are contiguous, so the last
+ * one bounds the array. The node-scope total event is only ever enabled on
+ * Arm/MPAM; on x86 its slot is simply left unused.
+ */
+#define QOS_NUM_L3_MBM_EVENTS (QOS_NODE_MBM_TOTAL_EVENT_ID - QOS_L3_MBM_TOTAL_EVENT_ID + 1)
#define MBM_STATE_IDX(evt) ((evt) - QOS_L3_MBM_TOTAL_EVENT_ID)

#endif /* __LINUX_RESCTRL_TYPES_H */
--
2.43.0