Skip to contentSkip to conversation

perf/arm-cmn: Support CMN S3 r2

PATCH Superseded Robin Murphy 3 participants 13 messages Jun 30 – Jul 9

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Robin Murphy

author maintainer
#0 lore raw

cover perf/arm-cmn: Support CMN S3 r2

Another year, another CMN - although arguably this was last year's CMN, and is still the same in name as the year before that, but in implementation the newer releases have significantly updated the HN-S PMU features, pushing the previous filtering abstraction beyond its breaking point. As before I've tried to break the refactoring and functional changes up into reasonably logical chunks so there's some vague hope of anyone being able to review it...

Cheers,
Robin.

Robin Murphy (5):
perf/arm-cmn: Rename filter variables for clarity perf/arm-cmn: Refactor event filter programming perf/arm-cmn: Refactor event filter data perf/arm-cmn: Add new filters perf/arm-cmn: Support CMN S3 r2

 drivers/perf/arm-cmn.c | 490 +++++++++++++++++++++++++++++------------
 1 file changed, 347 insertions(+), 143 deletions(-)
Signature · 1 line
2.54.0.dirty
author maintainer
#1 lore raw

PATCH 1/5 perf/arm-cmn: Rename filter variables for clarity

CMN has already grown many more event-specific filters than the original Occupancy ID, but since they are all independent of each other we've just overloaded them onto the same name. Before we add yet more, and they begin to overlap, rename all our "occupid" variables to "filter" so that things can be a bit clearer and more consistent (but leaving the format attribute itself, to avoid UAPI concerns).

---
 drivers/perf/arm-cmn.c | 53 ++++++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 25 deletions(-)
drivers/perf/arm-cmn.c +28 -25
index 50402bc4a21d..2a8a67da72c3 100644
@@ -163,13 +163,13 @@
 /* Event attributes */
 #define CMN_CONFIG_TYPE			GENMASK_ULL(15, 0)
 #define CMN_CONFIG_EVENTID		GENMASK_ULL(26, 16)
-#define CMN_CONFIG_OCCUPID		GENMASK_ULL(30, 27)
+#define CMN_CONFIG_FILTER		GENMASK_ULL(30, 27)
 #define CMN_CONFIG_BYNODEID		BIT_ULL(31)
 #define CMN_CONFIG_NODEID		GENMASK_ULL(47, 32)
 
 #define CMN_EVENT_TYPE(event)		FIELD_GET(CMN_CONFIG_TYPE, (event)->attr.config)
 #define CMN_EVENT_EVENTID(event)	FIELD_GET(CMN_CONFIG_EVENTID, (event)->attr.config)
-#define CMN_EVENT_OCCUPID(event)	FIELD_GET(CMN_CONFIG_OCCUPID, (event)->attr.config)
+#define CMN_EVENT_FILTER(event)		FIELD_GET(CMN_CONFIG_FILTER, (event)->attr.config)
 #define CMN_EVENT_BYNODEID(event)	FIELD_GET(CMN_CONFIG_BYNODEID, (event)->attr.config)
 #define CMN_EVENT_NODEID(event)		FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config)
 
@@ -301,7 +301,7 @@ struct arm_cmn_node {
 	struct {
 		u8 val : 4;
 		u8 count : 4;
-	} occupid[SEL_MAX];
+	} filter[SEL_MAX];
 	union {
 		u8 event[4];
 		__le32 event_sel;
@@ -672,7 +672,7 @@ struct arm_cmn_event_attr {
 	enum cmn_node_type type;
 	enum cmn_filter_select fsel;
 	u16 eventid;
-	u8 occupid;
+	u8 filter;
 };
 
 struct arm_cmn_format_attr {
@@ -681,13 +681,13 @@ struct arm_cmn_format_attr {
 	int config;
 };
 
-#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _occupid, _fsel)\
+#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, _fsel)\
 	(&((struct arm_cmn_event_attr[]) {{				\
 		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
 		.model = _model,					\
 		.type = _type,						\
 		.eventid = _eventid,					\
-		.occupid = _occupid,					\
+		.filter = _filter,					\
 		.fsel = _fsel,						\
 	}})[0].attr.attr)
 #define CMN_EVENT_ATTR(_model, _name, _type, _eventid)			\
@@ -709,8 +709,8 @@ static ssize_t arm_cmn_event_show(struct device *dev,
 				  eattr->type, eattr->eventid);
 
 	if (eattr->fsel > SEL_NONE)
-		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n",
-				  eattr->type, eattr->eventid, eattr->occupid);
+		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n",
+				  eattr->type, eattr->eventid, eattr->filter);
 
 	return sysfs_emit(buf, "type=0x%x,eventid=0x%x\n", eattr->type,
 			  eattr->eventid);
@@ -1320,7 +1320,7 @@ static ssize_t arm_cmn_format_show(struct device *dev,
 static struct attribute *arm_cmn_format_attrs[] = {
 	CMN_FORMAT_ATTR(type, CMN_CONFIG_TYPE),
 	CMN_FORMAT_ATTR(eventid, CMN_CONFIG_EVENTID),
-	CMN_FORMAT_ATTR(occupid, CMN_CONFIG_OCCUPID),
+	CMN_FORMAT_ATTR(filter, CMN_CONFIG_FILTER),
 	CMN_FORMAT_ATTR(bynodeid, CMN_CONFIG_BYNODEID),
 	CMN_FORMAT_ATTR(nodeid, CMN_CONFIG_NODEID),
 
@@ -1333,6 +1333,9 @@ static struct attribute *arm_cmn_format_attrs[] = {
 	_CMN_FORMAT_ATTR(wp_val, 1, CMN_CONFIG1_WP_VAL),
 	_CMN_FORMAT_ATTR(wp_mask, 2, CMN_CONFIG2_WP_MASK),
 
+	/* Old name for UAPI compatibility */
+	CMN_FORMAT_ATTR(occupid, CMN_CONFIG_FILTER),
+
 	NULL
 };
 
@@ -1544,30 +1547,30 @@ static void arm_cmn_event_read(struct perf_event *event)
 }
 
 static int arm_cmn_set_event_sel_hi(struct arm_cmn_node *dn,
-				    enum cmn_filter_select fsel, u8 occupid)
+				    enum cmn_filter_select fsel, u8 filter)
 {
 	u64 reg;
 
 	if (fsel == SEL_NONE)
 		return 0;
 
-	if (!dn->occupid[fsel].count) {
-		dn->occupid[fsel].val = occupid;
+	if (!dn->filter[fsel].count) {
+		dn->filter[fsel].val = filter;
 		reg = FIELD_PREP(CMN__PMU_CBUSY_SNTHROTTLE_SEL,
-				 dn->occupid[SEL_CBUSY_SNTHROTTLE_SEL].val) |
+				 dn->filter[SEL_CBUSY_SNTHROTTLE_SEL].val) |
 		      FIELD_PREP(CMN__PMU_SN_HOME_SEL,
-				 dn->occupid[SEL_SN_HOME_SEL].val) |
+				 dn->filter[SEL_SN_HOME_SEL].val) |
 		      FIELD_PREP(CMN__PMU_HBT_LBT_SEL,
-				 dn->occupid[SEL_HBT_LBT_SEL].val) |
+				 dn->filter[SEL_HBT_LBT_SEL].val) |
 		      FIELD_PREP(CMN__PMU_CLASS_OCCUP_ID,
-				 dn->occupid[SEL_CLASS_OCCUP_ID].val) |
+				 dn->filter[SEL_CLASS_OCCUP_ID].val) |
 		      FIELD_PREP(CMN__PMU_OCCUP1_ID,
-				 dn->occupid[SEL_OCCUP1ID].val);
+				 dn->filter[SEL_OCCUP1ID].val);
 		writel_relaxed(reg >> 32, dn->pmu_base + CMN_PMU_EVENT_SEL + 4);
-	} else if (dn->occupid[fsel].val != occupid) {
+	} else if (dn->filter[fsel].val != filter) {
 		return -EBUSY;
 	}
-	dn->occupid[fsel].count++;
+	dn->filter[fsel].count++;
 	return 0;
 }
 
@@ -1649,7 +1652,7 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
 
 struct arm_cmn_val {
 	u8 dtm_count[CMN_MAX_DTMS];
-	u8 occupid[CMN_MAX_DTMS][SEL_MAX];
+	u8 filter[CMN_MAX_DTMS][SEL_MAX];
 	u8 wp[CMN_MAX_DTMS][4];
 	u8 wp_combine[CMN_MAX_DTMS][2];
 	int dtc_count[CMN_MAX_DTCS];
@@ -1694,7 +1697,7 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
 		val->dtm_count[dtm]++;
 
 		if (sel > SEL_NONE)
-			val->occupid[dtm][sel] = CMN_EVENT_OCCUPID(event) + 1;
+			val->filter[dtm][sel] = CMN_EVENT_FILTER(event) + 1;
 
 		if (type != CMN_TYPE_WP)
 			continue;
@@ -1745,8 +1748,8 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
 		if (val->dtm_count[dtm] == CMN_DTM_NUM_COUNTERS)
 			goto done;
 
-		if (sel > SEL_NONE && val->occupid[dtm][sel] &&
-		    val->occupid[dtm][sel] != CMN_EVENT_OCCUPID(event) + 1)
+		if (sel > SEL_NONE && val->filter[dtm][sel] &&
+		    val->filter[dtm][sel] != CMN_EVENT_FILTER(event) + 1)
 			goto done;
 
 		if (type != CMN_TYPE_WP)
@@ -1892,7 +1895,7 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 		}
 
 		if (hw->filter_sel > SEL_NONE)
-			hw->dn[i].occupid[hw->filter_sel].count--;
+			hw->dn[i].filter[hw->filter_sel].count--;
 
 		dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
 		writel_relaxed(dtm->pmu_config_low, dtm->base + CMN_DTM_PMU_CONFIG);
@@ -1978,7 +1981,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
 			input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
 				    (nid.port << 4) + (nid.dev << 2);
 
-			if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_OCCUPID(event)))
+			if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_FILTER(event)))
 				goto free_dtms;
 		}
 
Signature · 1 line
2.54.0.dirty
Reviewed-by on v1 1/5
#2 lore raw

Re: [PATCH 1/5] perf/arm-cmn: Rename filter variables for clarity

Quoted 8 lines · Robin Murphy

CMN has already grown many more event-specific filters than the original Occupancy ID, but since they are all independent of each other we've just overloaded them onto the same name. Before we add yet more, and they begin to overlap, rename all our "occupid" variables to "filter" so that things can be a bit clearer and more consistent (but leaving the format attribute itself, to avoid UAPI concerns).

Looks good to me

Cheers, Ilkka

Quoted 180 lines

---
drivers/perf/arm-cmn.c | 53 ++++++++++++++++++++++--------------------

1 file changed, 28 insertions(+), 25 deletions(-)
drivers/perf/arm-cmn.c +28 -25
index 50402bc4a21d..2a8a67da72c3 100644
@@ -163,13 +163,13 @@
 /* Event attributes */
 #define CMN_CONFIG_TYPE			GENMASK_ULL(15, 0)
 #define CMN_CONFIG_EVENTID		GENMASK_ULL(26, 16)
-#define CMN_CONFIG_OCCUPID		GENMASK_ULL(30, 27)
+#define CMN_CONFIG_FILTER		GENMASK_ULL(30, 27)
 #define CMN_CONFIG_BYNODEID		BIT_ULL(31)
 #define CMN_CONFIG_NODEID		GENMASK_ULL(47, 32)
 
 #define CMN_EVENT_TYPE(event)		FIELD_GET(CMN_CONFIG_TYPE, (event)->attr.config)
 #define CMN_EVENT_EVENTID(event)	FIELD_GET(CMN_CONFIG_EVENTID, (event)->attr.config)
-#define CMN_EVENT_OCCUPID(event)	FIELD_GET(CMN_CONFIG_OCCUPID, (event)->attr.config)
+#define CMN_EVENT_FILTER(event)		FIELD_GET(CMN_CONFIG_FILTER, (event)->attr.config)
 #define CMN_EVENT_BYNODEID(event)	FIELD_GET(CMN_CONFIG_BYNODEID, (event)->attr.config)
 #define CMN_EVENT_NODEID(event)		FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config)
 
@@ -301,7 +301,7 @@ struct arm_cmn_node {
 	struct {
 		u8 val : 4;
 		u8 count : 4;
-	} occupid[SEL_MAX];
+	} filter[SEL_MAX];
 	union {
 		u8 event[4];
 		__le32 event_sel;
@@ -672,7 +672,7 @@ struct arm_cmn_event_attr {
 	enum cmn_node_type type;
 	enum cmn_filter_select fsel;
 	u16 eventid;
-	u8 occupid;
+	u8 filter;
 };
 
 struct arm_cmn_format_attr {
@@ -681,13 +681,13 @@ struct arm_cmn_format_attr {
 	int config;
 };
 
-#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _occupid, _fsel)\
+#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, _fsel)\
 	(&((struct arm_cmn_event_attr[]) {{				\
 		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
 		.model = _model,					\
 		.type = _type,						\
 		.eventid = _eventid,					\
-		.occupid = _occupid,					\
+		.filter = _filter,					\
 		.fsel = _fsel,						\
 	}})[0].attr.attr)
 #define CMN_EVENT_ATTR(_model, _name, _type, _eventid)			\
@@ -709,8 +709,8 @@ static ssize_t arm_cmn_event_show(struct device *dev,
 				  eattr->type, eattr->eventid);
 
 	if (eattr->fsel > SEL_NONE)
-		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,occupid=0x%x\n",
-				  eattr->type, eattr->eventid, eattr->occupid);
+		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n",
+				  eattr->type, eattr->eventid, eattr->filter);
 
 	return sysfs_emit(buf, "type=0x%x,eventid=0x%x\n", eattr->type,
 			  eattr->eventid);
