From: Francesco Nigro <nigro.fra@gmail.com>
To: peterz@infradead.org, mingo@redhat.com, acme@kernel.org,
namhyung@kernel.org
Cc: mark.rutland@arm.com, alexander.shishkin@linux.intel.com,
jolsa@kernel.org, irogers@google.com, adrian.hunter@intel.com,
james.clark@linaro.org, kan.liang@linux.intel.com,
leo.yan@arm.com, linux-perf-users@vger.kernel.org,
linux-kernel@vger.kernel.org, regressions@lists.linux.dev,
Francesco Nigro <nigro.fra@gmail.com>
Subject: [PATCH v2] perf test: Add test for events re-armed with PERF_EVENT_IOC_REFRESH
Date: Wed, 21 Oct 2026 16:17:36 +0200 [thread overview]
Message-ID: <20261021141736.2414170-1-nigro.fra@gmail.com> (raw)
Open a sampling event with sample_period 1, re-arm it with
PERF_EVENT_IOC_REFRESH from its overflow signal handler, as
self-profilers such as async-profiler do, and check that each of 1000
events delivers a signal. One case uses a software event (page faults),
the other the syscalls:sys_enter_getppid tracepoint. The signal is a
real-time one, so overflows cannot be coalesced, and the page fault
buffer is marked MADV_NOHUGEPAGE so that each touched page faults once.
The tracepoint case fails since
commit 18dbcbfabfff ("perf: Fix the POLL_HUP delivery breakage"):
only the first overflow is delivered. The re-arming ioctl succeeds,
but the event count stays at 1. It passes on the parent commit. The
software case passes on both.
The test was written with an AI coding assistant (Claude) from a
standalone reproducer; I reviewed it and ran it in QEMU on current
mainline, on that commit and on its parent.
Link: https://github.com/async-profiler/async-profiler/issues/1813
Signed-off-by: Francesco Nigro <nigro.fra@gmail.com>
---
Notes:
v2:
- Mark the page fault buffer MADV_NOHUGEPAGE: with transparent_hugepage=always
the software case got 489 of 1000 overflows (reported by Sashiko).
- Use a real-time signal instead of SIGIO, so overflows cannot be coalesced.
- Print the event count, POLL_HUP signals and the re-arm ioctl's errno.
- Skip only on EACCES/EPERM, check the setup calls, accept >= 1000
overflows, close the event before restoring the signal handler.
v1: https://lore.kernel.org/r/20261021131006.2101248-1-nigro.fra@gmail.com
To run it on any x86_64 box with virtme-ng:
vng --kconfig && make -j$(nproc)
make -C tools/perf NO_LIBTRACEEVENT=1
vng -- tools/perf/perf test -vv REFRESH
Tracepoint case, 2 vCPUs:
b320789d6883 (parent) Ok: count 1000, 1000 overflows (1000 POLL_HUP)
18dbcbfabfff FAILED: count 1, 1 overflows (1 POLL_HUP), re-arm errno 0
a804c5742bff (master) FAILED: count 1, 1 overflows (1 POLL_HUP), re-arm errno 0
The software case passes on all three, and on master with
transparent_hugepage=always.
Leo Yan's series "perf: Revert direct PMU stop for refresh limit"
reverts 18dbcbfabfff for a different reason; I haven't tested it
against this:
https://lore.kernel.org/r/20260512-arm_cs_clean_perf_handle-v1-0-75ff373ecd22@arm.com
#regzbot introduced: 18dbcbfabfff
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/event_refresh.c | 162 +++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
4 files changed, 165 insertions(+)
create mode 100644 tools/perf/tests/event_refresh.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 66944a4f4..199e10c8c 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -23,6 +23,7 @@ perf-test-y += hists_output.o
perf-test-y += hists_cumulate.o
perf-test-y += bp_signal.o
perf-test-y += bp_signal_overflow.o
+perf-test-y += event_refresh.o
perf-test-y += bp_account.o
perf-test-y += wp.o
perf-test-y += task-exit.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 4d0784b16..2b10f4d7f 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -98,6 +98,7 @@ static struct test_suite *generic_tests[] = {
&suite__hists_link,
&suite__bp_signal,
&suite__bp_signal_overflow,
+ &suite__event_refresh,
&suite__bp_accounting,
&suite__wp,
&suite__task_exit,
diff --git a/tools/perf/tests/event_refresh.c b/tools/perf/tests/event_refresh.c
new file mode 100644
index 000000000..726e5de4c
--- /dev/null
+++ b/tools/perf/tests/event_refresh.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Re-arm a sampling event from its overflow signal handler with
+ * PERF_EVENT_IOC_REFRESH and check that every overflow is delivered.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <linux/perf_event.h>
+
+#include "cloexec.h"
+#include "debug.h"
+#include "tests.h"
+#include "tp_pmu.h"
+#include <internal/lib.h> // page_size
+#include "../perf-sys.h"
+
+#define NR_EVENTS 1000
+/* A real-time signal is queued, so no overflow can be coalesced away. */
+#define REFRESH_SIGNAL (SIGRTMIN + 1)
+
+static volatile sig_atomic_t overflows, hups, refresh_errno;
+
+static void sig_handler(int signum __maybe_unused, siginfo_t *info,
+ void *uc __maybe_unused)
+{
+ int saved_errno = errno;
+
+ overflows++;
+ if (info->si_code == POLL_HUP)
+ hups++;
+ if (ioctl(info->si_fd, PERF_EVENT_IOC_REFRESH, 1) < 0)
+ refresh_errno = errno;
+ errno = saved_errno;
+}
+
+/* Count overflow signals for NR_EVENTS calls of trigger(), period 1. */
+static int count_overflows(struct perf_event_attr *attr, void (*trigger)(int i))
+{
+ struct sigaction sa, old_sa;
+ uint64_t count = 0;
+ int fd, i, err = TEST_FAIL;
+
+ attr->size = sizeof(*attr);
+ attr->sample_period = 1;
+ attr->disabled = 1;
+
+ fd = sys_perf_event_open(attr, 0, -1, -1, perf_event_open_cloexec_flag());
+ if (fd < 0) {
+ int open_errno = errno;
+
+ pr_debug("failed opening event: %s\n", strerror(open_errno));
+ return open_errno == EACCES || open_errno == EPERM ? TEST_SKIP : TEST_FAIL;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = sig_handler;
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(REFRESH_SIGNAL, &sa, &old_sa) < 0) {
+ pr_debug("failed setting up signal handler\n");
+ close(fd);
+ return TEST_FAIL;
+ }
+
+ if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK | O_ASYNC) < 0 ||
+ fcntl(fd, F_SETSIG, REFRESH_SIGNAL) < 0 ||
+ fcntl(fd, F_SETOWN, getpid()) < 0) {
+ pr_debug("failed setting up signal delivery: %s\n", strerror(errno));
+ goto out;
+ }
+
+ overflows = 0;
+ hups = 0;
+ refresh_errno = 0;
+ if (ioctl(fd, PERF_EVENT_IOC_REFRESH, 1) < 0) {
+ pr_debug("PERF_EVENT_IOC_REFRESH failed: %s\n", strerror(errno));
+ goto out;
+ }
+ for (i = 0; i < NR_EVENTS; i++)
+ trigger(i);
+ ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
+
+ if (read(fd, &count, sizeof(count)) != sizeof(count))
+ pr_debug("failed to read the event count\n");
+ pr_debug("%d events, count %" PRIu64 ", %d overflows (%d POLL_HUP), re-arm errno %d\n",
+ NR_EVENTS, count, overflows, hups, refresh_errno);
+ /* Unrelated page faults can only add overflows, never hide one. */
+ if (overflows >= NR_EVENTS)
+ err = TEST_OK;
+out:
+ /* Close first: a late signal must still find our handler. */
+ close(fd);
+ sigaction(REFRESH_SIGNAL, &old_sa, NULL);
+ return err;
+}
+
+static char *pages;
+
+static void touch_page(int i)
+{
+ pages[(long)i * page_size] = 1;
+}
+
+static int test__event_refresh_sw(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_PAGE_FAULTS,
+ };
+ int err;
+
+ pages = mmap(NULL, (long)NR_EVENTS * page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (pages == MAP_FAILED)
+ return TEST_FAIL;
+ /* One fault per touched page: a huge page would cover many of them. */
+ madvise(pages, (long)NR_EVENTS * page_size, MADV_NOHUGEPAGE);
+
+ err = count_overflows(&attr, touch_page);
+ munmap(pages, (long)NR_EVENTS * page_size);
+ return err;
+}
+
+static void call_getppid(int i __maybe_unused)
+{
+ syscall(SYS_getppid);
+}
+
+static int test__event_refresh_tp(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_TRACEPOINT,
+ };
+ int id = tp_pmu__id("syscalls", "sys_enter_getppid");
+
+ if (id < 0) {
+ pr_debug("no syscalls:sys_enter_getppid tracepoint\n");
+ return TEST_SKIP;
+ }
+ attr.config = id;
+
+ return count_overflows(&attr, call_getppid);
+}
+
+static struct test_case tests__event_refresh[] = {
+ TEST_CASE_REASON("Software event", event_refresh_sw, "permissions"),
+ TEST_CASE_REASON("Tracepoint event", event_refresh_tp, "permissions"),
+ { .name = NULL, }
+};
+
+struct test_suite suite__event_refresh = {
+ .desc = "Event re-armed with PERF_EVENT_IOC_REFRESH",
+ .test_cases = tests__event_refresh,
+};
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index cee9e6b62..ecc25b5a3 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -124,6 +124,7 @@ DECLARE_SUITE(parse_events);
DECLARE_SUITE(hists_link);
DECLARE_SUITE(bp_signal);
DECLARE_SUITE(bp_signal_overflow);
+DECLARE_SUITE(event_refresh);
DECLARE_SUITE(bp_accounting);
DECLARE_SUITE(wp);
DECLARE_SUITE(task_exit);
--
2.55.0
reply other threads:[~2026-10-21 14:17 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261021141736.2414170-1-nigro.fra@gmail.com \
--to=nigro.fra@gmail.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=leo.yan@arm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=regressions@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®