Message ID | 20190726082553.1083-1-qiangqing.zhang@nxp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [V3] perf: imx8_ddr_perf: add AXI ID filter support | expand |
On Fri, Jul 26, 2019 at 08:28:13AM +0000, Joakim Zhang wrote: > AXI filtering is used by CSV modes 0x41 and 0x42 to count reads or > writes with an ARID or AXID matching filter setting. Granularity is at > subsystem level. Implementation does not allow filtring between masters > within a subsystem. Filter is defined with 2 configuration registers. > > --AXI_ID defines AxID matching value > --AXI_MASKING defines which bits of AxID are meaningful for the matching > > When non-masked bits are matching corresponding AXI_ID bits then counter > is incremented. This filter allows counting read or write access from a > subsystem or multiple subsystems. > > Perf counter is incremented if AxID && AXI_MASKING == AXI_ID && AXI_MASKING > > AXI_ID and AXI_MASKING are mapped on DPCR1 register in performance counter. > > Read and write AXI ID filter should write same value to DPCR1 if want to > specify at the same time as this filter is shared between counters. > > e.g. > perf stat -a -e imx8_ddr0/axi-id-read,axi_id=0xMMMMDDDD/,imx8_ddr0/ > axi-id-write,axi_id=0xMMMMDDDD/cmd > MMMM: AXI_MASKING > DDDD: AXI_ID > > ChangeLog: > V1 -> V2: > * add error log if user specifies read/write AXI ID filter at > the same time. > * of_device_get_match_data() instead of of_match_device(), and > remove the check of return value. > V2 -> V3: > * move the AXI ID check to event_add(). > * add support for same value of axi_id. > > Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com> > --- > drivers/perf/fsl_imx8_ddr_perf.c | 50 ++++++++++++++++++++++++++++++-- > 1 file changed, 48 insertions(+), 2 deletions(-) > @@ -288,6 +307,24 @@ static int ddr_perf_event_add(struct perf_event *event, int flags) > int counter; > int cfg = event->attr.config; > > + if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) { > + if (event->attr.config == 0x41) > + pmu->axi_id_read = event->attr.config1; > + > + if (event->attr.config == 0x42) > + pmu->axi_id_write = event->attr.config1; > + > + if (pmu->axi_id_read && pmu->axi_id_write && > + (pmu->axi_id_read != pmu->axi_id_write)) { > + dev_err(pmu->dev, "axi-id-read/write event must have the same value of axi_id\n"); > + return -EINVAL; > + } else if (pmu->axi_id_read) { > + writel(pmu->axi_id_read, pmu->base + COUNTER_DPCR1); > + } else if (pmu->axi_id_write) { > + writel(pmu->axi_id_write, pmu->base + COUNTER_DPCR1); > + } > + } It is not appropriate to print an error here after allowing the user to configure the PMU in this way. If you must support this filtering, then: * In pmu::event_init, verify that all of the events in the group have requested the same filtering, and reject the group if not. * In event_add, only accept an event which matches the filtering requirements of any events which are currently installed. Do not print an error message. ... that way the perf core will periodically rotate conflicting events, as it would for any other PMU. It will keep track of how long each event is installed, and the userspace tools can report this. You should document this under Documentation/admin-guide/perf/, but the driver shouldn't print anything to dmesg for this case. Thanks, Mark.
diff --git a/drivers/perf/fsl_imx8_ddr_perf.c b/drivers/perf/fsl_imx8_ddr_perf.c index 63fe21600072..e7427aec8c3e 100644 --- a/drivers/perf/fsl_imx8_ddr_perf.c +++ b/drivers/perf/fsl_imx8_ddr_perf.c @@ -42,9 +42,22 @@ static DEFINE_IDA(ddr_ida); +/* DDR Perf hardware feature */ +#define DDR_CAP_AXI_ID_FILTER 0x1 /* support AXI ID filter */ + +struct fsl_ddr_devtype_data { + unsigned int quirks; /* quirks needed for different DDR Perf core */ +}; + +static const struct fsl_ddr_devtype_data imx8_devtype_data; + +static const struct fsl_ddr_devtype_data imx8m_devtype_data = { + .quirks = DDR_CAP_AXI_ID_FILTER, +}; + static const struct of_device_id imx_ddr_pmu_dt_ids[] = { - { .compatible = "fsl,imx8-ddr-pmu",}, - { .compatible = "fsl,imx8m-ddr-pmu",}, + { .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data}, + { .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data}, { /* sentinel */ } }; @@ -57,6 +70,8 @@ struct ddr_pmu { struct perf_event *events[NUM_COUNTERS]; int active_events; enum cpuhp_state cpuhp_state; + const struct fsl_ddr_devtype_data *devtype_data; + unsigned int axi_id_read, axi_id_write; int irq; int id; }; @@ -128,6 +143,8 @@ static struct attribute *ddr_perf_events_attrs[] = { IMX8_DDR_PMU_EVENT_ATTR(refresh, 0x37), IMX8_DDR_PMU_EVENT_ATTR(write, 0x38), IMX8_DDR_PMU_EVENT_ATTR(raw-hazard, 0x39), + IMX8_DDR_PMU_EVENT_ATTR(axi-id-read, 0x41), + IMX8_DDR_PMU_EVENT_ATTR(axi-id-write, 0x42), NULL, }; @@ -137,9 +154,11 @@ static struct attribute_group ddr_perf_events_attr_group = { }; PMU_FORMAT_ATTR(event, "config:0-7"); +PMU_FORMAT_ATTR(axi_id, "config1:0-31"); static struct attribute *ddr_perf_format_attrs[] = { &format_attr_event.attr, + &format_attr_axi_id.attr, NULL, }; @@ -288,6 +307,24 @@ static int ddr_perf_event_add(struct perf_event *event, int flags) int counter; int cfg = event->attr.config; + if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) { + if (event->attr.config == 0x41) + pmu->axi_id_read = event->attr.config1; + + if (event->attr.config == 0x42) + pmu->axi_id_write = event->attr.config1; + + if (pmu->axi_id_read && pmu->axi_id_write && + (pmu->axi_id_read != pmu->axi_id_write)) { + dev_err(pmu->dev, "axi-id-read/write event must have the same value of axi_id\n"); + return -EINVAL; + } else if (pmu->axi_id_read) { + writel(pmu->axi_id_read, pmu->base + COUNTER_DPCR1); + } else if (pmu->axi_id_write) { + writel(pmu->axi_id_write, pmu->base + COUNTER_DPCR1); + } + } + counter = ddr_perf_alloc_counter(pmu, cfg); if (counter < 0) { dev_dbg(pmu->dev, "There are not enough counters\n"); @@ -312,6 +349,11 @@ static void ddr_perf_event_stop(struct perf_event *event, int flags) struct hw_perf_event *hwc = &event->hw; int counter = hwc->idx; + if (pmu->devtype_data->quirks & DDR_CAP_AXI_ID_FILTER) { + pmu->axi_id_read = 0; + pmu->axi_id_write = 0; + } + ddr_perf_counter_enable(pmu, event->attr.config, counter, false); ddr_perf_event_update(event); @@ -445,6 +487,7 @@ static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node) static int ddr_perf_probe(struct platform_device *pdev) { + const struct fsl_ddr_devtype_data *data; struct ddr_pmu *pmu; struct device_node *np; void __iomem *base; @@ -472,6 +515,9 @@ static int ddr_perf_probe(struct platform_device *pdev) if (!name) return -ENOMEM; + data = of_device_get_match_data(&pdev->dev); + pmu->devtype_data = data; + pmu->cpu = raw_smp_processor_id(); ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, DDR_CPUHP_CB_NAME,
AXI filtering is used by CSV modes 0x41 and 0x42 to count reads or writes with an ARID or AXID matching filter setting. Granularity is at subsystem level. Implementation does not allow filtring between masters within a subsystem. Filter is defined with 2 configuration registers. --AXI_ID defines AxID matching value --AXI_MASKING defines which bits of AxID are meaningful for the matching When non-masked bits are matching corresponding AXI_ID bits then counter is incremented. This filter allows counting read or write access from a subsystem or multiple subsystems. Perf counter is incremented if AxID && AXI_MASKING == AXI_ID && AXI_MASKING AXI_ID and AXI_MASKING are mapped on DPCR1 register in performance counter. Read and write AXI ID filter should write same value to DPCR1 if want to specify at the same time as this filter is shared between counters. e.g. perf stat -a -e imx8_ddr0/axi-id-read,axi_id=0xMMMMDDDD/,imx8_ddr0/ axi-id-write,axi_id=0xMMMMDDDD/cmd MMMM: AXI_MASKING DDDD: AXI_ID ChangeLog: V1 -> V2: * add error log if user specifies read/write AXI ID filter at the same time. * of_device_get_match_data() instead of of_match_device(), and remove the check of return value. V2 -> V3: * move the AXI ID check to event_add(). * add support for same value of axi_id. Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com> --- drivers/perf/fsl_imx8_ddr_perf.c | 50 ++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-)