Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Augment send_signal test with remote signaling

Add testcases to test bpf_send_signal_task(). In these new test cases,
the main process triggers the BPF program and the forked process
receives the signals. The target process's signal handler receives a
cookie from the bpf program.

Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20241016084136.10305-3-puranjay@kernel.org

authored by

Puranjay Mohan and committed by
Andrii Nakryiko
0e141894 6280cf71

+130 -38
+101 -32
tools/testing/selftests/bpf/prog_tests/send_signal.c
··· 8 8 9 9 static void sigusr1_handler(int signum) 10 10 { 11 - sigusr1_received = 1; 11 + sigusr1_received = 8; 12 + } 13 + 14 + static void sigusr1_siginfo_handler(int s, siginfo_t *i, void *v) 15 + { 16 + sigusr1_received = (int)(long long)i->si_value.sival_ptr; 12 17 } 13 18 14 19 static void test_send_signal_common(struct perf_event_attr *attr, 15 - bool signal_thread) 20 + bool signal_thread, bool remote) 16 21 { 17 22 struct test_send_signal_kern *skel; 23 + struct sigaction sa; 18 24 int pipe_c2p[2], pipe_p2c[2]; 19 25 int err = -1, pmu_fd = -1; 26 + volatile int j = 0; 20 27 char buf[256]; 21 28 pid_t pid; 29 + int old_prio; 22 30 23 31 if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p")) 24 32 return; ··· 47 39 } 48 40 49 41 if (pid == 0) { 50 - int old_prio; 51 - volatile int j = 0; 52 - 53 42 /* install signal handler and notify parent */ 54 - ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); 43 + if (remote) { 44 + sa.sa_sigaction = sigusr1_siginfo_handler; 45 + sa.sa_flags = SA_RESTART | SA_SIGINFO; 46 + ASSERT_NEQ(sigaction(SIGUSR1, &sa, NULL), -1, "sigaction"); 47 + } else { 48 + ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); 49 + } 55 50 56 51 close(pipe_c2p[0]); /* close read */ 57 52 close(pipe_p2c[1]); /* close write */ ··· 63 52 * that if an interrupt happens, the underlying task 64 53 * is this process. 65 54 */ 66 - errno = 0; 67 - old_prio = getpriority(PRIO_PROCESS, 0); 68 - ASSERT_OK(errno, "getpriority"); 69 - ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); 55 + if (!remote) { 56 + errno = 0; 57 + old_prio = getpriority(PRIO_PROCESS, 0); 58 + ASSERT_OK(errno, "getpriority"); 59 + ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); 60 + } 70 61 71 62 /* notify parent signal handler is installed */ 72 63 ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); ··· 79 66 /* wait a little for signal handler */ 80 67 for (int i = 0; i < 1000000000 && !sigusr1_received; i++) { 81 68 j /= i + j + 1; 82 - if (!attr) 83 - /* trigger the nanosleep tracepoint program. */ 84 - usleep(1); 69 + if (remote) 70 + sleep(1); 71 + else 72 + if (!attr) 73 + /* trigger the nanosleep tracepoint program. */ 74 + usleep(1); 85 75 } 86 76 87 - buf[0] = sigusr1_received ? '2' : '0'; 88 - ASSERT_EQ(sigusr1_received, 1, "sigusr1_received"); 77 + buf[0] = sigusr1_received; 78 + 79 + ASSERT_EQ(sigusr1_received, 8, "sigusr1_received"); 89 80 ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); 90 81 91 82 /* wait for parent notification and exit */ 92 83 ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read"); 93 84 94 85 /* restore the old priority */ 95 - ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); 86 + if (!remote) 87 + ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); 96 88 97 89 close(pipe_c2p[1]); 98 90 close(pipe_p2c[0]); ··· 111 93 if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) 112 94 goto skel_open_load_failure; 113 95 96 + /* boost with a high priority so we got a higher chance 97 + * that if an interrupt happens, the underlying task 98 + * is this process. 99 + */ 100 + if (remote) { 101 + errno = 0; 102 + old_prio = getpriority(PRIO_PROCESS, 0); 103 + ASSERT_OK(errno, "getpriority"); 104 + ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); 105 + } 106 + 114 107 if (!attr) { 115 108 err = test_send_signal_kern__attach(skel); 116 109 if (!ASSERT_OK(err, "skel_attach")) { ··· 129 100 goto destroy_skel; 130 101 } 131 102 } else { 132 - pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, 133 - -1 /* group id */, 0 /* flags */); 103 + if (!remote) 104 + pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, 105 + -1 /* group id */, 0 /* flags */); 106 + else 107 + pmu_fd = syscall(__NR_perf_event_open, attr, getpid(), -1 /* cpu */, 108 + -1 /* group id */, 0 /* flags */); 134 109 if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) { 135 110 err = -1; 136 111 goto destroy_skel; ··· 152 119 /* trigger the bpf send_signal */ 153 120 skel->bss->signal_thread = signal_thread; 154 121 skel->bss->sig = SIGUSR1; 155 - skel->bss->pid = pid; 122 + if (!remote) { 123 + skel->bss->target_pid = 0; 124 + skel->bss->pid = pid; 125 + } else { 126 + skel->bss->target_pid = pid; 127 + skel->bss->pid = getpid(); 128 + } 156 129 157 130 /* notify child that bpf program can send_signal now */ 158 131 ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); 132 + 133 + /* For the remote test, the BPF program is triggered from this 134 + * process but the other process/thread is signaled. 135 + */ 136 + if (remote) { 137 + if (!attr) { 138 + for (int i = 0; i < 10; i++) 139 + usleep(1); 140 + } else { 141 + for (int i = 0; i < 100000000; i++) 142 + j /= i + 1; 143 + } 144 + } 159 145 160 146 /* wait for result */ 161 147 err = read(pipe_c2p[0], buf, 1); ··· 185 133 goto disable_pmu; 186 134 } 187 135 188 - ASSERT_EQ(buf[0], '2', "incorrect result"); 136 + ASSERT_EQ(buf[0], 8, "incorrect result"); 189 137 190 138 /* notify child safe to exit */ 191 139 ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); ··· 194 142 close(pmu_fd); 195 143 destroy_skel: 196 144 test_send_signal_kern__destroy(skel); 145 + /* restore the old priority */ 146 + if (remote) 147 + ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); 197 148 skel_open_load_failure: 198 149 close(pipe_c2p[0]); 199 150 close(pipe_p2c[1]); 200 151 wait(NULL); 201 152 } 202 153 203 - static void test_send_signal_tracepoint(bool signal_thread) 154 + static void test_send_signal_tracepoint(bool signal_thread, bool remote) 204 155 { 205 - test_send_signal_common(NULL, signal_thread); 156 + test_send_signal_common(NULL, signal_thread, remote); 206 157 } 207 158 208 - static void test_send_signal_perf(bool signal_thread) 159 + static void test_send_signal_perf(bool signal_thread, bool remote) 209 160 { 210 161 struct perf_event_attr attr = { 211 162 .freq = 1, ··· 217 162 .config = PERF_COUNT_SW_CPU_CLOCK, 218 163 }; 219 164 220 - test_send_signal_common(&attr, signal_thread); 165 + test_send_signal_common(&attr, signal_thread, remote); 221 166 } 222 167 223 - static void test_send_signal_nmi(bool signal_thread) 168 + static void test_send_signal_nmi(bool signal_thread, bool remote) 224 169 { 225 170 struct perf_event_attr attr = { 226 171 .sample_period = 1, ··· 246 191 close(pmu_fd); 247 192 } 248 193 249 - test_send_signal_common(&attr, signal_thread); 194 + test_send_signal_common(&attr, signal_thread, remote); 250 195 } 251 196 252 197 void test_send_signal(void) 253 198 { 254 199 if (test__start_subtest("send_signal_tracepoint")) 255 - test_send_signal_tracepoint(false); 200 + test_send_signal_tracepoint(false, false); 256 201 if (test__start_subtest("send_signal_perf")) 257 - test_send_signal_perf(false); 202 + test_send_signal_perf(false, false); 258 203 if (test__start_subtest("send_signal_nmi")) 259 - test_send_signal_nmi(false); 204 + test_send_signal_nmi(false, false); 260 205 if (test__start_subtest("send_signal_tracepoint_thread")) 261 - test_send_signal_tracepoint(true); 206 + test_send_signal_tracepoint(true, false); 262 207 if (test__start_subtest("send_signal_perf_thread")) 263 - test_send_signal_perf(true); 208 + test_send_signal_perf(true, false); 264 209 if (test__start_subtest("send_signal_nmi_thread")) 265 - test_send_signal_nmi(true); 210 + test_send_signal_nmi(true, false); 211 + 212 + /* Signal remote thread and thread group */ 213 + if (test__start_subtest("send_signal_tracepoint_remote")) 214 + test_send_signal_tracepoint(false, true); 215 + if (test__start_subtest("send_signal_perf_remote")) 216 + test_send_signal_perf(false, true); 217 + if (test__start_subtest("send_signal_nmi_remote")) 218 + test_send_signal_nmi(false, true); 219 + if (test__start_subtest("send_signal_tracepoint_thread_remote")) 220 + test_send_signal_tracepoint(true, true); 221 + if (test__start_subtest("send_signal_perf_thread_remote")) 222 + test_send_signal_perf(true, true); 223 + if (test__start_subtest("send_signal_nmi_thread_remote")) 224 + test_send_signal_nmi(true, true); 266 225 }
+29 -6
tools/testing/selftests/bpf/progs/test_send_signal_kern.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 // Copyright (c) 2019 Facebook 3 - #include <linux/bpf.h> 3 + #include <vmlinux.h> 4 4 #include <linux/version.h> 5 5 #include <bpf/bpf_helpers.h> 6 6 7 - __u32 sig = 0, pid = 0, status = 0, signal_thread = 0; 7 + struct task_struct *bpf_task_from_pid(int pid) __ksym; 8 + void bpf_task_release(struct task_struct *p) __ksym; 9 + int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, u64 value) __ksym; 10 + 11 + __u32 sig = 0, pid = 0, status = 0, signal_thread = 0, target_pid = 0; 8 12 9 13 static __always_inline int bpf_send_signal_test(void *ctx) 10 14 { 15 + struct task_struct *target_task = NULL; 11 16 int ret; 17 + u64 value; 12 18 13 19 if (status != 0 || pid == 0) 14 20 return 0; 15 21 16 22 if ((bpf_get_current_pid_tgid() >> 32) == pid) { 17 - if (signal_thread) 18 - ret = bpf_send_signal_thread(sig); 19 - else 20 - ret = bpf_send_signal(sig); 23 + if (target_pid) { 24 + target_task = bpf_task_from_pid(target_pid); 25 + if (!target_task) 26 + return 0; 27 + value = 8; 28 + } 29 + 30 + if (signal_thread) { 31 + if (target_pid) 32 + ret = bpf_send_signal_task(target_task, sig, PIDTYPE_PID, value); 33 + else 34 + ret = bpf_send_signal_thread(sig); 35 + } else { 36 + if (target_pid) 37 + ret = bpf_send_signal_task(target_task, sig, PIDTYPE_TGID, value); 38 + else 39 + ret = bpf_send_signal(sig); 40 + } 21 41 if (ret == 0) 22 42 status = 1; 23 43 } 44 + 45 + if (target_task) 46 + bpf_task_release(target_task); 24 47 25 48 return 0; 26 49 }