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