| From 4c1daba15c209b99d192f147fea3dade30f72ed2 Mon Sep 17 00:00:00 2001 |
| From: Robin Murphy <robin.murphy@arm.com> |
| Date: Tue, 8 Jun 2021 12:55:12 +0100 |
| Subject: perf/smmuv3: Don't trample existing events with global filter |
| |
| From: Robin Murphy <robin.murphy@arm.com> |
| |
| commit 4c1daba15c209b99d192f147fea3dade30f72ed2 upstream. |
| |
| With global filtering, we only allow an event to be scheduled if its |
| filter settings exactly match those of any existing events, therefore |
| it is pointless to reapply the filter in that case. Much worse, though, |
| is that in doing that we trample the event type of counter 0 if it's |
| already active, and never touch the appropriate PMEVTYPERn so the new |
| event is likely not counting the right thing either. Don't do that. |
| |
| CC: stable@vger.kernel.org |
| Signed-off-by: Robin Murphy <robin.murphy@arm.com> |
| Link: https://lore.kernel.org/r/32c80c0e46237f49ad8da0c9f8864e13c4a803aa.1623153312.git.robin.murphy@arm.com |
| Signed-off-by: Will Deacon <will@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/perf/arm_smmuv3_pmu.c | 18 ++++++++++-------- |
| 1 file changed, 10 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/perf/arm_smmuv3_pmu.c |
| +++ b/drivers/perf/arm_smmuv3_pmu.c |
| @@ -275,7 +275,7 @@ static int smmu_pmu_apply_event_filter(s |
| struct perf_event *event, int idx) |
| { |
| u32 span, sid; |
| - unsigned int num_ctrs = smmu_pmu->num_counters; |
| + unsigned int cur_idx, num_ctrs = smmu_pmu->num_counters; |
| bool filter_en = !!get_filter_enable(event); |
| |
| span = filter_en ? get_filter_span(event) : |
| @@ -283,17 +283,19 @@ static int smmu_pmu_apply_event_filter(s |
| sid = filter_en ? get_filter_stream_id(event) : |
| SMMU_PMCG_DEFAULT_FILTER_SID; |
| |
| - /* Support individual filter settings */ |
| - if (!smmu_pmu->global_filter) { |
| + cur_idx = find_first_bit(smmu_pmu->used_counters, num_ctrs); |
| + /* |
| + * Per-counter filtering, or scheduling the first globally-filtered |
| + * event into an empty PMU so idx == 0 and it works out equivalent. |
| + */ |
| + if (!smmu_pmu->global_filter || cur_idx == num_ctrs) { |
| smmu_pmu_set_event_filter(event, idx, span, sid); |
| return 0; |
| } |
| |
| - /* Requested settings same as current global settings*/ |
| - idx = find_first_bit(smmu_pmu->used_counters, num_ctrs); |
| - if (idx == num_ctrs || |
| - smmu_pmu_check_global_filter(smmu_pmu->events[idx], event)) { |
| - smmu_pmu_set_event_filter(event, 0, span, sid); |
| + /* Otherwise, must match whatever's currently scheduled */ |
| + if (smmu_pmu_check_global_filter(smmu_pmu->events[cur_idx], event)) { |
| + smmu_pmu_set_evtyper(smmu_pmu, idx, get_event(event)); |
| return 0; |
| } |
| |