1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include <sys/time.h> 4 #include <sys/resource.h> 5 #include "test_send_signal_kern.skel.h" 6 #include "io_helpers.h" 7 8 static int sigusr1_received; 9 10 static void sigusr1_handler(int signum) 11 { 12 sigusr1_received = 8; 13 } 14 15 static void sigusr1_siginfo_handler(int s, siginfo_t *i, void *v) 16 { 17 sigusr1_received = (int)(long long)i->si_value.sival_ptr; 18 } 19 20 static void test_send_signal_common(struct perf_event_attr *attr, 21 bool signal_thread, bool remote) 22 { 23 struct test_send_signal_kern *skel; 24 struct sigaction sa; 25 int pipe_c2p[2], pipe_p2c[2]; 26 int err = -1, pmu_fd = -1; 27 volatile int j = 0; 28 int retry_count; 29 char buf[256]; 30 pid_t pid; 31 int old_prio; 32 33 if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p")) 34 return; 35 36 if (!ASSERT_OK(pipe(pipe_p2c), "pipe_p2c")) { 37 close(pipe_c2p[0]); 38 close(pipe_c2p[1]); 39 return; 40 } 41 42 pid = fork(); 43 if (!ASSERT_GE(pid, 0, "fork")) { 44 close(pipe_c2p[0]); 45 close(pipe_c2p[1]); 46 close(pipe_p2c[0]); 47 close(pipe_p2c[1]); 48 return; 49 } 50 51 if (pid == 0) { 52 /* install signal handler and notify parent */ 53 if (remote) { 54 sa.sa_sigaction = sigusr1_siginfo_handler; 55 sa.sa_flags = SA_RESTART | SA_SIGINFO; 56 ASSERT_NEQ(sigaction(SIGUSR1, &sa, NULL), -1, "sigaction"); 57 } else { 58 ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); 59 } 60 61 close(pipe_c2p[0]); /* close read */ 62 close(pipe_p2c[1]); /* close write */ 63 64 /* boost with a high priority so we got a higher chance 65 * that if an interrupt happens, the underlying task 66 * is this process. 67 */ 68 if (!remote) { 69 errno = 0; 70 old_prio = getpriority(PRIO_PROCESS, 0); 71 ASSERT_OK(errno, "getpriority"); 72 ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); 73 } 74 75 /* notify parent signal handler is installed */ 76 ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); 77 78 /* make sure parent enabled bpf program to send_signal */ 79 ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read"); 80 81 /* wait a little for signal handler */ 82 for (int i = 0; i < 1000000000 && !sigusr1_received; i++) { 83 j /= i + j + 1; 84 if (remote) 85 sleep(1); 86 else 87 if (!attr) 88 /* trigger the nanosleep tracepoint program. */ 89 usleep(1); 90 } 91 92 buf[0] = sigusr1_received; 93 94 ASSERT_EQ(sigusr1_received, 8, "sigusr1_received"); 95 ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); 96 97 /* wait for parent notification and exit */ 98 ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read"); 99 100 /* restore the old priority */ 101 if (!remote) 102 ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); 103 104 close(pipe_c2p[1]); 105 close(pipe_p2c[0]); 106 exit(0); 107 } 108 109 close(pipe_c2p[1]); /* close write */ 110 close(pipe_p2c[0]); /* close read */ 111 112 skel = test_send_signal_kern__open_and_load(); 113 if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) 114 goto skel_open_load_failure; 115 116 /* boost with a high priority so we got a higher chance 117 * that if an interrupt happens, the underlying task 118 * is this process. 119 */ 120 if (remote) { 121 errno = 0; 122 old_prio = getpriority(PRIO_PROCESS, 0); 123 ASSERT_OK(errno, "getpriority"); 124 ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); 125 } 126 127 if (!attr) { 128 err = test_send_signal_kern__attach(skel); 129 if (!ASSERT_OK(err, "skel_attach")) { 130 err = -1; 131 goto destroy_skel; 132 } 133 } else { 134 if (!remote) 135 pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, 136 -1 /* group id */, 0 /* flags */); 137 else 138 pmu_fd = syscall(__NR_perf_event_open, attr, getpid(), -1 /* cpu */, 139 -1 /* group id */, 0 /* flags */); 140 if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) { 141 err = -1; 142 goto destroy_skel; 143 } 144 145 skel->links.send_signal_perf = 146 bpf_program__attach_perf_event(skel->progs.send_signal_perf, pmu_fd); 147 if (!ASSERT_OK_PTR(skel->links.send_signal_perf, "attach_perf_event")) 148 goto disable_pmu; 149 } 150 151 /* wait until child signal handler installed */ 152 ASSERT_EQ(read(pipe_c2p[0], buf, 1), 1, "pipe_read"); 153 154 /* trigger the bpf send_signal */ 155 skel->bss->signal_thread = signal_thread; 156 skel->bss->sig = SIGUSR1; 157 if (!remote) { 158 skel->bss->target_pid = 0; 159 skel->bss->pid = pid; 160 } else { 161 skel->bss->target_pid = pid; 162 skel->bss->pid = getpid(); 163 } 164 165 /* notify child that bpf program can send_signal now */ 166 ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); 167 168 for (retry_count = 0;;) { 169 /* For the remote test, the BPF program is triggered from this 170 * process but the other process/thread is signaled. 171 */ 172 if (remote) { 173 if (!attr) { 174 for (int i = 0; i < 10; i++) 175 usleep(1); 176 } else { 177 for (int i = 0; i < 100000000; i++) 178 j /= i + 1; 179 } 180 } 181 /* wait for result */ 182 err = read_with_timeout(pipe_c2p[0], buf, 1, 100); 183 if (err == -EAGAIN && retry_count++ < 10000) 184 continue; 185 break; 186 } 187 if (!ASSERT_GE(err, 0, "reading pipe")) 188 goto disable_pmu; 189 if (!ASSERT_GT(err, 0, "reading pipe error: size 0")) { 190 err = -1; 191 goto disable_pmu; 192 } 193 194 ASSERT_EQ(buf[0], 8, "incorrect result"); 195 196 /* notify child safe to exit */ 197 ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); 198 199 disable_pmu: 200 close(pmu_fd); 201 destroy_skel: 202 test_send_signal_kern__destroy(skel); 203 /* restore the old priority */ 204 if (remote) 205 ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); 206 skel_open_load_failure: 207 close(pipe_c2p[0]); 208 close(pipe_p2c[1]); 209 /* 210 * Child is either about to exit cleanly or stuck in case of errors. 211 * Nudge it to exit. 212 */ 213 kill(pid, SIGKILL); 214 wait(NULL); 215 } 216 217 static void test_send_signal_tracepoint(bool signal_thread, bool remote) 218 { 219 test_send_signal_common(NULL, signal_thread, remote); 220 } 221 222 static void test_send_signal_perf(bool signal_thread, bool remote) 223 { 224 struct perf_event_attr attr = { 225 .freq = 1, 226 .sample_freq = 1000, 227 .type = PERF_TYPE_SOFTWARE, 228 .config = PERF_COUNT_SW_CPU_CLOCK, 229 }; 230 231 test_send_signal_common(&attr, signal_thread, remote); 232 } 233 234 static void test_send_signal_nmi(bool signal_thread, bool remote) 235 { 236 struct perf_event_attr attr = { 237 .freq = 1, 238 .sample_freq = 1000, 239 .type = PERF_TYPE_HARDWARE, 240 .config = PERF_COUNT_HW_CPU_CYCLES, 241 }; 242 int pmu_fd; 243 244 /* Some setups (e.g. virtual machines) might run with hardware 245 * perf events disabled. If this is the case, skip this test. 246 */ 247 pmu_fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, 248 -1 /* cpu */, -1 /* group_fd */, 0 /* flags */); 249 if (pmu_fd == -1) { 250 if (errno == ENOENT || errno == EOPNOTSUPP) { 251 printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n", 252 __func__); 253 test__skip(); 254 return; 255 } 256 /* Let the test fail with a more informative message */ 257 } else { 258 close(pmu_fd); 259 } 260 261 test_send_signal_common(&attr, signal_thread, remote); 262 } 263 264 void test_send_signal(void) 265 { 266 if (test__start_subtest("send_signal_tracepoint")) 267 test_send_signal_tracepoint(false, false); 268 if (test__start_subtest("send_signal_perf")) 269 test_send_signal_perf(false, false); 270 if (test__start_subtest("send_signal_nmi")) 271 test_send_signal_nmi(false, false); 272 if (test__start_subtest("send_signal_tracepoint_thread")) 273 test_send_signal_tracepoint(true, false); 274 if (test__start_subtest("send_signal_perf_thread")) 275 test_send_signal_perf(true, false); 276 if (test__start_subtest("send_signal_nmi_thread")) 277 test_send_signal_nmi(true, false); 278 279 /* Signal remote thread and thread group */ 280 if (test__start_subtest("send_signal_tracepoint_remote")) 281 test_send_signal_tracepoint(false, true); 282 if (test__start_subtest("send_signal_perf_remote")) 283 test_send_signal_perf(false, true); 284 if (test__start_subtest("send_signal_nmi_remote")) 285 test_send_signal_nmi(false, true); 286 if (test__start_subtest("send_signal_tracepoint_thread_remote")) 287 test_send_signal_tracepoint(true, true); 288 if (test__start_subtest("send_signal_perf_thread_remote")) 289 test_send_signal_perf(true, true); 290 if (test__start_subtest("send_signal_nmi_thread_remote")) 291 test_send_signal_nmi(true, true); 292 } 293