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