@@ -1320,7 +1320,7 @@ static ssize_t arm_cmn_format_show(struct device *dev,
 static struct attribute *arm_cmn_format_attrs[] = {
 	CMN_FORMAT_ATTR(type, CMN_CONFIG_TYPE),
 	CMN_FORMAT_ATTR(eventid, CMN_CONFIG_EVENTID),
-	CMN_FORMAT_ATTR(occupid, CMN_CONFIG_OCCUPID),
+	CMN_FORMAT_ATTR(filter, CMN_CONFIG_FILTER),
 	CMN_FORMAT_ATTR(bynodeid, CMN_CONFIG_BYNODEID),
 	CMN_FORMAT_ATTR(nodeid, CMN_CONFIG_NODEID),
 
@@ -1333,6 +1333,9 @@ static struct attribute *arm_cmn_format_attrs[] = {
 	_CMN_FORMAT_ATTR(wp_val, 1, CMN_CONFIG1_WP_VAL),
 	_CMN_FORMAT_ATTR(wp_mask, 2, CMN_CONFIG2_WP_MASK),
 
+	/* Old name for UAPI compatibility */
+	CMN_FORMAT_ATTR(occupid, CMN_CONFIG_FILTER),
+
 	NULL
 };
 
@@ -1544,30 +1547,30 @@ static void arm_cmn_event_read(struct perf_event *event)
 }
 
 static int arm_cmn_set_event_sel_hi(struct arm_cmn_node *dn,
-				    enum cmn_filter_select fsel, u8 occupid)
+				    enum cmn_filter_select fsel, u8 filter)
 {
 	u64 reg;
 
 	if (fsel == SEL_NONE)
 		return 0;
 
-	if (!dn->occupid[fsel].count) {
-		dn->occupid[fsel].val = occupid;
+	if (!dn->filter[fsel].count) {
+		dn->filter[fsel].val = filter;
 		reg = FIELD_PREP(CMN__PMU_CBUSY_SNTHROTTLE_SEL,
-				 dn->occupid[SEL_CBUSY_SNTHROTTLE_SEL].val) |
+				 dn->filter[SEL_CBUSY_SNTHROTTLE_SEL].val) |
 		      FIELD_PREP(CMN__PMU_SN_HOME_SEL,
-				 dn->occupid[SEL_SN_HOME_SEL].val) |
+				 dn->filter[SEL_SN_HOME_SEL].val) |
 		      FIELD_PREP(CMN__PMU_HBT_LBT_SEL,
-				 dn->occupid[SEL_HBT_LBT_SEL].val) |
+				 dn->filter[SEL_HBT_LBT_SEL].val) |
 		      FIELD_PREP(CMN__PMU_CLASS_OCCUP_ID,
-				 dn->occupid[SEL_CLASS_OCCUP_ID].val) |
+				 dn->filter[SEL_CLASS_OCCUP_ID].val) |
 		      FIELD_PREP(CMN__PMU_OCCUP1_ID,
-				 dn->occupid[SEL_OCCUP1ID].val);
+				 dn->filter[SEL_OCCUP1ID].val);
 		writel_relaxed(reg >> 32, dn->pmu_base + CMN_PMU_EVENT_SEL + 4);
-	} else if (dn->occupid[fsel].val != occupid) {
+	} else if (dn->filter[fsel].val != filter) {
 		return -EBUSY;
 	}
-	dn->occupid[fsel].count++;
+	dn->filter[fsel].count++;
 	return 0;
 }
 
@@ -1649,7 +1652,7 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
 
 struct arm_cmn_val {
 	u8 dtm_count[CMN_MAX_DTMS];
-	u8 occupid[CMN_MAX_DTMS][SEL_MAX];
+	u8 filter[CMN_MAX_DTMS][SEL_MAX];
 	u8 wp[CMN_MAX_DTMS][4];
 	u8 wp_combine[CMN_MAX_DTMS][2];
 	int dtc_count[CMN_MAX_DTCS];
@@ -1694,7 +1697,7 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
 		val->dtm_count[dtm]++;
 
 		if (sel > SEL_NONE)
-			val->occupid[dtm][sel] = CMN_EVENT_OCCUPID(event) + 1;
+			val->filter[dtm][sel] = CMN_EVENT_FILTER(event) + 1;
 
 		if (type != CMN_TYPE_WP)
 			continue;
@@ -1745,8 +1748,8 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
 		if (val->dtm_count[dtm] == CMN_DTM_NUM_COUNTERS)
 			goto done;
 
-		if (sel > SEL_NONE && val->occupid[dtm][sel] &&
-		    val->occupid[dtm][sel] != CMN_EVENT_OCCUPID(event) + 1)
+		if (sel > SEL_NONE && val->filter[dtm][sel] &&
+		    val->filter[dtm][sel] != CMN_EVENT_FILTER(event) + 1)
 			goto done;
 
 		if (type != CMN_TYPE_WP)
@@ -1892,7 +1895,7 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 		}
 
 		if (hw->filter_sel > SEL_NONE)
-			hw->dn[i].occupid[hw->filter_sel].count--;
+			hw->dn[i].filter[hw->filter_sel].count--;
 
 		dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
 		writel_relaxed(dtm->pmu_config_low, dtm->base + CMN_DTM_PMU_CONFIG);
@@ -1978,7 +1981,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
 			input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
 				    (nid.port << 4) + (nid.dev << 2);
 
-			if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_OCCUPID(event)))
+			if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_FILTER(event)))
 				goto free_dtms;
 		}
 
Signature · 1 line
2.54.0.dirty
author maintainer
#3 lore raw

PATCH 2/5 perf/arm-cmn: Refactor event filter programming

In reply to Robin Murphy

We're soon going to need to cope with events having multiple filters, plus the filter fields themselves moving around, wherein any more inline if/else logic will struggle to scale. Add a more general abstraction for the node-specific filter controls, and rejig the pmu_event_sel filter programming around it in a more extensible manner.

---
 drivers/perf/arm-cmn.c | 139 ++++++++++++++++++++++++++++++-----------
 1 file changed, 101 insertions(+), 38 deletions(-)
drivers/perf/arm-cmn.c +101 -38
index 2a8a67da72c3..14c267d2e2f9 100644
@@ -278,8 +278,8 @@ enum cmn_node_type {
 };
 
 enum cmn_filter_select {
-	SEL_NONE = -1,
-	SEL_OCCUP1ID,
+	SEL_NONE,
+	SEL_OCCUP1_ID,
 	SEL_CLASS_OCCUP_ID,
 	SEL_CBUSY_SNTHROTTLE_SEL,
 	SEL_HBT_LBT_SEL,
@@ -599,6 +599,57 @@ static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id)
 static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id) {}
 #endif
 
+enum cmn_filter_type {
+	FILT_NONE,
+	FILT_OCCUP1_ID,
+	FILT_HNF_700,
+	FILT_HNS,
+};
+#define CMN_FILTER(_sel)	[SEL_##_sel] = CMN__PMU_##_sel
+
+static const u64 arm_cmn_filters[][SEL_MAX] = {
+	[FILT_NONE] = {},
+	/* DVM etc. */
+	[FILT_OCCUP1_ID] = {
+		CMN_FILTER(OCCUP1_ID)
+	},
+	/* Newer HN-F */
+	[FILT_HNF_700] = {
+		CMN_FILTER(OCCUP1_ID),
+		CMN_FILTER(CLASS_OCCUP_ID),
+		CMN_FILTER(CBUSY_SNTHROTTLE_SEL)
+	},
+	/* HN-S */
+	[FILT_HNS] = {
+		CMN_FILTER(OCCUP1_ID),
+		CMN_FILTER(CLASS_OCCUP_ID),
+		CMN_FILTER(CBUSY_SNTHROTTLE_SEL),
+		CMN_FILTER(HBT_LBT_SEL),
+		CMN_FILTER(SN_HOME_SEL)
+	},
+};
+
+static enum cmn_filter_type arm_cmn_filter(enum cmn_node_type node,
+					   enum cmn_model model)
+{
+	switch (node) {
+	default:
+		return FILT_NONE;
+	case CMN_TYPE_DVM:
+	case CMN_TYPE_CXRA:
+	case CMN_TYPE_CXHA:
+	case CMN_TYPE_CCRA:
+	case CMN_TYPE_CCHA:
+		return FILT_OCCUP1_ID;
+	case CMN_TYPE_HNF:
+		if (model < CMN700)
+			return FILT_OCCUP1_ID;
+		return FILT_HNF_700;
+	case CMN_TYPE_HNS:
+		return FILT_HNS;
+	};
+}
+
 struct arm_cmn_hw_event {
 	struct arm_cmn_node *dn;
 	union {
@@ -708,7 +759,7 @@ static ssize_t arm_cmn_event_show(struct device *dev,
 				  "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
 				  eattr->type, eattr->eventid);
 
-	if (eattr->fsel > SEL_NONE)
+	if (eattr->fsel)
 		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n",
 				  eattr->type, eattr->eventid, eattr->filter);
 
@@ -832,16 +883,16 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 #define CMN_EVENT_DVM(_model, _name, _event)			\
 	_CMN_EVENT_DVM(_model, _name, _event, 0, SEL_NONE)
 #define CMN_EVENT_DVM_OCC(_model, _name, _event)			\
-	_CMN_EVENT_DVM(_model, _name##_all, _event, 0, SEL_OCCUP1ID),	\
-	_CMN_EVENT_DVM(_model, _name##_dvmop, _event, 1, SEL_OCCUP1ID),	\
-	_CMN_EVENT_DVM(_model, _name##_dvmsync, _event, 2, SEL_OCCUP1ID)
+	_CMN_EVENT_DVM(_model, _name##_all, _event, 0, SEL_OCCUP1_ID),	\
+	_CMN_EVENT_DVM(_model, _name##_dvmop, _event, 1, SEL_OCCUP1_ID),	\
+	_CMN_EVENT_DVM(_model, _name##_dvmsync, _event, 2, SEL_OCCUP1_ID)
 
 #define CMN_EVENT_HN_OCC(_model, _name, _type, _event)		\
-	_CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 1, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 2, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, 3, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, 4, SEL_OCCUP1ID)
+	_CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 1, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 2, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, 3, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, 4, SEL_OCCUP1_ID)
 #define CMN_EVENT_HN_CLS(_model, _name, _type, _event)			\
 	_CMN_EVENT_ATTR(_model, _name##_class0, _type, _event, 0, SEL_CLASS_OCCUP_ID), \
 	_CMN_EVENT_ATTR(_model, _name##_class1, _type, _event, 1, SEL_CLASS_OCCUP_ID), \
@@ -865,9 +916,9 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 
 #define CMN_EVENT_HNS_OCC(_name, _event)				\
 	CMN_EVENT_HN_OCC(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event),	\
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, 5, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 6, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 7, SEL_OCCUP1ID)
+	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, 5, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 6, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 7, SEL_OCCUP1_ID)
 #define CMN_EVENT_HNS_CLS( _name, _event)				\
 	CMN_EVENT_HN_CLS(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
 #define CMN_EVENT_HNS_SNT(_name, _event)				\
@@ -1547,27 +1598,20 @@ static void arm_cmn_event_read(struct perf_event *event)
 }
 
 static int arm_cmn_set_event_sel_hi(struct arm_cmn_node *dn,
-				    enum cmn_filter_select fsel, u8 filter)
+				    enum cmn_filter_select fsel, u8 val)
 {
-	u64 reg;
-
-	if (fsel == SEL_NONE)
-		return 0;
-
 	if (!dn->filter[fsel].count) {
-		dn->filter[fsel].val = filter;
-		reg = FIELD_PREP(CMN__PMU_CBUSY_SNTHROTTLE_SEL,
-				 dn->filter[SEL_CBUSY_SNTHROTTLE_SEL].val) |
-		      FIELD_PREP(CMN__PMU_SN_HOME_SEL,
-				 dn->filter[SEL_SN_HOME_SEL].val) |
-		      FIELD_PREP(CMN__PMU_HBT_LBT_SEL,
-				 dn->filter[SEL_HBT_LBT_SEL].val) |
-		      FIELD_PREP(CMN__PMU_CLASS_OCCUP_ID,
-				 dn->filter[SEL_CLASS_OCCUP_ID].val) |
-		      FIELD_PREP(CMN__PMU_OCCUP1_ID,
-				 dn->filter[SEL_OCCUP1ID].val);
+		const u64 *filter = arm_cmn_filters[dn->filter[SEL_NONE].val];
+		u64 reg = 0;
+
+		dn->filter[fsel].val = val;
+		for (int i = SEL_OCCUP1_ID; i < SEL_MAX; i++) {
+			if (filter[i])
+				reg |= field_prep(filter[i], dn->filter[i].val);
+		}
+
 		writel_relaxed(reg >> 32, dn->pmu_base + CMN_PMU_EVENT_SEL + 4);
-	} else if (dn->filter[fsel].val != filter) {
+	} else if (dn->filter[fsel].val != val) {
 		return -EBUSY;
 	}
 	dn->filter[fsel].count++;
@@ -1696,7 +1740,7 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
 
 		val->dtm_count[dtm]++;
 
-		if (sel > SEL_NONE)
+		if (sel)
 			val->filter[dtm][sel] = CMN_EVENT_FILTER(event) + 1;
 
 		if (type != CMN_TYPE_WP)
@@ -1748,7 +1792,7 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
 		if (val->dtm_count[dtm] == CMN_DTM_NUM_COUNTERS)
 			goto done;
 
-		if (sel > SEL_NONE && val->filter[dtm][sel] &&
+		if (sel && val->filter[dtm][sel] &&
 		    val->filter[dtm][sel] != CMN_EVENT_FILTER(event) + 1)
 			goto done;
 
@@ -1894,7 +1938,7 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 			dtm->wp_event[wp_idx] = -1;
 		}
 
-		if (hw->filter_sel > SEL_NONE)
+		if (hw->filter_sel)
 			hw->dn[i].filter[hw->filter_sel].count--;
 
 		dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
@@ -1906,6 +1950,17 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 		cmn->dtc[j].counters[idx] = NULL;
 }
 
+static int arm_cmn_set_event_filter(struct arm_cmn_node *dn, struct perf_event *event)
+{
+	enum cmn_filter_select fsel = to_cmn_hw(event)->filter_sel;
+	int ret = 0;
+
+	if (fsel)
+		ret = arm_cmn_set_event_sel_hi(dn, fsel, CMN_EVENT_FILTER(event));
+
+	return ret;
+}
+
 static int arm_cmn_event_add(struct perf_event *event, int flags)
 {
 	struct arm_cmn *cmn = to_cmn(event->pmu);
@@ -1981,7 +2036,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
 			input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
 				    (nid.port << 4) + (nid.dev << 2);
 
-			if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_FILTER(event)))
+			if (arm_cmn_set_event_filter(dn, event))
 				goto free_dtms;
 		}
 
@@ -2311,6 +2366,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 	void __iomem *cfg_region, __iomem *xp_region;
 	struct arm_cmn_node cfg, *dn;
 	struct arm_cmn_dtm *dtm;
+	enum cmn_model model;
 	enum cmn_part part;
 	u16 child_count, child_poff;
 	u64 reg;
@@ -2342,12 +2398,14 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 			 "Firmware binding mismatch: expected part number 0x%x, found 0x%x\n",
 			 cmn->part, part);
 	cmn->part = part;
-	if (!arm_cmn_model(cmn))
-		dev_warn(cmn->dev, "Unknown part number: 0x%x\n", part);
 
 	reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_23);
 	cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg);
 
