| From bf6fd29fedf733974c978cf81315dc0f90fe2ec8 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 29 Sep 2021 17:04:21 +1000 |
| Subject: perf/x86: Reset destroy callback on event init failure |
| |
| From: Anand K Mistry <amistry@google.com> |
| |
| [ Upstream commit 02d029a41dc986e2d5a77ecca45803857b346829 ] |
| |
| perf_init_event tries multiple init callbacks and does not reset the |
| event state between tries. When x86_pmu_event_init runs, it |
| unconditionally sets the destroy callback to hw_perf_event_destroy. On |
| the next init attempt after x86_pmu_event_init, in perf_try_init_event, |
| if the pmu's capabilities includes PERF_PMU_CAP_NO_EXCLUDE, the destroy |
| callback will be run. However, if the next init didn't set the destroy |
| callback, hw_perf_event_destroy will be run (since the callback wasn't |
| reset). |
| |
| Looking at other pmu init functions, the common pattern is to only set |
| the destroy callback on a successful init. Resetting the callback on |
| failure tries to replicate that pattern. |
| |
| This was discovered after commit f11dd0d80555 ("perf/x86/amd/ibs: Extend |
| PERF_PMU_CAP_NO_EXCLUDE to IBS Op") when the second (and only second) |
| run of the perf tool after a reboot results in 0 samples being |
| generated. The extra run of hw_perf_event_destroy results in |
| active_events having an extra decrement on each perf run. The second run |
| has active_events == 0 and every subsequent run has active_events < 0. |
| When active_events == 0, the NMI handler will early-out and not record |
| any samples. |
| |
| Signed-off-by: Anand K Mistry <amistry@google.com> |
| Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Link: https://lkml.kernel.org/r/20210929170405.1.I078b98ee7727f9ae9d6df8262bad7e325e40faf0@changeid |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kernel/cpu/perf_event.c | 1 + |
| 1 file changed, 1 insertion(+) |
| |
| diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c |
| index b983d3dc4e6c..851fbdb99767 100644 |
| --- a/arch/x86/kernel/cpu/perf_event.c |
| +++ b/arch/x86/kernel/cpu/perf_event.c |
| @@ -2001,6 +2001,7 @@ static int x86_pmu_event_init(struct perf_event *event) |
| if (err) { |
| if (event->destroy) |
| event->destroy(event); |
| + event->destroy = NULL; |
| } |
| |
| if (ACCESS_ONCE(x86_pmu.attr_rdpmc)) |
| -- |
| 2.33.0 |
| |