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 wait(NULL); 210 } 211 212 static void test_send_signal_tracepoint(bool signal_thread, bool remote) 213 { 214 test_send_signal_common(NULL, signal_thread, remote); 215 } 216 217 static void test_send_signal_perf(bool signal_thread, bool remote) 218 { 219 struct perf_event_attr attr = { 220 .freq = 1, 221 .sample_freq = 1000, 222 .type = PERF_TYPE_SOFTWARE, 223 .config = PERF_COUNT_SW_CPU_CLOCK, 224 }; 225 226 test_send_signal_common(&attr, signal_thread, remote); 227 } 228 229 static void test_send_signal_nmi(bool signal_thread, bool remote) 230 { 231 struct perf_event_attr attr = { 232 .freq = 1, 233 .sample_freq = 1000, 234 .type = PERF_TYPE_HARDWARE, 235 .config = PERF_COUNT_HW_CPU_CYCLES, 236 }; 237 int pmu_fd; 238 239 /* Some setups (e.g. virtual machines) might run with hardware 240 * perf events disabled. If this is the case, skip this test. 241 */ 242 pmu_fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, 243 -1 /* cpu */, -1 /* group_fd */, 0 /* flags */); 244 if (pmu_fd == -1) { 245 if (errno == ENOENT || errno == EOPNOTSUPP) { 246 printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n", 247 __func__); 248 test__skip(); 249 return; 250 } 251 /* Let the test fail with a more informative message */ 252 } else { 253 close(pmu_fd); 254 } 255 256 test_send_signal_common(&attr, signal_thread, remote); 257 } 258 259 void test_send_signal(void) 260 { 261 if (test__start_subtest("send_signal_tracepoint")) 262 test_send_signal_tracepoint(false, false); 263 if (test__start_subtest("send_signal_perf")) 264 test_send_signal_perf(false, false); 265 if (test__start_subtest("send_signal_nmi")) 266 test_send_signal_nmi(false, false); 267 if (test__start_subtest("send_signal_tracepoint_thread")) 268 test_send_signal_tracepoint(true, false); 269 if (test__start_subtest("send_signal_perf_thread")) 270 test_send_signal_perf(true, false); 271 if (test__start_subtest("send_signal_nmi_thread")) 272 test_send_signal_nmi(true, false); 273 274 /* Signal remote thread and thread group */ 275 if (test__start_subtest("send_signal_tracepoint_remote")) 276 test_send_signal_tracepoint(false, true); 277 if (test__start_subtest("send_signal_perf_remote")) 278 test_send_signal_perf(false, true); 279 if (test__start_subtest("send_signal_nmi_remote")) 280 test_send_signal_nmi(false, true); 281 if (test__start_subtest("send_signal_tracepoint_thread_remote")) 282 test_send_signal_tracepoint(true, true); 283 if (test__start_subtest("send_signal_perf_thread_remote")) 284 test_send_signal_perf(true, true); 285 if (test__start_subtest("send_signal_nmi_thread_remote")) 286 test_send_signal_nmi(true, true); 287 } 288