Commit 18dbcbfabfff ("perf: Fix the POLL_HUP delivery breakage") added a
direct pmu->stop(event, 0) call when the refresh limit reaches zero.That change was based on a report [1] using SIGIO to receive POLL_HUP. Since SIGIO is a standard signal, multiple notifications can be coalesced and userspace may miss a signal even though the perf core generated. Using a real-time signal avoids that coalescing and shows that POLL_HUP is delivered reliably without directly stopping the PMU from the overflow path. There is still a race to handle. For a high frequency event, another overflow can arrive after perf_event_disable_inatomic() has queued the disable irq_work but before that irq_work has run. If overflow processing continues, pending_kill can be overwritten from POLL_HUP back to POLL_IN and samples can be recorded after the refresh limit has been reached. The direct PMU stop avoids this by stopping the hardware immediately, but the event still relies on perf_event_disable_inatomic() to complete the disable state transition. This is redundant that it might inject unnecessary stop operations in the middle. More importantly, the throttling mechanism already exists for stopping high frequency overflows. Make the overflow path explicitly check pending_disable instead of the PMU stop. Once disable is pending, skip further overflow processing so the pending POLL_HUP is preserved and no samples are recorded for an event that is already waiting to be disabled. [1] https://lore.kernel.org/lkml/[email protected]/ Signed-off-by: Leo Yan <[email protected]> --- kernel/events/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 7935d5663944ee1cbaf38cf8018c3347635e8d31..0fadb53e5d79cab8cb52a08d7656b0064a77ef55 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -10745,12 +10745,18 @@ static int __perf_event_overflow(struct perf_event *event, * events */ + /* + * Disable is pending, skip further overflow processing so the pending + * POLL_HUP is preserved and no samples are recorded beyond the limit. + */ + if (event->pending_disable) + goto out; + event->pending_kill = POLL_IN; if (events && atomic_dec_and_test(&event->event_limit)) { ret = 1; event->pending_kill = POLL_HUP; perf_event_disable_inatomic(event); - event->pmu->stop(event, 0); } if (event->attr.sigtrap) { -- 2.34.1

