[PATCH] drivers/perf/arm_smmuv3_pmu: Support for PMU Snapshot
Tarun Sahu
tarunsahu at google.com
Tue Apr 23 06:06:47 PDT 2024
This patch adds support to capture snapshot of all event counters for
SMMUV3 PMU counters. The following functionalities are added:
1. Manually capture the snapshot
2. Capture the snapshot when an event counter overflows.
Test:
To manually capture the snapshot:
$ echo 1 > /sys/devices/smmuv3_pmcg_<device_id>/capture
'OR'
Configure an event for capturing the snapshot when it overflows
$ perf stat -e /smmuv3_pmcg_ff88840/transaction,filter_enable=1,
overflow_capture=1,filter_span=1,filter_stream_id=0x42/ -a
netperf
To read the snapshot:
$ cat /sys/devices/smmuv3_pmcg_<device_id>/capture
Signed-off-by: Tarun Sahu <tarunsahu at google.com>
---
drivers/perf/arm_smmuv3_pmu.c | 77 +++++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/drivers/perf/arm_smmuv3_pmu.c b/drivers/perf/arm_smmuv3_pmu.c
index 6303b82566f9..6e2baa752ccc 100644
--- a/drivers/perf/arm_smmuv3_pmu.c
+++ b/drivers/perf/arm_smmuv3_pmu.c
@@ -15,6 +15,7 @@
* filter_enable - 0 = no filtering, 1 = filtering enabled
* filter_span - 0 = exact match, 1 = pattern match
* filter_stream_id - pattern to filter against
+ * overflow_capture - capture all events when this event overflows
*
* To match a partial StreamID where the X most-significant bits must match
* but the Y least-significant bits might differ, STREAMID is programmed
@@ -56,6 +57,8 @@
#define SMMU_PMCG_EVCNTR0 0x0
#define SMMU_PMCG_EVCNTR(n, stride) (SMMU_PMCG_EVCNTR0 + (n) * (stride))
+#define SMMU_PMCG_SVR0 0x0
+#define SMMU_PMCG_SVR(n, stride) (SMMU_PMCG_SVR0 + (n) * (stride))
#define SMMU_PMCG_EVTYPER0 0x400
#define SMMU_PMCG_EVTYPER(n) (SMMU_PMCG_EVTYPER0 + (n) * 4)
#define SMMU_PMCG_SID_SPAN_SHIFT 29
@@ -67,8 +70,11 @@
#define SMMU_PMCG_INTENCLR0 0xC60
#define SMMU_PMCG_OVSCLR0 0xC80
#define SMMU_PMCG_OVSSET0 0xCC0
+#define SMMU_PMCG_CAPR 0xD88
+#define SMMU_PMCG_CAPR_CAPTURE BIT(0)
#define SMMU_PMCG_CFGR 0xE00
#define SMMU_PMCG_CFGR_SID_FILTER_TYPE BIT(23)
+#define SMMU_PMCG_CFGR_CAPTURE BIT(22)
#define SMMU_PMCG_CFGR_MSI BIT(21)
#define SMMU_PMCG_CFGR_RELOC_CTRS BIT(20)
#define SMMU_PMCG_CFGR_SIZE GENMASK(13, 8)
@@ -135,6 +141,7 @@ struct smmu_pmu {
u32 options;
u32 iidr;
bool global_filter;
+ bool capture;
};
#define to_smmu_pmu(p) (container_of(p, struct smmu_pmu, pmu))
@@ -147,6 +154,7 @@ struct smmu_pmu {
} \
SMMU_PMU_EVENT_ATTR_EXTRACTOR(event, config, 0, 15);
+SMMU_PMU_EVENT_ATTR_EXTRACTOR(overflow_capture, config, 31, 31);
SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_stream_id, config1, 0, 31);
SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_span, config1, 32, 32);
SMMU_PMU_EVENT_ATTR_EXTRACTOR(filter_enable, config1, 33, 33);
@@ -219,6 +227,18 @@ static inline u64 smmu_pmu_counter_get_value(struct smmu_pmu *smmu_pmu, u32 idx)
return value;
}
+static inline u64 smmu_pmu_svr_get_value(struct smmu_pmu *smmu_pmu, u32 idx)
+{
+ u64 value;
+
+ if (smmu_pmu->counter_mask & BIT(32))
+ value = readq(smmu_pmu->reloc_base + SMMU_PMCG_SVR(idx, 8));
+ else
+ value = readl(smmu_pmu->reloc_base + SMMU_PMCG_SVR(idx, 4));
+
+ return value;
+}
+
static inline void smmu_pmu_counter_enable(struct smmu_pmu *smmu_pmu, u32 idx)
{
writeq(BIT(idx), smmu_pmu->reg_base + SMMU_PMCG_CNTENSET0);
@@ -306,6 +326,7 @@ static void smmu_pmu_set_event_filter(struct perf_event *event,
u32 evtyper;
evtyper = get_event(event) | span << SMMU_PMCG_SID_SPAN_SHIFT;
+ evtyper |= get_overflow_capture(event);
smmu_pmu_set_evtyper(smmu_pmu, idx, evtyper);
smmu_pmu_set_smr(smmu_pmu, idx, sid);
}
@@ -549,6 +570,54 @@ static const struct attribute_group smmu_pmu_cpumask_group = {
.attrs = smmu_pmu_cpumask_attrs,
};
+static ssize_t smmu_pmu_capture_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev));
+ u32 capture, i;
+
+ if (!smmu_pmu->capture)
+ return -EPERM;
+ capture = readl_relaxed(smmu_pmu->reg_base + SMMU_PMCG_CAPR) &
+ SMMU_PMCG_CAPR_CAPTURE;
+ if (capture == 0) {
+ for (i = 0; i < SMMU_PMCG_MAX_COUNTERS; i++) {
+ capture = sprintf(buf, "%s%d %llu\n", buf, i,
+ smmu_pmu_svr_get_value(smmu_pmu, i));
+ }
+ }
+ return capture;
+}
+
+static ssize_t smmu_pmu_capture_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct smmu_pmu *smmu_pmu = to_smmu_pmu(dev_get_drvdata(dev));
+ unsigned long val;
+
+ if (!smmu_pmu->capture)
+ return -EPERM;
+ if (kstrtoul(buf, 0, &val) == 0 && val == 1) {
+ writeq_relaxed(1, smmu_pmu->reg_base + SMMU_PMCG_CAPR);
+ return count;
+ }
+ return -EINVAL;
+}
+
+static struct device_attribute smmu_pmu_capture_attr =
+ __ATTR(capture, 0644, smmu_pmu_capture_show, smmu_pmu_capture_store);
+
+static struct attribute *smmu_pmu_capture_attrs[] = {
+ &smmu_pmu_capture_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smmu_pmu_capture_group = {
+ .attrs = smmu_pmu_capture_attrs,
+};
+
/* Events */
static ssize_t smmu_pmu_event_show(struct device *dev,
@@ -633,12 +702,14 @@ static const struct attribute_group smmu_pmu_identifier_group = {
/* Formats */
PMU_FORMAT_ATTR(event, "config:0-15");
+PMU_FORMAT_ATTR(overflow_capture, "config:31");
PMU_FORMAT_ATTR(filter_stream_id, "config1:0-31");
PMU_FORMAT_ATTR(filter_span, "config1:32");
PMU_FORMAT_ATTR(filter_enable, "config1:33");
static struct attribute *smmu_pmu_formats[] = {
&format_attr_event.attr,
+ &format_attr_overflow_capture.attr,
&format_attr_filter_stream_id.attr,
&format_attr_filter_span.attr,
&format_attr_filter_enable.attr,
@@ -651,6 +722,7 @@ static const struct attribute_group smmu_pmu_format_group = {
};
static const struct attribute_group *smmu_pmu_attr_grps[] = {
+ &smmu_pmu_capture_group,
&smmu_pmu_cpumask_group,
&smmu_pmu_events_group,
&smmu_pmu_format_group,
@@ -888,6 +960,11 @@ static int smmu_pmu_probe(struct platform_device *pdev)
smmu_pmu->reloc_base = smmu_pmu->reg_base;
}
+ if (cfgr & SMMU_PMCG_CFGR_CAPTURE)
+ smmu_pmu->capture = true;
+ else
+ smmu_pmu->capture = false;
+
irq = platform_get_irq_optional(pdev, 0);
if (irq > 0)
smmu_pmu->irq = irq;
--
2.44.0.769.g3c40516874-goog
More information about the linux-arm-kernel
mailing list