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