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