1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include "test_send_signal_kern.skel.h" 4 5 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 (!ASSERT_OK_PTR(skel->links.send_signal_perf, "attach_perf_event")) 95 goto disable_pmu; 96 } 97 98 /* wait until child signal handler installed */ 99 CHECK(read(pipe_c2p[0], buf, 1) != 1, "pipe_read", "err %d\n", -errno); 100 101 /* trigger the bpf send_signal */ 102 skel->bss->pid = pid; 103 skel->bss->sig = SIGUSR1; 104 skel->bss->signal_thread = signal_thread; 105 106 /* notify child that bpf program can send_signal now */ 107 CHECK(write(pipe_p2c[1], buf, 1) != 1, "pipe_write", "err %d\n", -errno); 108 109 /* wait for result */ 110 err = read(pipe_c2p[0], buf, 1); 111 if (CHECK(err < 0, test_name, "reading pipe error: %s\n", strerror(errno))) 112 goto disable_pmu; 113 if (CHECK(err == 0, test_name, "reading pipe error: size 0\n")) { 114 err = -1; 115 goto disable_pmu; 116 } 117 118 CHECK(buf[0] != '2', test_name, "incorrect result\n"); 119 120 /* notify child safe to exit */ 121 CHECK(write(pipe_p2c[1], buf, 1) != 1, "pipe_write", "err %d\n", -errno); 122 123 disable_pmu: 124 close(pmu_fd); 125 destroy_skel: 126 test_send_signal_kern__destroy(skel); 127 skel_open_load_failure: 128 close(pipe_c2p[0]); 129 close(pipe_p2c[1]); 130 wait(NULL); 131 } 132 133 static void test_send_signal_tracepoint(bool signal_thread) 134 { 135 test_send_signal_common(NULL, signal_thread, "tracepoint"); 136 } 137 138 static void test_send_signal_perf(bool signal_thread) 139 { 140 struct perf_event_attr attr = { 141 .sample_period = 1, 142 .type = PERF_TYPE_SOFTWARE, 143 .config = PERF_COUNT_SW_CPU_CLOCK, 144 }; 145 146 test_send_signal_common(&attr, signal_thread, "perf_sw_event"); 147 } 148 149 static void test_send_signal_nmi(bool signal_thread) 150 { 151 struct perf_event_attr attr = { 152 .sample_period = 1, 153 .type = PERF_TYPE_HARDWARE, 154 .config = PERF_COUNT_HW_CPU_CYCLES, 155 }; 156 int pmu_fd; 157 158 /* Some setups (e.g. virtual machines) might run with hardware 159 * perf events disabled. If this is the case, skip this test. 160 */ 161 pmu_fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */, 162 -1 /* cpu */, -1 /* group_fd */, 0 /* flags */); 163 if (pmu_fd == -1) { 164 if (errno == ENOENT) { 165 printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n", 166 __func__); 167 test__skip(); 168 return; 169 } 170 /* Let the test fail with a more informative message */ 171 } else { 172 close(pmu_fd); 173 } 174 175 test_send_signal_common(&attr, signal_thread, "perf_hw_event"); 176 } 177 178 void test_send_signal(void) 179 { 180 if (test__start_subtest("send_signal_tracepoint")) 181 test_send_signal_tracepoint(false); 182 if (test__start_subtest("send_signal_perf")) 183 test_send_signal_perf(false); 184 if (test__start_subtest("send_signal_nmi")) 185 test_send_signal_nmi(false); 186 if (test__start_subtest("send_signal_tracepoint_thread")) 187 test_send_signal_tracepoint(true); 188 if (test__start_subtest("send_signal_perf_thread")) 189 test_send_signal_perf(true); 190 if (test__start_subtest("send_signal_nmi_thread")) 191 test_send_signal_nmi(true); 192 } 193