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


Reply via email to