+	model = arm_cmn_model(cmn);
+	if (!model)
+		dev_warn(cmn->dev, "Unknown part number: 0x%x\n", part);
+
 	/*
 	 * With the device isolation feature, if firmware has neglected to enable
 	 * an XP port then we risk locking up if we try to access anything behind
@@ -2500,6 +2558,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 				dev_err(cmn->dev, "Node ID invalid for supported CMN versions: %d\n", dn->logid);
 				return -ENODEV;
 			}
+			/*
+			 * We can utilise the "wasted" filter array slot to store
+			 * the index for referencing the filter encodings later.
+			 */
+			dn->filter[SEL_NONE].val = arm_cmn_filter(dn->type, model);
 
 			switch (dn->type) {
 			case CMN_TYPE_DTC:
Signature · 1 line
2.54.0.dirty
Reviewed-by on v1 2/5
#4 lore raw

Re: [PATCH 2/5] perf/arm-cmn: Refactor event filter programming

Quoted 7 lines · Robin Murphy

We're soon going to need to cope with events having multiple filters, plus the filter fields themselves moving around, wherein any more inline if/else logic will struggle to scale. Add a more general abstraction for the node-specific filter controls, and rejig the pmu_event_sel filter programming around it in a more extensible manner.

Looks good to me

Cheers, Ilkka

Quoted 258 lines

---
drivers/perf/arm-cmn.c | 139 ++++++++++++++++++++++++++++++-----------

1 file changed, 101 insertions(+), 38 deletions(-)
drivers/perf/arm-cmn.c +101 -38
index 2a8a67da72c3..14c267d2e2f9 100644
@@ -278,8 +278,8 @@ enum cmn_node_type {
 };
 
 enum cmn_filter_select {
-	SEL_NONE = -1,
-	SEL_OCCUP1ID,
+	SEL_NONE,
+	SEL_OCCUP1_ID,
 	SEL_CLASS_OCCUP_ID,
 	SEL_CBUSY_SNTHROTTLE_SEL,
 	SEL_HBT_LBT_SEL,
@@ -599,6 +599,57 @@ static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id)
 static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id) {}
 #endif
 
+enum cmn_filter_type {
+	FILT_NONE,
+	FILT_OCCUP1_ID,
+	FILT_HNF_700,
+	FILT_HNS,
+};
+#define CMN_FILTER(_sel)	[SEL_##_sel] = CMN__PMU_##_sel
+
+static const u64 arm_cmn_filters[][SEL_MAX] = {
+	[FILT_NONE] = {},
+	/* DVM etc. */
+	[FILT_OCCUP1_ID] = {
+		CMN_FILTER(OCCUP1_ID)
+	},
+	/* Newer HN-F */
+	[FILT_HNF_700] = {
+		CMN_FILTER(OCCUP1_ID),
+		CMN_FILTER(CLASS_OCCUP_ID),
+		CMN_FILTER(CBUSY_SNTHROTTLE_SEL)
+	},
+	/* HN-S */
+	[FILT_HNS] = {
+		CMN_FILTER(OCCUP1_ID),
+		CMN_FILTER(CLASS_OCCUP_ID),
+		CMN_FILTER(CBUSY_SNTHROTTLE_SEL),
+		CMN_FILTER(HBT_LBT_SEL),
+		CMN_FILTER(SN_HOME_SEL)
+	},
+};
+
+static enum cmn_filter_type arm_cmn_filter(enum cmn_node_type node,
+					   enum cmn_model model)
+{
+	switch (node) {
+	default:
+		return FILT_NONE;
+	case CMN_TYPE_DVM:
+	case CMN_TYPE_CXRA:
+	case CMN_TYPE_CXHA:
+	case CMN_TYPE_CCRA:
+	case CMN_TYPE_CCHA:
+		return FILT_OCCUP1_ID;
+	case CMN_TYPE_HNF:
+		if (model < CMN700)
+			return FILT_OCCUP1_ID;
+		return FILT_HNF_700;
+	case CMN_TYPE_HNS:
+		return FILT_HNS;
+	};
+}
+
 struct arm_cmn_hw_event {
 	struct arm_cmn_node *dn;
 	union {
@@ -708,7 +759,7 @@ static ssize_t arm_cmn_event_show(struct device *dev,
 				  "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
 				  eattr->type, eattr->eventid);
 
-	if (eattr->fsel > SEL_NONE)
+	if (eattr->fsel)
 		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n",
 				  eattr->type, eattr->eventid, eattr->filter);
 
@@ -832,16 +883,16 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 #define CMN_EVENT_DVM(_model, _name, _event)			\
 	_CMN_EVENT_DVM(_model, _name, _event, 0, SEL_NONE)
 #define CMN_EVENT_DVM_OCC(_model, _name, _event)			\
-	_CMN_EVENT_DVM(_model, _name##_all, _event, 0, SEL_OCCUP1ID),	\
-	_CMN_EVENT_DVM(_model, _name##_dvmop, _event, 1, SEL_OCCUP1ID),	\
-	_CMN_EVENT_DVM(_model, _name##_dvmsync, _event, 2, SEL_OCCUP1ID)
+	_CMN_EVENT_DVM(_model, _name##_all, _event, 0, SEL_OCCUP1_ID),	\
+	_CMN_EVENT_DVM(_model, _name##_dvmop, _event, 1, SEL_OCCUP1_ID),	\
+	_CMN_EVENT_DVM(_model, _name##_dvmsync, _event, 2, SEL_OCCUP1_ID)
 
 #define CMN_EVENT_HN_OCC(_model, _name, _type, _event)		\
-	_CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 1, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 2, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, 3, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, 4, SEL_OCCUP1ID)
+	_CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 1, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 2, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, 3, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, 4, SEL_OCCUP1_ID)
 #define CMN_EVENT_HN_CLS(_model, _name, _type, _event)			\
 	_CMN_EVENT_ATTR(_model, _name##_class0, _type, _event, 0, SEL_CLASS_OCCUP_ID), \
 	_CMN_EVENT_ATTR(_model, _name##_class1, _type, _event, 1, SEL_CLASS_OCCUP_ID), \
@@ -865,9 +916,9 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 
 #define CMN_EVENT_HNS_OCC(_name, _event)				\
 	CMN_EVENT_HN_OCC(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event),	\
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, 5, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 6, SEL_OCCUP1ID), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 7, SEL_OCCUP1ID)
+	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, 5, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 6, SEL_OCCUP1_ID), \
+	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 7, SEL_OCCUP1_ID)
 #define CMN_EVENT_HNS_CLS( _name, _event)				\
 	CMN_EVENT_HN_CLS(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
 #define CMN_EVENT_HNS_SNT(_name, _event)				\
@@ -1547,27 +1598,20 @@ static void arm_cmn_event_read(struct perf_event *event)
 }
 
 static int arm_cmn_set_event_sel_hi(struct arm_cmn_node *dn,
-				    enum cmn_filter_select fsel, u8 filter)
+				    enum cmn_filter_select fsel, u8 val)
 {
-	u64 reg;
-
-	if (fsel == SEL_NONE)
-		return 0;
-
 	if (!dn->filter[fsel].count) {
-		dn->filter[fsel].val = filter;
-		reg = FIELD_PREP(CMN__PMU_CBUSY_SNTHROTTLE_SEL,
-				 dn->filter[SEL_CBUSY_SNTHROTTLE_SEL].val) |
-		      FIELD_PREP(CMN__PMU_SN_HOME_SEL,
-				 dn->filter[SEL_SN_HOME_SEL].val) |
-		      FIELD_PREP(CMN__PMU_HBT_LBT_SEL,
-				 dn->filter[SEL_HBT_LBT_SEL].val) |
-		      FIELD_PREP(CMN__PMU_CLASS_OCCUP_ID,
-				 dn->filter[SEL_CLASS_OCCUP_ID].val) |
-		      FIELD_PREP(CMN__PMU_OCCUP1_ID,
-				 dn->filter[SEL_OCCUP1ID].val);
+		const u64 *filter = arm_cmn_filters[dn->filter[SEL_NONE].val];
+		u64 reg = 0;
+
+		dn->filter[fsel].val = val;
+		for (int i = SEL_OCCUP1_ID; i < SEL_MAX; i++) {
+			if (filter[i])
+				reg |= field_prep(filter[i], dn->filter[i].val);
+		}
+
 		writel_relaxed(reg >> 32, dn->pmu_base + CMN_PMU_EVENT_SEL + 4);
-	} else if (dn->filter[fsel].val != filter) {
+	} else if (dn->filter[fsel].val != val) {
 		return -EBUSY;
 	}
 	dn->filter[fsel].count++;
@@ -1696,7 +1740,7 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
 
 		val->dtm_count[dtm]++;
 
-		if (sel > SEL_NONE)
+		if (sel)
 			val->filter[dtm][sel] = CMN_EVENT_FILTER(event) + 1;
 
 		if (type != CMN_TYPE_WP)
@@ -1748,7 +1792,7 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
 		if (val->dtm_count[dtm] == CMN_DTM_NUM_COUNTERS)
 			goto done;
 
-		if (sel > SEL_NONE && val->filter[dtm][sel] &&
+		if (sel && val->filter[dtm][sel] &&
 		    val->filter[dtm][sel] != CMN_EVENT_FILTER(event) + 1)
 			goto done;
 
@@ -1894,7 +1938,7 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 			dtm->wp_event[wp_idx] = -1;
 		}
 
-		if (hw->filter_sel > SEL_NONE)
+		if (hw->filter_sel)
 			hw->dn[i].filter[hw->filter_sel].count--;
 
 		dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
@@ -1906,6 +1950,17 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 		cmn->dtc[j].counters[idx] = NULL;
 }
 
+static int arm_cmn_set_event_filter(struct arm_cmn_node *dn, struct perf_event *event)
+{
+	enum cmn_filter_select fsel = to_cmn_hw(event)->filter_sel;
+	int ret = 0;
+
+	if (fsel)
+		ret = arm_cmn_set_event_sel_hi(dn, fsel, CMN_EVENT_FILTER(event));
+
+	return ret;
+}
+
 static int arm_cmn_event_add(struct perf_event *event, int flags)
 {
 	struct arm_cmn *cmn = to_cmn(event->pmu);
@@ -1981,7 +2036,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
 			input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx +
 				    (nid.port << 4) + (nid.dev << 2);
 
-			if (arm_cmn_set_event_sel_hi(dn, hw->filter_sel, CMN_EVENT_FILTER(event)))
+			if (arm_cmn_set_event_filter(dn, event))
 				goto free_dtms;
 		}
 
@@ -2311,6 +2366,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 	void __iomem *cfg_region, __iomem *xp_region;
 	struct arm_cmn_node cfg, *dn;
 	struct arm_cmn_dtm *dtm;
+	enum cmn_model model;
 	enum cmn_part part;
 	u16 child_count, child_poff;
 	u64 reg;
@@ -2342,12 +2398,14 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 			 "Firmware binding mismatch: expected part number 0x%x, found 0x%x\n",
 			 cmn->part, part);
 	cmn->part = part;
-	if (!arm_cmn_model(cmn))
-		dev_warn(cmn->dev, "Unknown part number: 0x%x\n", part);
 
 	reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_23);
 	cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg);
 
