1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "test_send_signal_kern.skel.h"
4 
5 int sigusr1_received = 0;
6 
7 static void sigusr1_handler(int signum)
8 {
9 	sigusr1_received++;
10 }
11 
12 static void test_send_signal_common(struct perf_event_attr *attr,
13 				    bool signal_thread)
14 {
15 	struct test_send_signal_kern *skel;
16 	int pipe_c2p[2], pipe_p2c[2];
17 	int err = -1, pmu_fd = -1;
18 	char buf[256];
19 	pid_t pid;
20 
21 	if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p"))
22 		return;
23 
24 	if (!ASSERT_OK(pipe(pipe_p2c), "pipe_p2c")) {
25 		close(pipe_c2p[0]);
26 		close(pipe_c2p[1]);
27 		return;
28 	}
29 
30 	pid = fork();
31 	if (!ASSERT_GE(pid, 0, "fork")) {
32 		close(pipe_c2p[0]);
33 		close(pipe_c2p[1]);
34 		close(pipe_p2c[0]);
35 		close(pipe_p2c[1]);
36 		return;
37 	}
38 
39 	if (pid == 0) {
40 		/* install signal handler and notify parent */
41 		signal(SIGUSR1, sigusr1_handler);
42 
43 		close(pipe_c2p[0]); /* close read */
44 		close(pipe_p2c[1]); /* close write */
45 
46 		/* notify parent signal handler is installed */
47 		ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write");
48 
49 		/* make sure parent enabled bpf program to send_signal */
50 		ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read");
51 
52 		/* wait a little for signal handler */
53 		sleep(1);
54 
55 		buf[0] = sigusr1_received ? '2' : '0';
56 		ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write");
57 
58 		/* wait for parent notification and exit */
59 		ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read");
60 
61 		close(pipe_c2p[1]);
62 		close(pipe_p2c[0]);
63 		exit(0);
64 	}
65 
66 	close(pipe_c2p[1]); /* close write */
67 	close(pipe_p2c[0]); /* close read */
68 
69 	skel = test_send_signal_kern__open_and_load();
70 	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
71 		goto skel_open_load_failure;
72 
73 	if (!attr) {
74 		err = test_send_signal_kern__attach(skel);
75 		if (!ASSERT_OK(err, "skel_attach")) {
76 			err = -1;
77 			goto destroy_skel;
78 		}
79 	} else {
80 		pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1,
81 				 -1 /* group id */, 0 /* flags */);
82 		if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) {
83 			err = -1;
84 			goto destroy_skel;
85 		}
86 
87 		skel->links.send_signal_perf =
88 			bpf_program__attach_perf_event(skel->progs.send_signal_perf, pmu_fd);
89 		if (!ASSERT_OK_PTR(skel->links.send_signal_perf, "attach_perf_event"))
90 			goto disable_pmu;
91 	}
92 
93 	/* wait until child signal handler installed */
94 	ASSERT_EQ(read(pipe_c2p[0], buf, 1), 1, "pipe_read");
95 
96 	/* trigger the bpf send_signal */
97 	skel->bss->pid = pid;
98 	skel->bss->sig = SIGUSR1;
99 	skel->bss->signal_thread = signal_thread;
100 
101 	/* notify child that bpf program can send_signal now */
102 	ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write");
103 
104 	/* wait for result */
105 	err = read(pipe_c2p[0], buf, 1);
106 	if (!ASSERT_GE(err, 0, "reading pipe"))
107 		goto disable_pmu;
108 	if (!ASSERT_GT(err, 0, "reading pipe error: size 0")) {
109 		err = -1;
110 		goto disable_pmu;
111 	}
112 
113 	ASSERT_EQ(buf[0], '2', "incorrect result");
114 
115 	/* notify child safe to exit */
116 	ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write");
117 
118 disable_pmu:
119 	close(pmu_fd);
120 destroy_skel:
121 	test_send_signal_kern__destroy(skel);
122 skel_open_load_failure:
123 	close(pipe_c2p[0]);
124 	close(pipe_p2c[1]);
125 	wait(NULL);
126 }
127 
128 static void test_send_signal_tracepoint(bool signal_thread)
129 {
130 	test_send_signal_common(NULL, signal_thread);
131 }
132 
133 static void test_send_signal_perf(bool signal_thread)
134 {
135 	struct perf_event_attr attr = {
136 		.sample_period = 1,
137 		.type = PERF_TYPE_SOFTWARE,
138 		.config = PERF_COUNT_SW_CPU_CLOCK,
139 	};
140 
141 	test_send_signal_common(&attr, signal_thread);
142 }
143 
144 static void test_send_signal_nmi(bool signal_thread)
145 {
146 	struct perf_event_attr attr = {
147 		.sample_period = 1,
148 		.type = PERF_TYPE_HARDWARE,
149 		.config = PERF_COUNT_HW_CPU_CYCLES,
150 	};
151 	int pmu_fd;
152 
153 	/* Some setups (e.g. virtual machines) might run with hardware
154 	 * perf events disabled. If this is the case, skip this test.
155 	 */
156 	pmu_fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */,
157 			 -1 /* cpu */, -1 /* group_fd */, 0 /* flags */);
158 	if (pmu_fd == -1) {
159 		if (errno == ENOENT) {
160 			printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n",
161 			       __func__);
162 			test__skip();
163 			return;
164 		}
165 		/* Let the test fail with a more informative message */
166 	} else {
167 		close(pmu_fd);
168 	}
169 
170 	test_send_signal_common(&attr, signal_thread);
171 }
172 
173 void test_send_signal(void)
174 {
175 	if (test__start_subtest("send_signal_tracepoint"))
176 		test_send_signal_tracepoint(false);
177 	if (test__start_subtest("send_signal_perf"))
178 		test_send_signal_perf(false);
179 	if (test__start_subtest("send_signal_nmi"))
180 		test_send_signal_nmi(false);
181 	if (test__start_subtest("send_signal_tracepoint_thread"))
182 		test_send_signal_tracepoint(true);
183 	if (test__start_subtest("send_signal_perf_thread"))
184 		test_send_signal_perf(true);
185 	if (test__start_subtest("send_signal_nmi_thread"))
186 		test_send_signal_nmi(true);
187 }
188