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