+	model = arm_cmn_model(cmn);
+	if (!model)
+		dev_warn(cmn->dev, "Unknown part number: 0x%x\n", part);
+
 	/*
 	 * With the device isolation feature, if firmware has neglected to enable
 	 * an XP port then we risk locking up if we try to access anything behind
@@ -2500,6 +2558,11 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 				dev_err(cmn->dev, "Node ID invalid for supported CMN versions: %d\n", dn->logid);
 				return -ENODEV;
 			}
+			/*
+			 * We can utilise the "wasted" filter array slot to store
+			 * the index for referencing the filter encodings later.
+			 */
+			dn->filter[SEL_NONE].val = arm_cmn_filter(dn->type, model);
 
 			switch (dn->type) {
 			case CMN_TYPE_DTC:
Signature · 1 line
2.54.0.dirty
author maintainer
#5 lore raw

PATCH 3/5 perf/arm-cmn: Refactor event filter data

In reply to Robin Murphy

The ABI hole I have dug myself into requires the driver to know which event encodings are associated with which particular filter control. Since we will soon have a notion of multiple filters per event, refactor the event data to encapsulate filters in an explicit structure, which can then more easily scale as an array in future.

---
 drivers/perf/arm-cmn.c | 95 ++++++++++++++++++++++--------------------
 1 file changed, 49 insertions(+), 46 deletions(-)
drivers/perf/arm-cmn.c +49 -46
index 14c267d2e2f9..4ba78df87045 100644
@@ -717,13 +717,17 @@ static void arm_cmn_clear_idx(struct arm_cmn_hw_event *hw)
 		bitmap_zero(hw->wp_idx, CMN_MAX_XPS);
 }
 
+struct arm_cmn_filter_attr {
+	enum cmn_filter_select sel;
+	u8 val;
+};
+
 struct arm_cmn_event_attr {
 	struct device_attribute attr;
 	enum cmn_model model;
 	enum cmn_node_type type;
-	enum cmn_filter_select fsel;
 	u16 eventid;
-	u8 filter;
+	struct arm_cmn_filter_attr filter[1];
 };
 
 struct arm_cmn_format_attr {
@@ -732,24 +736,25 @@ struct arm_cmn_format_attr {
 	int config;
 };
 
-#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, _fsel)\
+#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, ...)	\
 	(&((struct arm_cmn_event_attr[]) {{				\
 		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
 		.model = _model,					\
 		.type = _type,						\
 		.eventid = _eventid,					\
-		.filter = _filter,					\
-		.fsel = _fsel,						\
+		.filter = {{_filter}},					\
 	}})[0].attr.attr)
-#define CMN_EVENT_ATTR(_model, _name, _type, _eventid)			\
-	_CMN_EVENT_ATTR(_model, _name, _type, _eventid, 0, SEL_NONE)
+#define CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter...)	\
+	_CMN_EVENT_ATTR(_model, _name, _type, _eventid, ##_filter, 0, 0)
 
 static ssize_t arm_cmn_event_show(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
 	struct arm_cmn_event_attr *eattr;
+	struct arm_cmn_filter_attr *filter;
 
 	eattr = container_of(attr, typeof(*eattr), attr);
+	filter = eattr->filter;
 
 	if (eattr->type == CMN_TYPE_DTC)
 		return sysfs_emit(buf, "type=0x%x\n", eattr->type);
@@ -759,9 +764,9 @@ static ssize_t arm_cmn_event_show(struct device *dev,
 				  "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
 				  eattr->type, eattr->eventid);
 
-	if (eattr->fsel)
+	if (filter[0].sel)
 		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n",
-				  eattr->type, eattr->eventid, eattr->filter);
+				  eattr->type, eattr->eventid, filter[0].val);
 
 	return sysfs_emit(buf, "type=0x%x,eventid=0x%x\n", eattr->type,
 			  eattr->eventid);
@@ -849,8 +854,8 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 	return attr->mode;
 }
 
