1e224d1c1SKyle Huey // SPDX-License-Identifier: GPL-2.0
2e224d1c1SKyle Huey #define _GNU_SOURCE
3e224d1c1SKyle Huey
4e224d1c1SKyle Huey #include <errno.h>
5e224d1c1SKyle Huey #include <fcntl.h>
6e224d1c1SKyle Huey #include <linux/perf_event.h>
7e224d1c1SKyle Huey #include <stddef.h>
8e224d1c1SKyle Huey #include <sched.h>
9e224d1c1SKyle Huey #include <signal.h>
10e224d1c1SKyle Huey #include <stdlib.h>
11e224d1c1SKyle Huey #include <string.h>
12e224d1c1SKyle Huey #include <sys/ioctl.h>
13e224d1c1SKyle Huey #include <sys/mman.h>
14e224d1c1SKyle Huey #include <sys/syscall.h>
15e224d1c1SKyle Huey #include <sys/wait.h>
16e224d1c1SKyle Huey #include <unistd.h>
17e224d1c1SKyle Huey
18e224d1c1SKyle Huey #include "../kselftest_harness.h"
19e224d1c1SKyle Huey
20e224d1c1SKyle Huey #define __maybe_unused __attribute__((__unused__))
21e224d1c1SKyle Huey
22e224d1c1SKyle Huey static int sigio_count;
23e224d1c1SKyle Huey
handle_sigio(int signum __maybe_unused,siginfo_t * oh __maybe_unused,void * uc __maybe_unused)24e224d1c1SKyle Huey static void handle_sigio(int signum __maybe_unused,
25e224d1c1SKyle Huey siginfo_t *oh __maybe_unused,
26e224d1c1SKyle Huey void *uc __maybe_unused)
27e224d1c1SKyle Huey {
28e224d1c1SKyle Huey ++sigio_count;
29e224d1c1SKyle Huey }
30e224d1c1SKyle Huey
do_child(void)31e224d1c1SKyle Huey static void do_child(void)
32e224d1c1SKyle Huey {
33e224d1c1SKyle Huey raise(SIGSTOP);
34e224d1c1SKyle Huey
35e224d1c1SKyle Huey for (int i = 0; i < 20; ++i)
36e224d1c1SKyle Huey sleep(1);
37e224d1c1SKyle Huey
38e224d1c1SKyle Huey raise(SIGSTOP);
39e224d1c1SKyle Huey
40e224d1c1SKyle Huey exit(0);
41e224d1c1SKyle Huey }
42e224d1c1SKyle Huey
TEST(watermark_signal)43e224d1c1SKyle Huey TEST(watermark_signal)
44e224d1c1SKyle Huey {
45e224d1c1SKyle Huey struct perf_event_attr attr;
46e224d1c1SKyle Huey struct perf_event_mmap_page *p = NULL;
47e224d1c1SKyle Huey struct sigaction previous_sigio, sigio = { 0 };
48e224d1c1SKyle Huey pid_t child = -1;
49e224d1c1SKyle Huey int child_status;
50e224d1c1SKyle Huey int fd = -1;
51e224d1c1SKyle Huey long page_size = sysconf(_SC_PAGE_SIZE);
52e224d1c1SKyle Huey
53e224d1c1SKyle Huey sigio.sa_sigaction = handle_sigio;
54e224d1c1SKyle Huey EXPECT_EQ(sigaction(SIGIO, &sigio, &previous_sigio), 0);
55e224d1c1SKyle Huey
56e224d1c1SKyle Huey memset(&attr, 0, sizeof(attr));
57e224d1c1SKyle Huey attr.size = sizeof(attr);
58e224d1c1SKyle Huey attr.type = PERF_TYPE_SOFTWARE;
59e224d1c1SKyle Huey attr.config = PERF_COUNT_SW_DUMMY;
60e224d1c1SKyle Huey attr.sample_period = 1;
61e224d1c1SKyle Huey attr.disabled = 1;
62e224d1c1SKyle Huey attr.watermark = 1;
63e224d1c1SKyle Huey attr.context_switch = 1;
64e224d1c1SKyle Huey attr.wakeup_watermark = 1;
65e224d1c1SKyle Huey
66e224d1c1SKyle Huey child = fork();
67e224d1c1SKyle Huey EXPECT_GE(child, 0);
68e224d1c1SKyle Huey if (child == 0)
69e224d1c1SKyle Huey do_child();
70e224d1c1SKyle Huey else if (child < 0) {
71e224d1c1SKyle Huey perror("fork()");
72e224d1c1SKyle Huey goto cleanup;
73e224d1c1SKyle Huey }
74e224d1c1SKyle Huey
75e224d1c1SKyle Huey if (waitpid(child, &child_status, WSTOPPED) != child ||
76e224d1c1SKyle Huey !(WIFSTOPPED(child_status) && WSTOPSIG(child_status) == SIGSTOP)) {
77e224d1c1SKyle Huey fprintf(stderr,
78*9d4b78dfSColin Ian King "failed to synchronize with child errno=%d status=%x\n",
79e224d1c1SKyle Huey errno,
80e224d1c1SKyle Huey child_status);
81e224d1c1SKyle Huey goto cleanup;
82e224d1c1SKyle Huey }
83e224d1c1SKyle Huey
84e224d1c1SKyle Huey fd = syscall(__NR_perf_event_open, &attr, child, -1, -1,
85e224d1c1SKyle Huey PERF_FLAG_FD_CLOEXEC);
86e224d1c1SKyle Huey if (fd < 0) {
87e224d1c1SKyle Huey fprintf(stderr, "failed opening event %llx\n", attr.config);
88e224d1c1SKyle Huey goto cleanup;
89e224d1c1SKyle Huey }
90e224d1c1SKyle Huey
91e224d1c1SKyle Huey if (fcntl(fd, F_SETFL, FASYNC)) {
92e224d1c1SKyle Huey perror("F_SETFL FASYNC");
93e224d1c1SKyle Huey goto cleanup;
94e224d1c1SKyle Huey }
95e224d1c1SKyle Huey
96e224d1c1SKyle Huey if (fcntl(fd, F_SETOWN, getpid())) {
97e224d1c1SKyle Huey perror("F_SETOWN getpid()");
98e224d1c1SKyle Huey goto cleanup;
99e224d1c1SKyle Huey }
100e224d1c1SKyle Huey
101e224d1c1SKyle Huey if (fcntl(fd, F_SETSIG, SIGIO)) {
102e224d1c1SKyle Huey perror("F_SETSIG SIGIO");
103e224d1c1SKyle Huey goto cleanup;
104e224d1c1SKyle Huey }
105e224d1c1SKyle Huey
106e224d1c1SKyle Huey p = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
107e224d1c1SKyle Huey if (p == NULL) {
108e224d1c1SKyle Huey perror("mmap");
109e224d1c1SKyle Huey goto cleanup;
110e224d1c1SKyle Huey }
111e224d1c1SKyle Huey
112e224d1c1SKyle Huey if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)) {
113e224d1c1SKyle Huey perror("PERF_EVENT_IOC_ENABLE");
114e224d1c1SKyle Huey goto cleanup;
115e224d1c1SKyle Huey }
116e224d1c1SKyle Huey
117e224d1c1SKyle Huey if (kill(child, SIGCONT) < 0) {
118e224d1c1SKyle Huey perror("SIGCONT");
119e224d1c1SKyle Huey goto cleanup;
120e224d1c1SKyle Huey }
121e224d1c1SKyle Huey
122e224d1c1SKyle Huey if (waitpid(child, &child_status, WSTOPPED) != -1 || errno != EINTR)
123e224d1c1SKyle Huey fprintf(stderr,
124e224d1c1SKyle Huey "expected SIGIO to terminate wait errno=%d status=%x\n%d",
125e224d1c1SKyle Huey errno,
126e224d1c1SKyle Huey child_status,
127e224d1c1SKyle Huey sigio_count);
128e224d1c1SKyle Huey
129e224d1c1SKyle Huey EXPECT_GE(sigio_count, 1);
130e224d1c1SKyle Huey
131e224d1c1SKyle Huey cleanup:
132e224d1c1SKyle Huey if (p != NULL)
133e224d1c1SKyle Huey munmap(p, 2 * page_size);
134e224d1c1SKyle Huey
135e224d1c1SKyle Huey if (fd >= 0)
136e224d1c1SKyle Huey close(fd);
137e224d1c1SKyle Huey
138e224d1c1SKyle Huey if (child > 0) {
139e224d1c1SKyle Huey kill(child, SIGKILL);
140e224d1c1SKyle Huey waitpid(child, NULL, 0);
141e224d1c1SKyle Huey }
142e224d1c1SKyle Huey
143e224d1c1SKyle Huey sigaction(SIGIO, &previous_sigio, NULL);
144e224d1c1SKyle Huey }
145e224d1c1SKyle Huey
146e224d1c1SKyle Huey TEST_HARNESS_MAIN
147