AUX snapshot sampling and AUX pause/resume can both run from NMI
context. Each path currently guards only against its own recursion.
An NMI can therefore enter one path while the other is changing the
AUX hardware or buffer state, allowing snapshot_aux() and the PMU
pause/resume callbacks to overlap.
Use aux_in_sampling and aux_in_pause_resume as a shared exclusion
scheme. If sampling nests inside pause/resume, emit the regular sample
without an AUX payload. If pause/resume nests inside AUX sampling, drop
the AUX action, matching the existing behavior for recursive
pause/resume.
This prevents overlapping PMU operations without waiting in NMI
context.
Assisted-by: Codex:gpt-6
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
kernel/events/core.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 17355e4b8b7c6c106117153c9eb8509e6f9a41e3..a524eb381d4b5731650ec0105d9794b8c89fba4d 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7990,10 +7990,12 @@ static unsigned long perf_prepare_sample_aux(struct perf_event *event,
goto out;
/*
- * If this is an NMI hit inside sampling code, don't take
- * the sample. See also perf_aux_sample_output().
+ * If this is an NMI hit inside AUX sampling or pause/resume, don't
+ * include AUX data in the sample. See also perf_aux_sample_output()
+ * and perf_event_aux_pause().
*/
- if (READ_ONCE(rb->aux_in_sampling)) {
+ if (READ_ONCE(rb->aux_in_sampling) ||
+ READ_ONCE(rb->aux_in_pause_resume)) {
data->aux_size = 0;
} else {
size = min_t(size_t, size, perf_aux_size(rb));@@ -8869,10 +8871,13 @@ static void perf_event_aux_pause(struct perf_event *event, bool pause)
scoped_guard (irqsave) {
/*
- * Guard against self-recursion here. Another event could trip
- * this same from NMI context.
+ * Guard against self-recursion and AUX sampling. Another event
+ * could trigger either path from NMI context, in which case the
+ * AUX action is dropped instead of waiting for the preempted
+ * context.
*/
- if (READ_ONCE(rb->aux_in_pause_resume))
+ if (READ_ONCE(rb->aux_in_pause_resume) ||
+ READ_ONCE(rb->aux_in_sampling))
break;
WRITE_ONCE(rb->aux_in_pause_resume, 1);
--
2.34.1