212 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <test_progs.h>3#include <sys/time.h>4#include <sys/resource.h>5#include "test_send_signal_kern.skel.h"6 7static int sigusr1_received;8 9static void sigusr1_handler(int signum)10{11 sigusr1_received = 1;12}13 14static void test_send_signal_common(struct perf_event_attr *attr,15 bool signal_thread)16{17 struct test_send_signal_kern *skel;18 int pipe_c2p[2], pipe_p2c[2];19 int err = -1, pmu_fd = -1;20 char buf[256];21 pid_t pid;22 23 if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p"))24 return;25 26 if (!ASSERT_OK(pipe(pipe_p2c), "pipe_p2c")) {27 close(pipe_c2p[0]);28 close(pipe_c2p[1]);29 return;30 }31 32 pid = fork();33 if (!ASSERT_GE(pid, 0, "fork")) {34 close(pipe_c2p[0]);35 close(pipe_c2p[1]);36 close(pipe_p2c[0]);37 close(pipe_p2c[1]);38 return;39 }40 41 if (pid == 0) {42 int old_prio;43 volatile int j = 0;44 45 /* install signal handler and notify parent */46 ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal");47 48 close(pipe_c2p[0]); /* close read */49 close(pipe_p2c[1]); /* close write */50 51 /* boost with a high priority so we got a higher chance52 * that if an interrupt happens, the underlying task53 * is this process.54 */55 errno = 0;56 old_prio = getpriority(PRIO_PROCESS, 0);57 ASSERT_OK(errno, "getpriority");58 ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority");59 60 /* notify parent signal handler is installed */61 ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write");62 63 /* make sure parent enabled bpf program to send_signal */64 ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read");65 66 /* wait a little for signal handler */67 for (int i = 0; i < 1000000000 && !sigusr1_received; i++) {68 j /= i + j + 1;69 if (!attr)70 /* trigger the nanosleep tracepoint program. */71 usleep(1);72 }73 74 buf[0] = sigusr1_received ? '2' : '0';75 ASSERT_EQ(sigusr1_received, 1, "sigusr1_received");76 ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write");77 78 /* wait for parent notification and exit */79 ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read");80 81 /* restore the old priority */82 ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority");83 84 close(pipe_c2p[1]);85 close(pipe_p2c[0]);86 exit(0);87 }88 89 close(pipe_c2p[1]); /* close write */90 close(pipe_p2c[0]); /* close read */91 92 skel = test_send_signal_kern__open_and_load();93 if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))94 goto skel_open_load_failure;95 96 if (!attr) {97 err = test_send_signal_kern__attach(skel);98 if (!ASSERT_OK(err, "skel_attach")) {99 err = -1;100 goto destroy_skel;101 }102 } else {103 pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */,104 -1 /* group id */, 0 /* flags */);105 if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) {106 err = -1;107 goto destroy_skel;108 }109 110 skel->links.send_signal_perf =111 bpf_program__attach_perf_event(skel->progs.send_signal_perf, pmu_fd);112 if (!ASSERT_OK_PTR(skel->links.send_signal_perf, "attach_perf_event"))113 goto disable_pmu;114 }115 116 /* wait until child signal handler installed */117 ASSERT_EQ(read(pipe_c2p[0], buf, 1), 1, "pipe_read");118 119 /* trigger the bpf send_signal */120 skel->bss->signal_thread = signal_thread;121 skel->bss->sig = SIGUSR1;122 skel->bss->pid = pid;123 124 /* notify child that bpf program can send_signal now */125 ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write");126 127 /* wait for result */128 err = read(pipe_c2p[0], buf, 1);129 if (!ASSERT_GE(err, 0, "reading pipe"))130 goto disable_pmu;131 if (!ASSERT_GT(err, 0, "reading pipe error: size 0")) {132 err = -1;133 goto disable_pmu;134 }135 136 ASSERT_EQ(buf[0], '2', "incorrect result");137 138 /* notify child safe to exit */139 ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write");140 141disable_pmu:142 close(pmu_fd);143destroy_skel:144 test_send_signal_kern__destroy(skel);145skel_open_load_failure:146 close(pipe_c2p[0]);147 close(pipe_p2c[1]);148 wait(NULL);149}150 151static void test_send_signal_tracepoint(bool signal_thread)152{153 test_send_signal_common(NULL, signal_thread);154}155 156static void test_send_signal_perf(bool signal_thread)157{158 struct perf_event_attr attr = {159 .freq = 1,160 .sample_freq = 1000,161 .type = PERF_TYPE_SOFTWARE,162 .config = PERF_COUNT_SW_CPU_CLOCK,163 };164 165 test_send_signal_common(&attr, signal_thread);166}167 168static void test_send_signal_nmi(bool signal_thread)169{170 struct perf_event_attr attr = {171 .sample_period = 1,172 .type = PERF_TYPE_HARDWARE,173 .config = PERF_COUNT_HW_CPU_CYCLES,174 };175 int pmu_fd;176 177 /* Some setups (e.g. virtual machines) might run with hardware178 * perf events disabled. If this is the case, skip this test.179 */180 pmu_fd = syscall(__NR_perf_event_open, &attr, 0 /* pid */,181 -1 /* cpu */, -1 /* group_fd */, 0 /* flags */);182 if (pmu_fd == -1) {183 if (errno == ENOENT || errno == EOPNOTSUPP) {184 printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n",185 __func__);186 test__skip();187 return;188 }189 /* Let the test fail with a more informative message */190 } else {191 close(pmu_fd);192 }193 194 test_send_signal_common(&attr, signal_thread);195}196 197void test_send_signal(void)198{199 if (test__start_subtest("send_signal_tracepoint"))200 test_send_signal_tracepoint(false);201 if (test__start_subtest("send_signal_perf"))202 test_send_signal_perf(false);203 if (test__start_subtest("send_signal_nmi"))204 test_send_signal_nmi(false);205 if (test__start_subtest("send_signal_tracepoint_thread"))206 test_send_signal_tracepoint(true);207 if (test__start_subtest("send_signal_perf_thread"))208 test_send_signal_perf(true);209 if (test__start_subtest("send_signal_nmi_thread"))210 test_send_signal_nmi(true);211}212