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