The test uses PERF_RECORD_SWITCH records to fill the ring buffer and
trigger the watermark wakeup, which in turn should trigger an IO
signal.

Signed-off-by: Kyle Huey <kh...@kylehuey.com>
---
 .../testing/selftests/perf_events/.gitignore  |   1 +
 tools/testing/selftests/perf_events/Makefile  |   2 +-
 .../selftests/perf_events/watermark_signal.c  | 146 ++++++++++++++++++
 3 files changed, 148 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/perf_events/watermark_signal.c

diff --git a/tools/testing/selftests/perf_events/.gitignore 
b/tools/testing/selftests/perf_events/.gitignore
index 790c47001e77..ee93dc4969b8 100644
--- a/tools/testing/selftests/perf_events/.gitignore
+++ b/tools/testing/selftests/perf_events/.gitignore
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 sigtrap_threads
 remove_on_exec
+watermark_signal
diff --git a/tools/testing/selftests/perf_events/Makefile 
b/tools/testing/selftests/perf_events/Makefile
index db93c4ff081a..70e3ff211278 100644
--- a/tools/testing/selftests/perf_events/Makefile
+++ b/tools/testing/selftests/perf_events/Makefile
@@ -2,5 +2,5 @@
 CFLAGS += -Wl,-no-as-needed -Wall $(KHDR_INCLUDES)
 LDFLAGS += -lpthread
 
-TEST_GEN_PROGS := sigtrap_threads remove_on_exec
+TEST_GEN_PROGS := sigtrap_threads remove_on_exec watermark_signal
 include ../lib.mk
diff --git a/tools/testing/selftests/perf_events/watermark_signal.c 
b/tools/testing/selftests/perf_events/watermark_signal.c
new file mode 100644
index 000000000000..49dc1e831174
--- /dev/null
+++ b/tools/testing/selftests/perf_events/watermark_signal.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/perf_event.h>
+#include <stddef.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define __maybe_unused __attribute__((__unused__))
+
+static int sigio_count;
+
+static void handle_sigio(int signum __maybe_unused,
+                        siginfo_t *oh __maybe_unused,
+                        void *uc __maybe_unused)
+{
+       ++sigio_count;
+}
+
+static void do_child(void)
+{
+       raise(SIGSTOP);
+
+       for (int i = 0; i < 20; ++i)
+               sleep(1);
+
+       raise(SIGSTOP);
+
+       exit(0);
+}
+
+TEST(watermark_signal)
+{
+       struct perf_event_attr attr;
+       struct perf_event_mmap_page *p = NULL;
+       struct sigaction previous_sigio, sigio = { 0 };
+       pid_t child = -1;
+       int child_status;
+       int fd = -1;
+       long page_size = sysconf(_SC_PAGE_SIZE);
+
+       sigio.sa_sigaction = handle_sigio;
+       EXPECT_EQ(sigaction(SIGIO, &sigio, &previous_sigio), 0);
+
+       memset(&attr, 0, sizeof(attr));
+       attr.size = sizeof(attr);
+       attr.type = PERF_TYPE_SOFTWARE;
+       attr.config = PERF_COUNT_SW_DUMMY;
+       attr.sample_period = 1;
+       attr.disabled = 1;
+       attr.watermark = 1;
+       attr.context_switch = 1;
+       attr.wakeup_watermark = 1;
+
+       child = fork();
+       EXPECT_GE(child, 0);
+       if (child == 0)
+               do_child();
+       else if (child < 0) {
+               perror("fork()");
+               goto cleanup;
+       }
+
+       if (waitpid(child, &child_status, WSTOPPED) != child ||
+           !(WIFSTOPPED(child_status) && WSTOPSIG(child_status) == SIGSTOP)) {
+               fprintf(stderr,
+                       "failed to sycnhronize with child errno=%d status=%x\n",
+                       errno,
+                       child_status);
+               goto cleanup;
+       }
+
+       fd = syscall(__NR_perf_event_open, &attr, child, -1, -1,
+                    PERF_FLAG_FD_CLOEXEC);
+       if (fd < 0) {
+               fprintf(stderr, "failed opening event %llx\n", attr.config);
+               goto cleanup;
+       }
+
+       if (fcntl(fd, F_SETFL, FASYNC)) {
+               perror("F_SETFL FASYNC");
+               goto cleanup;
+       }
+
+       if (fcntl(fd, F_SETOWN, getpid())) {
+               perror("F_SETOWN getpid()");
+               goto cleanup;
+       }
+
+       if (fcntl(fd, F_SETSIG, SIGIO)) {
+               perror("F_SETSIG SIGIO");
+               goto cleanup;
+       }
+
+       p = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 
0);
+       if (p == NULL) {
+               perror("mmap");
+               goto cleanup;
+       }
+
+       if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)) {
+               perror("PERF_EVENT_IOC_ENABLE");
+               goto cleanup;
+       }
+
+       if (kill(child, SIGCONT) < 0) {
+               perror("SIGCONT");
+               goto cleanup;
+       }
+
+       if (waitpid(child, &child_status, WSTOPPED) != -1 || errno != EINTR)
+               fprintf(stderr,
+                       "expected SIGIO to terminate wait errno=%d 
status=%x\n%d",
+                       errno,
+                       child_status,
+                       sigio_count);
+
+       EXPECT_GE(sigio_count, 1);
+
+cleanup:
+       if (p != NULL)
+               munmap(p, 2 * page_size);
+
+       if (fd >= 0)
+               close(fd);
+
+       if (child > 0) {
+               kill(child, SIGKILL);
+               waitpid(child, NULL, 0);
+       }
+
+       sigaction(SIGIO, &previous_sigio, NULL);
+}
+
+TEST_HARNESS_MAIN
-- 
2.34.1


Reply via email to