diff options
Diffstat (limited to 'tools/testing/selftests/bpf/prog_tests/send_signal.c')
-rw-r--r-- | tools/testing/selftests/bpf/prog_tests/send_signal.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c new file mode 100644 index 000000000000..67cea1686305 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c | |||
@@ -0,0 +1,198 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | #include <test_progs.h> | ||
3 | |||
4 | static volatile int sigusr1_received = 0; | ||
5 | |||
6 | static void sigusr1_handler(int signum) | ||
7 | { | ||
8 | sigusr1_received++; | ||
9 | } | ||
10 | |||
11 | static int test_send_signal_common(struct perf_event_attr *attr, | ||
12 | int prog_type, | ||
13 | const char *test_name) | ||
14 | { | ||
15 | int err = -1, pmu_fd, prog_fd, info_map_fd, status_map_fd; | ||
16 | const char *file = "./test_send_signal_kern.o"; | ||
17 | struct bpf_object *obj = NULL; | ||
18 | int pipe_c2p[2], pipe_p2c[2]; | ||
19 | __u32 key = 0, duration = 0; | ||
20 | char buf[256]; | ||
21 | pid_t pid; | ||
22 | __u64 val; | ||
23 | |||
24 | if (CHECK(pipe(pipe_c2p), test_name, | ||
25 | "pipe pipe_c2p error: %s\n", strerror(errno))) | ||
26 | goto no_fork_done; | ||
27 | |||
28 | if (CHECK(pipe(pipe_p2c), test_name, | ||
29 | "pipe pipe_p2c error: %s\n", strerror(errno))) { | ||
30 | close(pipe_c2p[0]); | ||
31 | close(pipe_c2p[1]); | ||
32 | goto no_fork_done; | ||
33 | } | ||
34 | |||
35 | pid = fork(); | ||
36 | if (CHECK(pid < 0, test_name, "fork error: %s\n", strerror(errno))) { | ||
37 | close(pipe_c2p[0]); | ||
38 | close(pipe_c2p[1]); | ||
39 | close(pipe_p2c[0]); | ||
40 | close(pipe_p2c[1]); | ||
41 | goto no_fork_done; | ||
42 | } | ||
43 | |||
44 | if (pid == 0) { | ||
45 | /* install signal handler and notify parent */ | ||
46 | signal(SIGUSR1, sigusr1_handler); | ||
47 | |||
48 | close(pipe_c2p[0]); /* close read */ | ||
49 | close(pipe_p2c[1]); /* close write */ | ||
50 | |||
51 | /* notify parent signal handler is installed */ | ||
52 | write(pipe_c2p[1], buf, 1); | ||
53 | |||
54 | /* make sure parent enabled bpf program to send_signal */ | ||
55 | read(pipe_p2c[0], buf, 1); | ||
56 | |||
57 | /* wait a little for signal handler */ | ||
58 | sleep(1); | ||
59 | |||
60 | if (sigusr1_received) | ||
61 | write(pipe_c2p[1], "2", 1); | ||
62 | else | ||
63 | write(pipe_c2p[1], "0", 1); | ||
64 | |||
65 | /* wait for parent notification and exit */ | ||
66 | read(pipe_p2c[0], buf, 1); | ||
67 | |||
68 | close(pipe_c2p[1]); | ||
69 | close(pipe_p2c[0]); | ||
70 | exit(0); | ||
71 | } | ||
72 | |||
73 | close(pipe_c2p[1]); /* close write */ | ||
74 | close(pipe_p2c[0]); /* close read */ | ||
75 | |||
76 | err = bpf_prog_load(file, prog_type, &obj, &prog_fd); | ||
77 | if (CHECK(err < 0, test_name, "bpf_prog_load error: %s\n", | ||
78 | strerror(errno))) | ||
79 | goto prog_load_failure; | ||
80 | |||
81 | pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1, | ||
82 | -1 /* group id */, 0 /* flags */); | ||
83 | if (CHECK(pmu_fd < 0, test_name, "perf_event_open error: %s\n", | ||
84 | strerror(errno))) { | ||
85 | err = -1; | ||
86 | goto close_prog; | ||
87 | } | ||
88 | |||
89 | err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0); | ||
90 | if (CHECK(err < 0, test_name, "ioctl perf_event_ioc_enable error: %s\n", | ||
91 | strerror(errno))) | ||
92 | goto disable_pmu; | ||
93 | |||
94 | err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); | ||
95 | if (CHECK(err < 0, test_name, "ioctl perf_event_ioc_set_bpf error: %s\n", | ||
96 | strerror(errno))) | ||
97 | goto disable_pmu; | ||
98 | |||
99 | err = -1; | ||
100 | info_map_fd = bpf_object__find_map_fd_by_name(obj, "info_map"); | ||
101 | if (CHECK(info_map_fd < 0, test_name, "find map %s error\n", "info_map")) | ||
102 | goto disable_pmu; | ||
103 | |||
104 | status_map_fd = bpf_object__find_map_fd_by_name(obj, "status_map"); | ||
105 | if (CHECK(status_map_fd < 0, test_name, "find map %s error\n", "status_map")) | ||
106 | goto disable_pmu; | ||
107 | |||
108 | /* wait until child signal handler installed */ | ||
109 | read(pipe_c2p[0], buf, 1); | ||
110 | |||
111 | /* trigger the bpf send_signal */ | ||
112 | key = 0; | ||
113 | val = (((__u64)(SIGUSR1)) << 32) | pid; | ||
114 | bpf_map_update_elem(info_map_fd, &key, &val, 0); | ||
115 | |||
116 | /* notify child that bpf program can send_signal now */ | ||
117 | write(pipe_p2c[1], buf, 1); | ||
118 | |||
119 | /* wait for result */ | ||
120 | err = read(pipe_c2p[0], buf, 1); | ||
121 | if (CHECK(err < 0, test_name, "reading pipe error: %s\n", strerror(errno))) | ||
122 | goto disable_pmu; | ||
123 | if (CHECK(err == 0, test_name, "reading pipe error: size 0\n")) { | ||
124 | err = -1; | ||
125 | goto disable_pmu; | ||
126 | } | ||
127 | |||
128 | err = CHECK(buf[0] != '2', test_name, "incorrect result\n"); | ||
129 | |||
130 | /* notify child safe to exit */ | ||
131 | write(pipe_p2c[1], buf, 1); | ||
132 | |||
133 | disable_pmu: | ||
134 | close(pmu_fd); | ||
135 | close_prog: | ||
136 | bpf_object__close(obj); | ||
137 | prog_load_failure: | ||
138 | close(pipe_c2p[0]); | ||
139 | close(pipe_p2c[1]); | ||
140 | wait(NULL); | ||
141 | no_fork_done: | ||
142 | return err; | ||
143 | } | ||
144 | |||
145 | static int test_send_signal_tracepoint(void) | ||
146 | { | ||
147 | const char *id_path = "/sys/kernel/debug/tracing/events/syscalls/sys_enter_nanosleep/id"; | ||
148 | struct perf_event_attr attr = { | ||
149 | .type = PERF_TYPE_TRACEPOINT, | ||
150 | .sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN, | ||
151 | .sample_period = 1, | ||
152 | .wakeup_events = 1, | ||
153 | }; | ||
154 | __u32 duration = 0; | ||
155 | int bytes, efd; | ||
156 | char buf[256]; | ||
157 | |||
158 | efd = open(id_path, O_RDONLY, 0); | ||
159 | if (CHECK(efd < 0, "tracepoint", | ||
160 | "open syscalls/sys_enter_nanosleep/id failure: %s\n", | ||
161 | strerror(errno))) | ||
162 | return -1; | ||
163 | |||
164 | bytes = read(efd, buf, sizeof(buf)); | ||
165 | close(efd); | ||
166 | if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "tracepoint", | ||
167 | "read syscalls/sys_enter_nanosleep/id failure: %s\n", | ||
168 | strerror(errno))) | ||
169 | return -1; | ||
170 | |||
171 | attr.config = strtol(buf, NULL, 0); | ||
172 | |||
173 | return test_send_signal_common(&attr, BPF_PROG_TYPE_TRACEPOINT, "tracepoint"); | ||
174 | } | ||
175 | |||
176 | static int test_send_signal_nmi(void) | ||
177 | { | ||
178 | struct perf_event_attr attr = { | ||
179 | .sample_freq = 50, | ||
180 | .freq = 1, | ||
181 | .type = PERF_TYPE_HARDWARE, | ||
182 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
183 | }; | ||
184 | |||
185 | return test_send_signal_common(&attr, BPF_PROG_TYPE_PERF_EVENT, "perf_event"); | ||
186 | } | ||
187 | |||
188 | void test_send_signal(void) | ||
189 | { | ||
190 | int ret = 0; | ||
191 | |||
192 | ret |= test_send_signal_tracepoint(); | ||
193 | ret |= test_send_signal_nmi(); | ||
194 | if (!ret) | ||
195 | printf("test_send_signal:OK\n"); | ||
196 | else | ||
197 | printf("test_send_signal:FAIL\n"); | ||
198 | } | ||