xref: /linux/tools/testing/selftests/perf_events/watermark_signal.c (revision ba199dc909a20fe62270ae4e93f263987bb9d119)
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 
22 static int sigio_count;
23 
24 static void handle_sigio(int signum __maybe_unused,
25 			 siginfo_t *oh __maybe_unused,
26 			 void *uc __maybe_unused)
27 {
28 	++sigio_count;
29 }
30 
31 static 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 
43 TEST(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 
131 cleanup:
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 
146 TEST_HARNESS_MAIN
147