-#define _CMN_EVENT_DVM(_model, _name, _event, _occup, _fsel)	\
-	_CMN_EVENT_ATTR(_model, dn_##_name, CMN_TYPE_DVM, _event, _occup, _fsel)
+#define CMN_EVENT_DVM(_model, _name, _event, _filter...)	\
+	CMN_EVENT_ATTR(_model, dn_##_name, CMN_TYPE_DVM, _event, ##_filter)
 #define CMN_EVENT_DTC(_name)					\
 	CMN_EVENT_ATTR(CMN_ANY, dtc_##_name, CMN_TYPE_DTC, 0)
 #define CMN_EVENT_HNF(_model, _name, _event)			\
@@ -880,32 +885,30 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 #define CMN_EVENT_HNS(_name, _event)				\
 	CMN_EVENT_ATTR(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
 
-#define CMN_EVENT_DVM(_model, _name, _event)			\
-	_CMN_EVENT_DVM(_model, _name, _event, 0, SEL_NONE)
 #define CMN_EVENT_DVM_OCC(_model, _name, _event)			\
-	_CMN_EVENT_DVM(_model, _name##_all, _event, 0, SEL_OCCUP1_ID),	\
-	_CMN_EVENT_DVM(_model, _name##_dvmop, _event, 1, SEL_OCCUP1_ID),	\
-	_CMN_EVENT_DVM(_model, _name##_dvmsync, _event, 2, SEL_OCCUP1_ID)
+	CMN_EVENT_DVM(_model, _name##_all, _event, SEL_OCCUP1_ID, 0),	\
+	CMN_EVENT_DVM(_model, _name##_dvmop, _event, SEL_OCCUP1_ID, 1),	\
+	CMN_EVENT_DVM(_model, _name##_dvmsync, _event, SEL_OCCUP1_ID, 2)
 
 #define CMN_EVENT_HN_OCC(_model, _name, _type, _event)		\
-	_CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_OCCUP1_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 1, SEL_OCCUP1_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 2, SEL_OCCUP1_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, 3, SEL_OCCUP1_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, 4, SEL_OCCUP1_ID)
+	CMN_EVENT_ATTR(_model, _name##_all, _type, _event, SEL_OCCUP1_ID, 0), \
+	CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_OCCUP1_ID, 1), \
+	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_OCCUP1_ID, 2), \
+	CMN_EVENT_ATTR(_model, _name##_atomic, _type, _event, SEL_OCCUP1_ID, 3), \
+	CMN_EVENT_ATTR(_model, _name##_stash, _type, _event, SEL_OCCUP1_ID, 4)
 #define CMN_EVENT_HN_CLS(_model, _name, _type, _event)			\
-	_CMN_EVENT_ATTR(_model, _name##_class0, _type, _event, 0, SEL_CLASS_OCCUP_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_class1, _type, _event, 1, SEL_CLASS_OCCUP_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_class2, _type, _event, 2, SEL_CLASS_OCCUP_ID), \
-	_CMN_EVENT_ATTR(_model, _name##_class3, _type, _event, 3, SEL_CLASS_OCCUP_ID)
+	CMN_EVENT_ATTR(_model, _name##_class0, _type, _event, SEL_CLASS_OCCUP_ID, 0), \
+	CMN_EVENT_ATTR(_model, _name##_class1, _type, _event, SEL_CLASS_OCCUP_ID, 1), \
+	CMN_EVENT_ATTR(_model, _name##_class2, _type, _event, SEL_CLASS_OCCUP_ID, 2), \
+	CMN_EVENT_ATTR(_model, _name##_class3, _type, _event, SEL_CLASS_OCCUP_ID, 3)
 #define CMN_EVENT_HN_SNT(_model, _name, _type, _event)			\
-	_CMN_EVENT_ATTR(_model, _name##_all, _type, _event, 0, SEL_CBUSY_SNTHROTTLE_SEL), \
-	_CMN_EVENT_ATTR(_model, _name##_group0_read, _type, _event, 1, SEL_CBUSY_SNTHROTTLE_SEL), \
-	_CMN_EVENT_ATTR(_model, _name##_group0_write, _type, _event, 2, SEL_CBUSY_SNTHROTTLE_SEL), \
-	_CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, 3, SEL_CBUSY_SNTHROTTLE_SEL), \
-	_CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, 4, SEL_CBUSY_SNTHROTTLE_SEL), \
-	_CMN_EVENT_ATTR(_model, _name##_read, _type, _event, 5, SEL_CBUSY_SNTHROTTLE_SEL), \
-	_CMN_EVENT_ATTR(_model, _name##_write, _type, _event, 6, SEL_CBUSY_SNTHROTTLE_SEL)
+	CMN_EVENT_ATTR(_model, _name##_all, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 0), \
+	CMN_EVENT_ATTR(_model, _name##_group0_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 1), \
+	CMN_EVENT_ATTR(_model, _name##_group0_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 2), \
+	CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 3), \
+	CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 4), \
+	CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 5), \
+	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6)
 
 #define CMN_EVENT_HNF_OCC(_model, _name, _event)			\
 	CMN_EVENT_HN_OCC(_model, hnf_##_name, CMN_TYPE_HNF, _event)
@@ -916,21 +919,21 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 
 #define CMN_EVENT_HNS_OCC(_name, _event)				\
 	CMN_EVENT_HN_OCC(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event),	\
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, 5, SEL_OCCUP1_ID), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 6, SEL_OCCUP1_ID), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 7, SEL_OCCUP1_ID)
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, SEL_OCCUP1_ID, 5), \
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, SEL_OCCUP1_ID, 6), \
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, SEL_OCCUP1_ID, 7)
 #define CMN_EVENT_HNS_CLS( _name, _event)				\
 	CMN_EVENT_HN_CLS(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
 #define CMN_EVENT_HNS_SNT(_name, _event)				\
 	CMN_EVENT_HN_SNT(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
 #define CMN_EVENT_HNS_HBT(_name, _event)				\
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, 0, SEL_HBT_LBT_SEL), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, 1, SEL_HBT_LBT_SEL), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, 2, SEL_HBT_LBT_SEL)
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, SEL_HBT_LBT_SEL, 0), \
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, SEL_HBT_LBT_SEL, 1), \
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, SEL_HBT_LBT_SEL, 2)
 #define CMN_EVENT_HNS_SNH(_name, _event)				\
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, 0, SEL_SN_HOME_SEL), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_sn, CMN_TYPE_HNS, _event, 1, SEL_SN_HOME_SEL), \
-	_CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_home, CMN_TYPE_HNS, _event, 2, SEL_SN_HOME_SEL)
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, SEL_SN_HOME_SEL, 0), \
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_sn, CMN_TYPE_HNS, _event, SEL_SN_HOME_SEL, 1), \
+	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_home, CMN_TYPE_HNS, _event, SEL_SN_HOME_SEL, 2)
 
 #define _CMN_EVENT_XP_MESH(_name, _event)			\
 	__CMN_EVENT_XP(e_##_name, (_event) | (0 << 2)),		\
@@ -1814,9 +1817,9 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
 	return ret;
 }
 
-static enum cmn_filter_select arm_cmn_filter_sel(const struct arm_cmn *cmn,
-						 enum cmn_node_type type,
-						 unsigned int eventid)
+static enum cmn_filter_select arm_cmn_event_filter(const struct arm_cmn *cmn,
+						   enum cmn_node_type type,
+						   unsigned int eventid)
 {
 	struct arm_cmn_event_attr *e;
 	enum cmn_model model = arm_cmn_model(cmn);
@@ -1824,7 +1827,7 @@ static enum cmn_filter_select arm_cmn_filter_sel(const struct arm_cmn *cmn,
 	for (int i = 0; i < ARRAY_SIZE(arm_cmn_event_attrs) - 1; i++) {
 		e = container_of(arm_cmn_event_attrs[i], typeof(*e), attr.attr);
 		if (e->model & model && e->type == type && e->eventid == eventid)
-			return e->fsel;
+			return e->filter[0].sel;
 	}
 	return SEL_NONE;
 }
@@ -1889,7 +1892,7 @@ static int arm_cmn_event_init(struct perf_event *event)
 	}
 
 	/* This is sufficiently annoying to recalculate, so cache it */
-	hw->filter_sel = arm_cmn_filter_sel(cmn, type, eventid);
+	hw->filter_sel = arm_cmn_event_filter(cmn, type, eventid);
 
 	bynodeid = CMN_EVENT_BYNODEID(event);
 	nodeid = CMN_EVENT_NODEID(event);
Signature · 1 line
2.54.0.dirty
#6 lore raw

Re: [PATCH 3/5] perf/arm-cmn: Refactor event filter data

On Tue, Jun 30, 2026 at 04:19:18PM +0100, Robin Murphy wrote:

[...]

Quoted 5 lines
(quoted diff) +4 -0
+struct arm_cmn_filter_attr {
+	enum cmn_filter_select sel;
+	u8 val;
+};
 

Review

Quoted 10 lines
(quoted diff) +2 -3
-#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, _fsel)\
+#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, ...)	\
 	(&((struct arm_cmn_event_attr[]) {{				\
 		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
 		.model = _model,					\
 		.type = _type,						\
 		.eventid = _eventid,					\
-		.filter = _filter,					\
-		.fsel = _fsel,						\
+		.filter = {{_filter}},					\

This patch might break as the "val" field is never initialized. Should it use two parameters "_fa, _fb" instead?

#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _fa, _fb, ...) \
      (&((struct arm_cmn_event_attr[]) {{                              \
              ...                                                      \
              .filter = {{_fa, _fb}},                                  \

Although the issue is fixed by a later patch, I think it is good to correct this patch for bisection.

Thanks,
Leo

author maintainer
#7 lore raw

Re: [PATCH 3/5] perf/arm-cmn: Refactor event filter data

Quoted 9 lines · Leo Yan

On Tue, Jun 30, 2026 at 04:19:18PM +0100, Robin Murphy wrote:

[...]

Quoted 4 lines
drivers/perf/arm-cmn.c +4 -0
+struct arm_cmn_filter_attr {
+	enum cmn_filter_select sel;
+	u8 val;
+};
Quoted 13 lines

drivers/perf/arm-cmn.c struct arm_cmn_format_attr { · line 745

Quoted 10 lines
drivers/perf/arm-cmn.c +2 -3
-#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, _fsel)\
+#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, ...)	\
  	(&((struct arm_cmn_event_attr[]) {{				\
  		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
  		.model = _model,					\
  		.type = _type,						\
  		.eventid = _eventid,					\
-		.filter = _filter,					\
-		.fsel = _fsel,						\
+		.filter = {{_filter}},					\

This patch might break as the "val" field is never initialized. Should it use two parameters "_fa, _fb" instead?

Ah, you're right - this evolved through several different designs, and this line is still from when "_filter..." was the whole varargs, but then I brought forward more of the changes from patch #5 to try to minimise intra-series churn, and I guess the fact that it didn't completely fail to compile (as I was accustomed to by that point...) meant I missed this subtlety.

Quoted 7 lines
#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _fa, _fb, ...) \
       (&((struct arm_cmn_event_attr[]) {{                              \
               ...                                                      \
               .filter = {{_fa, _fb}},                                  \

Although the issue is fixed by a later patch, I think it is good to correct this patch for bisection.

Agreed, I'll fix that for a v2.

Thanks,
Robin.

author maintainer
#8 lore raw

PATCH 4/5 perf/arm-cmn: Add new filters

In reply to Robin Murphy

Add the logic to handle events with the upcoming new filter controls. Since for now we will have the sole invariant of all EVICT_STATE_SEL events having HBT_LBT_SEL as a secondary filter, for the sake of simplicity we can just special-case that, and save the complication of a full abstraction until unavoidably necessary.

---
 drivers/perf/arm-cmn.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
drivers/perf/arm-cmn.c +19 -0
index 4ba78df87045..c935ddcf462a 100644
@@ -166,12 +166,14 @@
 #define CMN_CONFIG_FILTER		GENMASK_ULL(30, 27)
 #define CMN_CONFIG_BYNODEID		BIT_ULL(31)
 #define CMN_CONFIG_NODEID		GENMASK_ULL(47, 32)
+#define CMN_CONFIG_FILTER2		GENMASK_ULL(51, 48)
 
 #define CMN_EVENT_TYPE(event)		FIELD_GET(CMN_CONFIG_TYPE, (event)->attr.config)
 #define CMN_EVENT_EVENTID(event)	FIELD_GET(CMN_CONFIG_EVENTID, (event)->attr.config)
 #define CMN_EVENT_FILTER(event)		FIELD_GET(CMN_CONFIG_FILTER, (event)->attr.config)
 #define CMN_EVENT_BYNODEID(event)	FIELD_GET(CMN_CONFIG_BYNODEID, (event)->attr.config)
 #define CMN_EVENT_NODEID(event)		FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config)
+#define CMN_EVENT_FILTER2(event)	FIELD_GET(CMN_CONFIG_FILTER2, (event)->attr.config)
 
 #define CMN_CONFIG_WP_COMBINE		GENMASK_ULL(30, 27)
 #define CMN_CONFIG_WP_DEV_SEL		GENMASK_ULL(50, 48)
@@ -284,6 +286,9 @@ enum cmn_filter_select {
 	SEL_CBUSY_SNTHROTTLE_SEL,
 	SEL_HBT_LBT_SEL,
 	SEL_SN_HOME_SEL,
+	SEL_SNP_VC_SEL,
+	SEL_ENHANCED_HBT_LBT_SEL,
+	SEL_EVICT_STATE_SEL,
 	SEL_MAX
 };
 
@@ -1377,6 +1382,7 @@ static struct attribute *arm_cmn_format_attrs[] = {
 	CMN_FORMAT_ATTR(filter, CMN_CONFIG_FILTER),
 	CMN_FORMAT_ATTR(bynodeid, CMN_CONFIG_BYNODEID),
 	CMN_FORMAT_ATTR(nodeid, CMN_CONFIG_NODEID),
+	CMN_FORMAT_ATTR(filter2, CMN_CONFIG_FILTER2),
 
 	CMN_FORMAT_ATTR(wp_dev_sel, CMN_CONFIG_WP_DEV_SEL),
 	CMN_FORMAT_ATTR(wp_chn_sel, CMN_CONFIG_WP_CHN_SEL),
@@ -1745,6 +1751,8 @@ static void arm_cmn_val_add_event(struct arm_cmn *cmn, struct arm_cmn_val *val,
 
 		if (sel)
 			val->filter[dtm][sel] = CMN_EVENT_FILTER(event) + 1;
+		if (sel == SEL_EVICT_STATE_SEL)
+			val->filter[dtm][SEL_HBT_LBT_SEL] = CMN_EVENT_FILTER2(event) + 1;
 
 		if (type != CMN_TYPE_WP)
 			continue;
@@ -1799,6 +1807,10 @@ static int arm_cmn_validate_group(struct arm_cmn *cmn, struct perf_event *event)
 		    val->filter[dtm][sel] != CMN_EVENT_FILTER(event) + 1)
 			goto done;
 
+		if (sel == SEL_EVICT_STATE_SEL && val->filter[dtm][SEL_HBT_LBT_SEL] &&
+		    val->filter[dtm][SEL_HBT_LBT_SEL] != CMN_EVENT_FILTER2(event) + 1)
+			goto done;
+
 		if (type != CMN_TYPE_WP)
 			continue;
 
@@ -1943,6 +1955,8 @@ static void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event,
 
 		if (hw->filter_sel)
 			hw->dn[i].filter[hw->filter_sel].count--;
+		if (hw->filter_sel == SEL_EVICT_STATE_SEL)
+			hw->dn[i].filter[SEL_HBT_LBT_SEL].count--;
 
 		dtm->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx);
 		writel_relaxed(dtm->pmu_config_low, dtm->base + CMN_DTM_PMU_CONFIG);
@@ -1961,6 +1975,11 @@ static int arm_cmn_set_event_filter(struct arm_cmn_node *dn, struct perf_event *
 	if (fsel)
 		ret = arm_cmn_set_event_sel_hi(dn, fsel, CMN_EVENT_FILTER(event));
 
+	if (fsel == SEL_EVICT_STATE_SEL && !ret) {
+		ret = arm_cmn_set_event_sel_hi(dn, SEL_HBT_LBT_SEL, CMN_EVENT_FILTER2(event));
+		if (ret)
+			dn->filter[fsel].count--;
+	}
 	return ret;
 }
 
Signature · 1 line
2.54.0.dirty
Reviewed-by on v1 4/5
#9 lore raw

Re: [PATCH 4/5] perf/arm-cmn: Add new filters

On Tue, Jun 30, 2026 at 04:19:19PM +0100, Robin Murphy wrote:

[...]

drivers/perf/arm-cmn.c enum cmn_filter_select { · line 293

Quoted 9 lines
drivers/perf/arm-cmn.c +3 -0
@@ -284,6 +286,9 @@ enum cmn_filter_select {
 	SEL_CBUSY_SNTHROTTLE_SEL,
 	SEL_HBT_LBT_SEL,
 	SEL_SN_HOME_SEL,
+	SEL_SNP_VC_SEL,
+	SEL_ENHANCED_HBT_LBT_SEL,
+	SEL_EVICT_STATE_SEL,
 	SEL_MAX
 };

Though SEL_SNP_VC_SEL/SEL_ENHANCED_HBT_LBT_SEL are not really used in this patch, I understand this is preparing for new filters.

The secondary filter between SEL_EVICT_STATE_SEL and SEL_HBT_LBT_SEL also looks good to me:

author maintainer
#10 lore raw

PATCH 5/5 perf/arm-cmn: Support CMN S3 r2

In reply to Robin Murphy

If you were disappointed at how minimal the initial CMN S3 support looked compared to previous versions, then oh boy is it time to put your party hats on... The biggest batch of incompatible changes yet comes not with a new CMN product, but a point release of an existing one. We've got new filters, loads of changes to existing events, register fields moved around for no good reason, and much, much more! On the upside, we do at least gain a means of working around the isolation feature.

As such, for the sake of sanity in the driver it is easiest to split it into a distict "model" for our internal abstractions despite it bearing the same part number as r0/r1.

---
 drivers/perf/arm-cmn.c | 270 +++++++++++++++++++++++++++++------------
 1 file changed, 193 insertions(+), 77 deletions(-)
drivers/perf/arm-cmn.c +193 -77
index c935ddcf462a..a03e2a8ca41e 100644
@@ -29,6 +29,7 @@
 #define CMN_CI_CHILD_PTR_OFFSET		GENMASK_ULL(31, 16)
 
 #define CMN_CHILD_NODE_ADDR		GENMASK(29, 0)
+#define CMN_CHILD_NODE_ISOLATED		BIT(30)
 #define CMN_CHILD_NODE_EXTERNAL		BIT(31)
 
 /* Some implementations use a mesh larger than the architectural max of 12 */
@@ -48,11 +49,16 @@
 
 #define CMN_CFGM_INFO_GLOBAL		0x0900
 #define CMN_INFO_MULTIPLE_DTM_EN	BIT_ULL(63)
+#define CMN_S3_R2_MULTIPLE_DTM_EN	BIT_ULL(59)
 #define CMN_INFO_RSP_VC_NUM		GENMASK_ULL(53, 52)
 #define CMN_INFO_DAT_VC_NUM		GENMASK_ULL(51, 50)
 #define CMN_INFO_DEVICE_ISO_ENABLE	BIT_ULL(44)
 
 #define CMN_CFGM_INFO_GLOBAL_1		0x0908
+#define CMN_S3_R2_RSP_VC_NUM		GENMASK_ULL(11, 9)
+#define CMN_S3_R2_DAT_VC_NUM		GENMASK_ULL(8, 6)
+#define CMN_S3_R2_SNP_VC_NUM		GENMASK_ULL(5, 3)
+#define CMN_S3_R2_REQ_VC_NUM		GENMASK_ULL(2, 0)
 #define CMN_INFO_SNP_VC_NUM		GENMASK_ULL(3, 2)
 #define CMN_INFO_REQ_VC_NUM		GENMASK_ULL(1, 0)
 
@@ -78,6 +84,16 @@
 /* Technically this is 4 bits wide on DNs, but we only use 2 there anyway */
 #define CMN__PMU_OCCUP1_ID		GENMASK_ULL(34, 32)
 
+/* But then... */
+#define CMN__PMU_EVICT_STATE_SEL	GENMASK_ULL(54, 52)
+#define CMN__PMU_ENHANCED_HBT_LBT_SEL	GENMASK_ULL(51, 48)
+#define CMN__PMU_SNP_VC_SEL		GENMASK_ULL(47, 46)
+#define CMN__S3_R2_CBUSY_SNTHROTTLE_SEL	GENMASK_ULL(45, 42)
+#define CMN__S3_R2_SN_HOME_SEL		GENMASK_ULL(41, 40)
+#define CMN__S3_R2_HBT_LBT_SEL		GENMASK_ULL(39, 38)
+#define CMN__S3_R2_CLASS_OCCUP_ID	GENMASK_ULL(37, 36)
+#define CMN__S3_R2_OCCUP1_ID		GENMASK_ULL(35, 32)
+
 /* Some types are designed to coexist with another device in the same node */
 #define CMN_CCLA_PMU_EVENT_SEL		0x008
 #define CMN_HNP_PMU_EVENT_SEL		0x008
@@ -202,12 +218,14 @@ enum cmn_model {
 	CMN650 = 2,
 	CI700 = 4,
 	CMN700 = 8,
-	CMNS3 = 16,
+	CMNS3R01 = 16,
+	CMNS3R2 = 32,
 	/* ...and then we can use bitmap tricks for commonality */
 	CMN_ANY = -1,
 	NOT_CMN600 = -2,
 	CMN_700ON = ~(CMN700 - 1),
 	CMN_650ON = CMN_700ON | CMN650,
+	CMNS3 = CMNS3R01 | CMNS3R2,
 };
 
 /* Actual part numbers and revision IDs defined by the hardware */
@@ -243,6 +261,10 @@ enum cmn_revision {
 	REV_CMNS3_R0P0 = 0,
 	REV_CMNS3_R0P1,
 	REV_CMNS3_R1P0,
+	REV_CMNS3_R2P0,
+	REV_CMNS3_R2P1,
+	REV_CMNS3_R2P2,
+	REV_CMNS3_R2P5,
 	REV_CI700_R0P0 = 0,
 	REV_CI700_R1P0,
 	REV_CI700_R2P0,
@@ -429,7 +451,9 @@ static enum cmn_model arm_cmn_model(const struct arm_cmn *cmn)
 	case PART_CI700:
 		return CI700;
 	case PART_CMN_S3:
-		return CMNS3;
+		if (cmn->rev >= REV_CMNS3_R2P0)
+			return CMNS3R2;
+		return CMNS3R01;
 	default:
 		return 0;
 	};
@@ -609,8 +633,10 @@ enum cmn_filter_type {
 	FILT_OCCUP1_ID,
 	FILT_HNF_700,
 	FILT_HNS,
+	FILT_HNS_S3R2,
 };
 #define CMN_FILTER(_sel)	[SEL_##_sel] = CMN__PMU_##_sel
+#define CMN_FILTER_V2(_sel)	[SEL_##_sel] = CMN__S3_R2_##_sel
 
 static const u64 arm_cmn_filters[][SEL_MAX] = {
 	[FILT_NONE] = {},
@@ -632,6 +658,17 @@ static const u64 arm_cmn_filters[][SEL_MAX] = {
 		CMN_FILTER(HBT_LBT_SEL),
 		CMN_FILTER(SN_HOME_SEL)
 	},
+	/* Newer HN-S */
+	[FILT_HNS_S3R2] = {
+		CMN_FILTER_V2(OCCUP1_ID),
+		CMN_FILTER_V2(CLASS_OCCUP_ID),
+		CMN_FILTER_V2(CBUSY_SNTHROTTLE_SEL),
+		CMN_FILTER_V2(HBT_LBT_SEL),
+		CMN_FILTER_V2(SN_HOME_SEL),
+		CMN_FILTER(SNP_VC_SEL),
+		CMN_FILTER(ENHANCED_HBT_LBT_SEL),
+		CMN_FILTER(EVICT_STATE_SEL)
+	}
 };
 
 static enum cmn_filter_type arm_cmn_filter(enum cmn_node_type node,
@@ -651,7 +688,9 @@ static enum cmn_filter_type arm_cmn_filter(enum cmn_node_type node,
 			return FILT_OCCUP1_ID;
 		return FILT_HNF_700;
 	case CMN_TYPE_HNS:
-		return FILT_HNS;
+		if (model < CMNS3R2)
+			return FILT_HNS;
+		return FILT_HNS_S3R2;
 	};
 }
 
@@ -732,7 +771,7 @@ struct arm_cmn_event_attr {
 	enum cmn_model model;
 	enum cmn_node_type type;
 	u16 eventid;
-	struct arm_cmn_filter_attr filter[1];
+	struct arm_cmn_filter_attr filter[2];
 };
 
 struct arm_cmn_format_attr {
@@ -741,16 +780,16 @@ struct arm_cmn_format_attr {
 	int config;
 };
 
-#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter, ...)	\
+#define _CMN_EVENT_ATTR(_model, _name, _type, _eventid, _fa, _fb, _fc, _fd, ...) \
 	(&((struct arm_cmn_event_attr[]) {{				\
 		.attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL),	\
 		.model = _model,					\
 		.type = _type,						\
 		.eventid = _eventid,					\
-		.filter = {{_filter}},					\
+		.filter = {{_fa, _fb}, {_fc, _fd}},			\
 	}})[0].attr.attr)
 #define CMN_EVENT_ATTR(_model, _name, _type, _eventid, _filter...)	\
-	_CMN_EVENT_ATTR(_model, _name, _type, _eventid, ##_filter, 0, 0)
+	_CMN_EVENT_ATTR(_model, _name, _type, _eventid, ##_filter, 0, 0, 0, 0)
 
 static ssize_t arm_cmn_event_show(struct device *dev,
 				  struct device_attribute *attr, char *buf)
@@ -769,6 +808,10 @@ static ssize_t arm_cmn_event_show(struct device *dev,
 				  "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n",
 				  eattr->type, eattr->eventid);
 
+	if (filter[1].sel)
+		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x,filter2=0x%x\n",
+				  eattr->type, eattr->eventid, filter[0].val, filter[1].val);
+
 	if (filter[0].sel)
 		return sysfs_emit(buf, "type=0x%x,eventid=0x%x,filter=0x%x\n",
 				  eattr->type, eattr->eventid, filter[0].val);
@@ -887,8 +930,8 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 	CMN_EVENT_ATTR(_model, ccha_##_name, CMN_TYPE_CCHA, _event)
 #define CMN_EVENT_CCLA(_name, _event)				\
 	CMN_EVENT_ATTR(CMN_ANY, ccla_##_name, CMN_TYPE_CCLA, _event)
-#define CMN_EVENT_HNS(_name, _event)				\
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
+#define _CMN_EVENT_HNS(_model, _name, _event, _filter...)		\
+	CMN_EVENT_ATTR(_model, hns_##_name, CMN_TYPE_HNS, _event, ##_filter)
 
 #define CMN_EVENT_DVM_OCC(_model, _name, _event)			\
 	CMN_EVENT_DVM(_model, _name##_all, _event, SEL_OCCUP1_ID, 0),	\
@@ -913,7 +956,12 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 	CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 3), \
 	CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 4), \
 	CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 5), \
-	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6)
+	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 9), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 10), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 11), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 12), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 13)
 
 #define CMN_EVENT_HNF_OCC(_model, _name, _event)			\
 	CMN_EVENT_HN_OCC(_model, hnf_##_name, CMN_TYPE_HNF, _event)
@@ -922,23 +970,75 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 #define CMN_EVENT_HNF_SNT(_model, _name, _event)			\
 	CMN_EVENT_HN_SNT(_model, hnf_##_name, CMN_TYPE_HNF, _event)
 
-#define CMN_EVENT_HNS_OCC(_name, _event)				\
-	CMN_EVENT_HN_OCC(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event),	\
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_rxsnp, CMN_TYPE_HNS, _event, SEL_OCCUP1_ID, 5), \
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, SEL_OCCUP1_ID, 6), \
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, SEL_OCCUP1_ID, 7)
+#define CMN_EVENT_HNS(_name, _event)					\
+	_CMN_EVENT_HNS(CMN_ANY, _name, _event)
+#define CMN_EVENT_HNSR0(_name, _event)					\
+	_CMN_EVENT_HNS(CMN700 | CMNS3R01, _name, _event)
+#define _CMN_EVENT_HNS_HBT(_model, _name, _event, _sel)			\
+	_CMN_EVENT_HNS(_model, _name##_all, _event, _sel, 0),		\
+	_CMN_EVENT_HNS(_model, _name##_hbt, _event, _sel, 1),		\
+	_CMN_EVENT_HNS(_model, _name##_lbt, _event, _sel, 2)
+#define _CMN_EVENT_HNS_HBT2(_model, _name, _event, _fsel1, f1)		\
+	_CMN_EVENT_HNS(_model, _name##_all, _event, _fsel1, f1, SEL_HBT_LBT_SEL, 0), \
+	_CMN_EVENT_HNS(_model, _name##_hbt, _event, _fsel1, f1, SEL_HBT_LBT_SEL, 1), \
+	_CMN_EVENT_HNS(_model, _name##_lbt, _event, _fsel1, f1, SEL_HBT_LBT_SEL, 2)
+
+#define CMN_EVENT_HNS_OCC(_model, _name, _event)			\
+	CMN_EVENT_HN_OCC(_model, hns_##_name, CMN_TYPE_HNS, _event),	\
+	_CMN_EVENT_HNS(_model, _name##_rxsnp, _event, SEL_OCCUP1_ID, 5), \
+	_CMN_EVENT_HNS(_model, _name##_lbt, _event, SEL_OCCUP1_ID, 6),	\
+	_CMN_EVENT_HNS(_model, _name##_hbt, _event, SEL_OCCUP1_ID, 7),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rnf, _event, SEL_OCCUP1_ID, 8),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rni, _event, SEL_OCCUP1_ID, 9),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccglcn, _event, SEL_OCCUP1_ID, 10), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccgrn, _event, SEL_OCCUP1_ID, 11)
 #define CMN_EVENT_HNS_CLS( _name, _event)				\
 	CMN_EVENT_HN_CLS(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
-#define CMN_EVENT_HNS_SNT(_name, _event)				\
-	CMN_EVENT_HN_SNT(CMN_ANY, hns_##_name, CMN_TYPE_HNS, _event)
-#define CMN_EVENT_HNS_HBT(_name, _event)				\
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, SEL_HBT_LBT_SEL, 0), \
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_hbt, CMN_TYPE_HNS, _event, SEL_HBT_LBT_SEL, 1), \
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_lbt, CMN_TYPE_HNS, _event, SEL_HBT_LBT_SEL, 2)
-#define CMN_EVENT_HNS_SNH(_name, _event)				\
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_all, CMN_TYPE_HNS, _event, SEL_SN_HOME_SEL, 0), \
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_sn, CMN_TYPE_HNS, _event, SEL_SN_HOME_SEL, 1), \
-	CMN_EVENT_ATTR(CMN_ANY, hns_##_name##_home, CMN_TYPE_HNS, _event, SEL_SN_HOME_SEL, 2)
+#define CMN_EVENT_HNSR0_CLS( _name, _event)				\
+	CMN_EVENT_HN_CLS(CMN700 | CMNS3R01, hns_##_name, CMN_TYPE_HNS, _event)
+#define CMN_EVENT_HNS_SNT(_model, _name, _event)			\
+	CMN_EVENT_HN_SNT(_model, hns_##_name, CMN_TYPE_HNS, _event)
+#define CMN_EVENT_HNS_SNH(_model, _name, _event)			\
+	_CMN_EVENT_HNS(_model, _name##_all, _event, SEL_SN_HOME_SEL, 0), \
+	_CMN_EVENT_HNS(_model, _name##_sn, _event, SEL_SN_HOME_SEL, 1),	\
+	_CMN_EVENT_HNS(_model, _name##_home, _event, SEL_SN_HOME_SEL, 2)
+#define CMN_EVENT_HNS_VC(_name, _event)					\
+	CMN_EVENT_HNSR0(_name, _event),					\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_vc0, _event, SEL_SNP_VC_SEL, 0), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_vc1, _event, SEL_SNP_VC_SEL, 1), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_vc2, _event, SEL_SNP_VC_SEL, 2)
+#define CMN_EVENT_HNS_ENHBT(_name, _event)				\
+	_CMN_EVENT_HNS_HBT(CMNS3R2, _name, _event, SEL_ENHANCED_HBT_LBT_SEL), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rnf, _event, SEL_ENHANCED_HBT_LBT_SEL, 3), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rni, _event, SEL_ENHANCED_HBT_LBT_SEL, 4), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccglcn, _event, SEL_ENHANCED_HBT_LBT_SEL, 5), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccgrn, _event, SEL_ENHANCED_HBT_LBT_SEL, 6)
+#define CMN_EVENT_HNS_EVICT(_model, _name, _event)			\
+	_CMN_EVENT_HNS_HBT2(_model, _name##_all, _event, SEL_EVICT_STATE_SEL, 0), \
+	_CMN_EVENT_HNS_HBT2(_model, _name##_eu, _event, SEL_EVICT_STATE_SEL, 1), \
+	_CMN_EVENT_HNS_HBT2(_model, _name##_en, _event, SEL_EVICT_STATE_SEL, 2), \
+	_CMN_EVENT_HNS_HBT2(_model, _name##_su, _event, SEL_EVICT_STATE_SEL, 3), \
+	_CMN_EVENT_HNS_HBT2(_model, _name##_sn, _event, SEL_EVICT_STATE_SEL, 4), \
+	_CMN_EVENT_HNS_HBT2(_model, _name##_mu, _event, SEL_EVICT_STATE_SEL, 5), \
+	_CMN_EVENT_HNS_HBT2(_model, _name##_mn, _event, SEL_EVICT_STATE_SEL, 6)
+
+#define CMN_EVENT_HNSR0_HBT(_name, _event)				\
+	_CMN_EVENT_HNS_HBT(CMN700 | CMNS3R01, _name, _event, SEL_HBT_LBT_SEL)
+#define CMN_EVENT_HNS_R2SNH(_name, _event)				\
+	CMN_EVENT_HNSR0(_name, _event),					\
+	CMN_EVENT_HNS_SNH(CMNS3R2, _name, _event)
+#define CMN_EVENT_HNS_R2HBT(_name, _event)				\
+	CMN_EVENT_HNSR0(_name, _event),					\
+	_CMN_EVENT_HNS_HBT(CMNS3R2, _name, _event, SEL_HBT_LBT_SEL)
+#define CMN_EVENT_HNS_HBT_ENHBT(_name, _event)				\
+	CMN_EVENT_HNSR0_HBT(_name, _event),				\
+	CMN_EVENT_HNS_ENHBT(_name, _event)
+#define CMN_EVENT_HNS_HBT_OCC(_name, _event)				\
+	CMN_EVENT_HNSR0_HBT(_name, _event),				\
+	CMN_EVENT_HNS_OCC(CMNS3R2, _name, _event)
+#define CMN_EVENT_HNS_HBT_EVICT(_name, _event)				\
+	CMN_EVENT_HNSR0_HBT(_name, _event),				\
+	CMN_EVENT_HNS_EVICT(CMNS3R2, _name, _event)
 
 #define _CMN_EVENT_XP_MESH(_name, _event)			\
 	__CMN_EVENT_XP(e_##_name, (_event) | (0 << 2)),		\
@@ -1288,65 +1388,72 @@ static struct attribute *arm_cmn_event_attrs[] = {
 	CMN_EVENT_CCLA(pfwd_sndr_stalls_static_crd,	0x2a),
 	CMN_EVENT_CCLA(pfwd_sndr_stalls_dynmaic_crd,	0x2b),
 
-	CMN_EVENT_HNS_HBT(cache_miss,			0x01),
-	CMN_EVENT_HNS_HBT(slc_sf_cache_access,		0x02),
-	CMN_EVENT_HNS_HBT(cache_fill,			0x03),
-	CMN_EVENT_HNS_HBT(pocq_retry,			0x04),
-	CMN_EVENT_HNS_HBT(pocq_reqs_recvd,		0x05),
-	CMN_EVENT_HNS_HBT(sf_hit,			0x06),
-	CMN_EVENT_HNS_HBT(sf_evictions,			0x07),
-	CMN_EVENT_HNS(dir_snoops_sent,			0x08),
-	CMN_EVENT_HNS(brd_snoops_sent,			0x09),
-	CMN_EVENT_HNS_HBT(slc_eviction,			0x0a),
-	CMN_EVENT_HNS_HBT(slc_fill_invalid_way,		0x0b),
-	CMN_EVENT_HNS(mc_retries_local,			0x0c),
-	CMN_EVENT_HNS_SNH(mc_reqs_local,		0x0d),
+	CMN_EVENT_HNS_HBT_ENHBT(cache_miss,		0x01),
+	CMN_EVENT_HNS_HBT_ENHBT(slc_sf_cache_access,	0x02),
+	CMN_EVENT_HNS_HBT_ENHBT(cache_fill,		0x03),
+	CMN_EVENT_HNS_HBT_OCC(pocq_retry,		0x04),
+	CMN_EVENT_HNS_HBT_OCC(pocq_reqs_recvd,		0x05),
+	CMN_EVENT_HNS_HBT_ENHBT(sf_hit,			0x06),
+	CMN_EVENT_HNS_HBT_EVICT(sf_evictions,		0x07),
+	CMN_EVENT_HNS_VC(dir_snoops_sent,		0x08),
+	CMN_EVENT_HNS_VC(brd_snoops_sent,		0x09),
+	CMN_EVENT_HNS_HBT_EVICT(slc_eviction,		0x0a),
+	CMN_EVENT_HNS_HBT_ENHBT(slc_fill_invalid_way,	0x0b),
+	CMN_EVENT_HNS_R2SNH(mc_retries_local,		0x0c),
+	CMN_EVENT_HNS_SNH(CMN_ANY, mc_reqs_local,	0x0d),
 	CMN_EVENT_HNS(qos_hh_retry,			0x0e),
-	CMN_EVENT_HNS_OCC(qos_pocq_occupancy,		0x0f),
-	CMN_EVENT_HNS(pocq_addrhaz,			0x10),
-	CMN_EVENT_HNS(pocq_atomic_addrhaz,		0x11),
-	CMN_EVENT_HNS(ld_st_swp_adq_full,		0x12),
-	CMN_EVENT_HNS(cmp_adq_full,			0x13),
+	CMN_EVENT_HNS_OCC(CMN_ANY, qos_pocq_occupancy,	0x0f),
+	CMN_EVENT_HNS_HBT_ENHBT(pocq_addrhaz,		0x10),
+	CMN_EVENT_HNS_HBT_ENHBT(pocq_atomic_addrhaz,	0x11),
+	CMN_EVENT_HNSR0(ld_st_swp_adq_full,		0x12),
+	CMN_EVENT_HNSR0(cmp_adq_full,			0x13),
 	CMN_EVENT_HNS(txdat_stall,			0x14),
 	CMN_EVENT_HNS(txrsp_stall,			0x15),
-	CMN_EVENT_HNS(seq_full,				0x16),
+	CMN_EVENT_HNSR0(seq_full,			0x16),
 	CMN_EVENT_HNS(seq_hit,				0x17),
-	CMN_EVENT_HNS(snp_sent,				0x18),
-	CMN_EVENT_HNS(sfbi_dir_snp_sent,		0x19),
-	CMN_EVENT_HNS(sfbi_brd_snp_sent,		0x1a),
+	CMN_EVENT_HNS_VC(snp_sent,			0x18),
+	CMN_EVENT_HNS_VC(sfbi_dir_snp_sent,		0x19),
+	CMN_EVENT_HNS_VC(sfbi_brd_snp_sent,		0x1a),
 	CMN_EVENT_HNS(intv_dirty,			0x1c),
-	CMN_EVENT_HNS(stash_snp_sent,			0x1d),
-	CMN_EVENT_HNS(stash_data_pull,			0x1e),
-	CMN_EVENT_HNS(snp_fwded,			0x1f),
-	CMN_EVENT_HNS(atomic_fwd,			0x20),
+	CMN_EVENT_HNSR0(stash_snp_sent,			0x1d),
+	CMN_EVENT_HNSR0(stash_data_pull,		0x1e),
+	CMN_EVENT_HNS_VC(snp_fwded,			0x1f),
+	CMN_EVENT_HNSR0(atomic_fwd,			0x20),
 	CMN_EVENT_HNS(mpam_hardlim,			0x21),
 	CMN_EVENT_HNS(mpam_softlim,			0x22),
-	CMN_EVENT_HNS(snp_sent_cluster,			0x23),
-	CMN_EVENT_HNS(sf_imprecise_evict,		0x24),
+	CMN_EVENT_HNS_VC(snp_sent_cluster,		0x23),
+	CMN_EVENT_HNS_R2HBT(sf_imprecise_evict,		0x24),
 	CMN_EVENT_HNS(sf_evict_shared_line,		0x25),
 	CMN_EVENT_HNS_CLS(pocq_class_occup,		0x26),
 	CMN_EVENT_HNS_CLS(pocq_class_retry,		0x27),
 	CMN_EVENT_HNS_CLS(class_mc_reqs_local,		0x28),
-	CMN_EVENT_HNS_CLS(class_cgnt_cmin,		0x29),
-	CMN_EVENT_HNS_SNT(sn_throttle,			0x2a),
-	CMN_EVENT_HNS_SNT(sn_throttle_min,		0x2b),
+	CMN_EVENT_HNSR0_CLS(class_cgnt_cmin,		0x29),
+	CMN_EVENT_HNS_SNT(CMN_ANY, sn_throttle,		0x2a),
+	CMN_EVENT_HNS_SNT(CMN_ANY, sn_throttle_min,	0x2b),
 	CMN_EVENT_HNS(sf_precise_to_imprecise,		0x2c),
 	CMN_EVENT_HNS(snp_intv_cln,			0x2d),
 	CMN_EVENT_HNS(nc_excl,				0x2e),
-	CMN_EVENT_HNS(excl_mon_ovfl,			0x2f),
+	CMN_EVENT_HNSR0(excl_mon_ovfl,			0x2f),
 	CMN_EVENT_HNS(snp_req_recvd,			0x30),
 	CMN_EVENT_HNS(snp_req_byp_pocq,			0x31),
 	CMN_EVENT_HNS(dir_ccgha_snp_sent,		0x32),
 	CMN_EVENT_HNS(brd_ccgha_snp_sent,		0x33),
-	CMN_EVENT_HNS(ccgha_snp_stall,			0x34),
+	CMN_EVENT_HNSR0(ccgha_snp_stall,		0x34),
 	CMN_EVENT_HNS(lbt_req_hardlim,			0x35),
 	CMN_EVENT_HNS(hbt_req_hardlim,			0x36),
 	CMN_EVENT_HNS(sf_reupdate,			0x37),
-	CMN_EVENT_HNS(excl_sf_imprecise,		0x38),
+	CMN_EVENT_HNS_R2HBT(excl_sf_imprecise,		0x38),
 	CMN_EVENT_HNS(snp_pocq_addrhaz,			0x39),
-	CMN_EVENT_HNS(mc_retries_remote,		0x3a),
-	CMN_EVENT_HNS_SNH(mc_reqs_remote,		0x3b),
+	CMN_EVENT_HNS_R2SNH(mc_retries_remote,		0x3a),
+	CMN_EVENT_HNS_SNH(CMN_ANY, mc_reqs_remote,	0x3b),
 	CMN_EVENT_HNS_CLS(class_mc_reqs_remote,		0x3c),
+	CMN_EVENT_HNS_ENHBT(readonce_hazard_detected,	0x3d),
+	CMN_EVENT_HNS_ENHBT(readonce_fwd_data_completed, 0x3e),
+	CMN_EVENT_HNS_SNT(CMNS3R2, cbusy00,		0x40),
+	CMN_EVENT_HNS_SNT(CMNS3R2, cbusy01, 		0x41),
+	CMN_EVENT_HNS_SNT(CMNS3R2, cbusy10,		0x42),
+	CMN_EVENT_HNS_SNT(CMNS3R2, cbusy11,		0x43),
+	CMN_EVENT_HNS_ENHBT(ro_rnsd_new_alloc_hint,	0x44),
 
 	NULL
 };
@@ -2431,21 +2538,33 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 	/*
 	 * With the device isolation feature, if firmware has neglected to enable
 	 * an XP port then we risk locking up if we try to access anything behind
-	 * it; however we also have no way to tell from Non-Secure whether any
-	 * given port is disabled or not, so the only way to win is not to play...
+	 * it; however prior to CMN S3 r2p0 we also have no way to tell from
+	 * Non-Secure whether any given port is disabled or not, so in that case
+	 * the only way to win is not to play...
 	 */
 	reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
-	if (reg & CMN_INFO_DEVICE_ISO_ENABLE) {
+	if (reg & CMN_INFO_DEVICE_ISO_ENABLE && model == CMNS3R01) {
 		dev_err(cmn->dev, "Device isolation enabled, not continuing due to risk of lockup\n");
 		return -ENODEV;
 	}
-	cmn->multi_dtm = reg & CMN_INFO_MULTIPLE_DTM_EN;
-	cmn->rsp_vc_num = FIELD_GET(CMN_INFO_RSP_VC_NUM, reg);
-	cmn->dat_vc_num = FIELD_GET(CMN_INFO_DAT_VC_NUM, reg);
+	if (model < CMNS3R2) {
+		cmn->multi_dtm = reg & CMN_INFO_MULTIPLE_DTM_EN;
+		cmn->rsp_vc_num = FIELD_GET(CMN_INFO_RSP_VC_NUM, reg);
+		cmn->dat_vc_num = FIELD_GET(CMN_INFO_DAT_VC_NUM, reg);
+	} else {
+		cmn->multi_dtm = reg & CMN_S3_R2_MULTIPLE_DTM_EN;
+	}
 
 	reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL_1);
-	cmn->snp_vc_num = FIELD_GET(CMN_INFO_SNP_VC_NUM, reg);
-	cmn->req_vc_num = FIELD_GET(CMN_INFO_REQ_VC_NUM, reg);
+	if (model < CMNS3R2) {
+		cmn->snp_vc_num = FIELD_GET(CMN_INFO_SNP_VC_NUM, reg);
+		cmn->req_vc_num = FIELD_GET(CMN_INFO_REQ_VC_NUM, reg);
+	} else {
+		cmn->rsp_vc_num = FIELD_GET(CMN_S3_R2_RSP_VC_NUM, reg);
+		cmn->dat_vc_num = FIELD_GET(CMN_S3_R2_DAT_VC_NUM, reg);
+		cmn->snp_vc_num = FIELD_GET(CMN_S3_R2_SNP_VC_NUM, reg);
+		cmn->req_vc_num = FIELD_GET(CMN_S3_R2_REQ_VC_NUM, reg);
+	}
 
 	reg = readq_relaxed(cfg_region + CMN_CHILD_INFO);
 	child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg);
@@ -2545,15 +2664,12 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 			reg = readq_relaxed(xp_region + child_poff + j * 8);
 			/*
 			 * Don't even try to touch anything external, since in general
-			 * we haven't a clue how to power up arbitrary CHI requesters.
-			 * As of CMN-600r1 these could only be RN-SAMs or CXLAs,
-			 * neither of which have any PMU events anyway.
-			 * (Actually, CXLAs do seem to have grown some events in r1p2,
-			 * but they don't go to regular XP DTMs, and they depend on
-			 * secure configuration which we can't easily deal with)
+			 * we haven't a clue how to power up arbitrary CHI requesters,
+			 * and none of them have standard PMU events anyway. Isolated
+			 * nodes effectively just do not exist at all from our PoV.
 			 */
-			if (reg & CMN_CHILD_NODE_EXTERNAL) {
-				dev_dbg(cmn->dev, "ignoring external node %llx\n", reg);
+			if (reg & (CMN_CHILD_NODE_EXTERNAL | CMN_CHILD_NODE_ISOLATED)) {
+				dev_dbg(cmn->dev, "ignoring external/isolated node %llx\n", reg);
 				continue;
 			}
 			/*
Signature · 1 line
2.54.0.dirty
#11 lore raw

On Tue, Jun 30, 2026 at 04:19:20PM +0100, Robin Murphy wrote:

[...]

drivers/perf/arm-cmn.c static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, · line 964

Quoted 11 lines
drivers/perf/arm-cmn.c +6 -1
@@ -913,7 +956,12 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
 	CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 3), \
 	CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 4), \
 	CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 5), \
-	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6)
+	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 9), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 10), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 11), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 12), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 13)

If these CMN_EVENT_ATTR(CMNS3R2, ...) entries are appended to CMN_EVENT_HN_SNT(), they will extend the CMNS3R2 attributes for other models as well. For example:

CMN_EVENT_HNF_SNT(CMN700, sn_throttle, 0x2a),

This would also add the CMNS3R2 attributes to CMN700. Is this intended?

drivers/perf/arm-cmn.c static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, · line 994

Quoted 9 lines
drivers/perf/arm-cmn.c +9 -0
+#define CMN_EVENT_HNS_OCC(_model, _name, _event)			\
+	CMN_EVENT_HN_OCC(_model, hns_##_name, CMN_TYPE_HNS, _event),	\
+	_CMN_EVENT_HNS(_model, _name##_rxsnp, _event, SEL_OCCUP1_ID, 5), \
+	_CMN_EVENT_HNS(_model, _name##_lbt, _event, SEL_OCCUP1_ID, 6),	\
+	_CMN_EVENT_HNS(_model, _name##_hbt, _event, SEL_OCCUP1_ID, 7),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rnf, _event, SEL_OCCUP1_ID, 8),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rni, _event, SEL_OCCUP1_ID, 9),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccglcn, _event, SEL_OCCUP1_ID, 10), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccgrn, _event, SEL_OCCUP1_ID, 11)

I have a similar question here. My impression is that this is mainly for the convenience of appending CMNS3R2 specific attributes, but it doesn't necessarily mean those attributes should be added for every model.

drivers/perf/arm-cmn.c static struct attribute *arm_cmn_event_attrs[] = { · line 1389

Quoted 3 lines
drivers/perf/arm-cmn.c +1 -0
@@ -1288,65 +1388,72 @@ static struct attribute *arm_cmn_event_attrs[] = {
 
+	CMN_EVENT_HNS_HBT_ENHBT(cache_miss,		0x01),

I manually extend the macro and got the result:

  CMN_EVENT_ATTR(CMN700 | CMNS3R01, hns_cache_miss_all, CMN_TYPE_HNS, 0x1, SEL_HBT_LBT_SEL, 0),
  CMN_EVENT_ATTR(CMN700 | CMNS3R01, hns_cache_miss_hbt, CMN_TYPE_HNS, 0x1, SEL_HBT_LBT_SEL, 1),
  CMN_EVENT_ATTR(CMN700 | CMNS3R01, hns_cache_miss_lbt, CMN_TYPE_HNS, 0x1, SEL_HBT_LBT_SEL, 2),

CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_all, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 0), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_hbt, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 1), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_lbt, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 2), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_rnf, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 3), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_rni, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 4), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_ccglcn, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 5), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_ccgrn, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 6),

CMNS3R2 has added items like rnf/rni/ccglcn/ccgrn, not sure if this is purposed or not ...

drivers/perf/arm-cmn.c static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) · line 2546

Quoted 13 lines
drivers/perf/arm-cmn.c +4 -3
@@ -2431,21 +2538,33 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
 	/*
 	 * With the device isolation feature, if firmware has neglected to enable
 	 * an XP port then we risk locking up if we try to access anything behind
-	 * it; however we also have no way to tell from Non-Secure whether any
-	 * given port is disabled or not, so the only way to win is not to play...
+	 * it; however prior to CMN S3 r2p0 we also have no way to tell from
+	 * Non-Secure whether any given port is disabled or not, so in that case
+	 * the only way to win is not to play...
 	 */
 	reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
-	if (reg & CMN_INFO_DEVICE_ISO_ENABLE) {
+	if (reg & CMN_INFO_DEVICE_ISO_ENABLE && model == CMNS3R01) {

As the comment claims "prior to CMN S3 r2p0", would here be:

  if (reg & CMN_INFO_DEVICE_ISO_ENABLE && model < CMNS3R2) {

drivers/perf/arm-cmn.c static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) · line 2549

Quoted 3 lines
drivers/perf/arm-cmn.c +0 -0
 		dev_err(cmn->dev, "Device isolation enabled, not continuing due to risk of lockup\n");
 		return -ENODEV;
 	}

Thanks,
Leo

author maintainer
#12 lore raw
Quoted 20 lines · Leo Yan

On Tue, Jun 30, 2026 at 04:19:20PM +0100, Robin Murphy wrote:

[...]

drivers/perf/arm-cmn.c static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, · line 964

Quoted 11 lines
drivers/perf/arm-cmn.c +6 -1
@@ -913,7 +956,12 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
  	CMN_EVENT_ATTR(_model, _name##_group1_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 3), \
  	CMN_EVENT_ATTR(_model, _name##_group1_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 4), \
  	CMN_EVENT_ATTR(_model, _name##_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 5), \
-	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6)
+	CMN_EVENT_ATTR(_model, _name##_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 6), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 9), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_ccg_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 10), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_read, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 11), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt_write, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 12), \
+	CMN_EVENT_ATTR(CMNS3R2, _name##_lbt, _type, _event, SEL_CBUSY_SNTHROTTLE_SEL, 13)

If these CMN_EVENT_ATTR(CMNS3R2, ...) entries are appended to CMN_EVENT_HN_SNT(), they will extend the CMNS3R2 attributes for other models as well. For example:

Quoted 3 lines
   CMN_EVENT_HNF_SNT(CMN700, sn_throttle, 0x2a),

This would also add the CMNS3R2 attributes to CMN700. Is this intended?

Yes, hence why the new values hard-code CMNS3R2 at this level rather than the incoming _model argument, so the additional attributes should still end up being hidden by arm_cmn_event_attr_is_visible() on anything else (indeed that probably won't scale well for the *next* CMN model, but for now that's future Robin's problem...)

As for the other angle, the documentation is not at all clear what applies if an instance of S3 r2 itself were to have HN-Fs rather than HN-Ses, nor whether that's even possible, so frankly I'm choosing not to care much about what we end up doing in that case. At worst we may advertise some aliases that aren't meaningful, and/or omit some that are. I can't say how wrong that might be since I don't know what's right.

Quoted 5 lines
Quoted 5 lines
drivers/perf/arm-cmn.c +5 -0
+#define CMN_EVENT_HNS_OCC(_model, _name, _event)			\
+	CMN_EVENT_HN_OCC(_model, hns_##_name, CMN_TYPE_HNS, _event),	\
+	_CMN_EVENT_HNS(_model, _name##_rxsnp, _event, SEL_OCCUP1_ID, 5), \
+	_CMN_EVENT_HNS(_model, _name##_lbt, _event, SEL_OCCUP1_ID, 6),	\
+	_CMN_EVENT_HNS(_model, _name##_hbt, _event, SEL_OCCUP1_ID, 7),	\
Quoted 8 lines

drivers/perf/arm-cmn.c static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, · line 994

Quoted 4 lines
drivers/perf/arm-cmn.c +4 -0
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rnf, _event, SEL_OCCUP1_ID, 8),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_rni, _event, SEL_OCCUP1_ID, 9),	\
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccglcn, _event, SEL_OCCUP1_ID, 10), \
+	_CMN_EVENT_HNS(CMNS3R2, _name##_ccgrn, _event, SEL_OCCUP1_ID, 11)

I have a similar question here. My impression is that this is mainly for the convenience of appending CMNS3R2 specific attributes, but it doesn't necessarily mean those attributes should be added for every model.

Note that the design here is that each event alias encodes a bitmap of the models to which it is relevant - although with some simplification, since we don't need to explicitly exclude models where the whole node type could never be present anyway. So tht notion of "adding attributes to a model" doesn't really make sense - at most we're adding the new model to existing attributes in some places (mostly implicitly in the "bitmap tricks" enum values), but mostly adding new attributes only *for* the new model.

Quoted 17 lines

drivers/perf/arm-cmn.c static struct attribute *arm_cmn_event_attrs[] = { · line 1389

Quoted 3 lines
drivers/perf/arm-cmn.c +1 -0
@@ -1288,65 +1388,72 @@ static struct attribute *arm_cmn_event_attrs[] = {
 
+	CMN_EVENT_HNS_HBT_ENHBT(cache_miss,		0x01),

I manually extend the macro and got the result:

   CMN_EVENT_ATTR(CMN700 | CMNS3R01, hns_cache_miss_all, CMN_TYPE_HNS, 0x1, SEL_HBT_LBT_SEL, 0),
   CMN_EVENT_ATTR(CMN700 | CMNS3R01, hns_cache_miss_hbt, CMN_TYPE_HNS, 0x1, SEL_HBT_LBT_SEL, 1),
   CMN_EVENT_ATTR(CMN700 | CMNS3R01, hns_cache_miss_lbt, CMN_TYPE_HNS, 0x1, SEL_HBT_LBT_SEL, 2),

CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_all, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 0), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_hbt, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 1), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_lbt, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 2), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_rnf, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 3), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_rni, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 4), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_ccglcn, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 5), CMN_EVENT_ATTR(CMNS3R2, hns_cache_miss_ccgrn, CMN_TYPE_HNS, 0x1, SEL_ENHANCED_HBT_LBT_SEL, 6),

Quoted 2 lines

CMNS3R2 has added items like rnf/rni/ccglcn/ccgrn, not sure if this is purposed or not ...

Indeed that is correct - in this case we can't encode (e.g.) a single "hns_cache_miss_hbt" attribute even though the name, node type, event code, and filter value are all common, since S3 r2 went and changed the filter itself. Thus we define two sets of aliases, one encoding the original selector and its values, and one for the new variant, but only one set or the other will be exposed at runtime based on the model match. (And if the model bitmaps were to overlap such that we did try to expose both, then we'd blow up with a sysfs collision from trying to create two attributes with the same name.)

Quoted 16 lines

drivers/perf/arm-cmn.c static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) · line 2546

Quoted 13 lines
drivers/perf/arm-cmn.c +4 -3
@@ -2431,21 +2538,33 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
  	/*
  	 * With the device isolation feature, if firmware has neglected to enable
  	 * an XP port then we risk locking up if we try to access anything behind
-	 * it; however we also have no way to tell from Non-Secure whether any
-	 * given port is disabled or not, so the only way to win is not to play...
+	 * it; however prior to CMN S3 r2p0 we also have no way to tell from
+	 * Non-Secure whether any given port is disabled or not, so in that case
+	 * the only way to win is not to play...
  	 */
  	reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
-	if (reg & CMN_INFO_DEVICE_ISO_ENABLE) {
+	if (reg & CMN_INFO_DEVICE_ISO_ENABLE && model == CMNS3R01) {

As the comment claims "prior to CMN S3 r2p0", would here be:

Quoted 1 line
   if (reg & CMN_INFO_DEVICE_ISO_ENABLE && model < CMNS3R2) {

Although bit 44 of INFO_GLOBAL should be RES0 on CMN-700 and older versions, it only carries the meaning of DEVICE_ISO_ENABLE from CMN S3 r0 onwards, so while we could get away with being lazy before, once we have *some* check then it might as well be the most correctly-specific one.

Thanks,
Robin.

Quoted 7 lines

drivers/perf/arm-cmn.c static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) · line 2549

Quoted 4 lines
drivers/perf/arm-cmn.c +0 -0
  		dev_err(cmn->dev, "Device isolation enabled, not continuing due to risk of lockup\n");
  		return -ENODEV;
  	}

Thanks,
Leo