1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
3 #include <test_progs.h>
8 #include "struct_ops_module.skel.h"
9 #include "struct_ops_nulled_out_cb.skel.h"
10 #include "struct_ops_forgotten_cb.skel.h"
11 #include "struct_ops_detach.skel.h"
12 #include "unsupported_ops.skel.h"
14 static void check_map_info(struct bpf_map_info *info)
16 struct bpf_btf_info btf_info;
18 u32 btf_info_len = sizeof(btf_info);
21 fd = bpf_btf_get_fd_by_id(info->btf_vmlinux_id);
22 if (!ASSERT_GE(fd, 0, "get_value_type_btf_obj_fd"))
25 memset(&btf_info, 0, sizeof(btf_info));
26 btf_info.name = ptr_to_u64(btf_name);
27 btf_info.name_len = sizeof(btf_name);
28 err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_info_len);
29 if (!ASSERT_OK(err, "get_value_type_btf_obj_info"))
32 if (!ASSERT_EQ(strcmp(btf_name, "bpf_testmod"), 0, "get_value_type_btf_obj_name"))
39 static int attach_ops_and_check(struct struct_ops_module *skel,
41 int expected_test_2_result)
43 struct bpf_link *link;
45 link = bpf_map__attach_struct_ops(map);
46 ASSERT_OK_PTR(link, "attach_test_mod_1");
50 /* test_{1,2}() would be called from bpf_dummy_reg() in bpf_testmod.c */
51 ASSERT_EQ(skel->bss->test_1_result, 0xdeadbeef, "test_1_result");
52 ASSERT_EQ(skel->bss->test_2_result, expected_test_2_result, "test_2_result");
54 bpf_link__destroy(link);
58 static void test_struct_ops_load(void)
60 struct struct_ops_module *skel;
61 struct bpf_map_info info = {};
65 skel = struct_ops_module__open();
66 if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
69 skel->struct_ops.testmod_1->data = 13;
70 skel->struct_ops.testmod_1->test_2 = skel->progs.test_3;
71 /* Since test_2() is not being used, it should be disabled from
72 * auto-loading, or it will fail to load.
74 bpf_program__set_autoload(skel->progs.test_2, false);
75 bpf_map__set_autocreate(skel->maps.testmod_zeroed, false);
77 err = struct_ops_module__load(skel);
78 if (!ASSERT_OK(err, "struct_ops_module_load"))
82 err = bpf_map_get_info_by_fd(bpf_map__fd(skel->maps.testmod_1), &info,
84 if (!ASSERT_OK(err, "bpf_map_get_info_by_fd"))
87 check_map_info(&info);
88 /* test_3() will be called from bpf_dummy_reg() in bpf_testmod.c
90 * In bpf_testmod.c it will pass 4 and 13 (the value of data) to
91 * .test_2. So, the value of test_2_result should be 20 (4 + 13 +
94 if (!attach_ops_and_check(skel, skel->maps.testmod_1, 20))
96 if (!attach_ops_and_check(skel, skel->maps.testmod_2, 12))
100 struct_ops_module__destroy(skel);
103 static void test_struct_ops_not_zeroed(void)
105 struct struct_ops_module *skel;
108 /* zeroed is 0, and zeroed_op is null */
109 skel = struct_ops_module__open();
110 if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
113 skel->struct_ops.testmod_zeroed->zeroed = 0;
114 /* zeroed_op prog should be not loaded automatically now */
115 skel->struct_ops.testmod_zeroed->zeroed_op = NULL;
117 err = struct_ops_module__load(skel);
118 ASSERT_OK(err, "struct_ops_module_load");
120 struct_ops_module__destroy(skel);
122 /* zeroed is not 0 */
123 skel = struct_ops_module__open();
124 if (!ASSERT_OK_PTR(skel, "struct_ops_module_open_not_zeroed"))
127 /* libbpf should reject the testmod_zeroed since struct
128 * bpf_testmod_ops in the kernel has no "zeroed" field and the
129 * value of "zeroed" is non-zero.
131 skel->struct_ops.testmod_zeroed->zeroed = 0xdeadbeef;
132 skel->struct_ops.testmod_zeroed->zeroed_op = NULL;
133 err = struct_ops_module__load(skel);
134 ASSERT_ERR(err, "struct_ops_module_load_not_zeroed");
136 struct_ops_module__destroy(skel);
138 /* zeroed_op is not null */
139 skel = struct_ops_module__open();
140 if (!ASSERT_OK_PTR(skel, "struct_ops_module_open_not_zeroed_op"))
143 /* libbpf should reject the testmod_zeroed since the value of its
144 * "zeroed_op" is not null.
146 skel->struct_ops.testmod_zeroed->zeroed_op = skel->progs.test_3;
147 err = struct_ops_module__load(skel);
148 ASSERT_ERR(err, "struct_ops_module_load_not_zeroed_op");
150 struct_ops_module__destroy(skel);
153 /* The signature of an implementation might not match the signature of the
154 * function pointer prototype defined in the BPF program. This mismatch
155 * should be allowed as long as the behavior of the operator program
156 * adheres to the signature in the kernel. Libbpf should not enforce the
157 * signature; rather, let the kernel verifier handle the enforcement.
159 static void test_struct_ops_incompatible(void)
161 struct struct_ops_module *skel;
162 struct bpf_link *link;
165 skel = struct_ops_module__open();
166 if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
169 bpf_map__set_autocreate(skel->maps.testmod_zeroed, false);
171 err = struct_ops_module__load(skel);
172 if (!ASSERT_OK(err, "skel_load"))
175 link = bpf_map__attach_struct_ops(skel->maps.testmod_incompatible);
176 if (ASSERT_OK_PTR(link, "attach_struct_ops"))
177 bpf_link__destroy(link);
180 struct_ops_module__destroy(skel);
183 /* validate that it's ok to "turn off" callback that kernel supports */
184 static void test_struct_ops_nulled_out_cb(void)
186 struct struct_ops_nulled_out_cb *skel;
189 skel = struct_ops_nulled_out_cb__open();
190 if (!ASSERT_OK_PTR(skel, "skel_open"))
193 /* kernel knows about test_1, but we still null it out */
194 skel->struct_ops.ops->test_1 = NULL;
196 err = struct_ops_nulled_out_cb__load(skel);
197 if (!ASSERT_OK(err, "skel_load"))
200 ASSERT_FALSE(bpf_program__autoload(skel->progs.test_1_turn_off), "prog_autoload");
201 ASSERT_LT(bpf_program__fd(skel->progs.test_1_turn_off), 0, "prog_fd");
204 struct_ops_nulled_out_cb__destroy(skel);
207 /* validate that libbpf generates reasonable error message if struct_ops is
208 * not referenced in any struct_ops map
210 static void test_struct_ops_forgotten_cb(void)
212 struct struct_ops_forgotten_cb *skel;
216 skel = struct_ops_forgotten_cb__open();
217 if (!ASSERT_OK_PTR(skel, "skel_open"))
220 start_libbpf_log_capture();
222 err = struct_ops_forgotten_cb__load(skel);
223 if (!ASSERT_ERR(err, "skel_load"))
226 log = stop_libbpf_log_capture();
227 ASSERT_HAS_SUBSTR(log,
228 "prog 'test_1_forgotten': SEC(\"struct_ops\") program isn't referenced anywhere, did you forget to use it?",
232 struct_ops_forgotten_cb__destroy(skel);
234 /* now let's programmatically use it, we should be fine now */
235 skel = struct_ops_forgotten_cb__open();
236 if (!ASSERT_OK_PTR(skel, "skel_open"))
239 skel->struct_ops.ops->test_1 = skel->progs.test_1_forgotten; /* not anymore */
241 err = struct_ops_forgotten_cb__load(skel);
242 if (!ASSERT_OK(err, "skel_load"))
246 struct_ops_forgotten_cb__destroy(skel);
249 /* Detach a link from a user space program */
250 static void test_detach_link(void)
252 struct epoll_event ev, events[2];
253 struct struct_ops_detach *skel;
254 struct bpf_link *link = NULL;
255 int fd, epollfd = -1, nfds;
258 skel = struct_ops_detach__open_and_load();
259 if (!ASSERT_OK_PTR(skel, "struct_ops_detach__open_and_load"))
262 link = bpf_map__attach_struct_ops(skel->maps.testmod_do_detach);
263 if (!ASSERT_OK_PTR(link, "attach_struct_ops"))
266 fd = bpf_link__fd(link);
267 if (!ASSERT_GE(fd, 0, "link_fd"))
270 epollfd = epoll_create1(0);
271 if (!ASSERT_GE(epollfd, 0, "epoll_create1"))
274 ev.events = EPOLLHUP;
276 err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
277 if (!ASSERT_OK(err, "epoll_ctl"))
280 err = bpf_link__detach(link);
281 if (!ASSERT_OK(err, "detach_link"))
284 /* Wait for EPOLLHUP */
285 nfds = epoll_wait(epollfd, events, 2, 500);
286 if (!ASSERT_EQ(nfds, 1, "epoll_wait"))
289 if (!ASSERT_EQ(events[0].data.fd, fd, "epoll_wait_fd"))
291 if (!ASSERT_TRUE(events[0].events & EPOLLHUP, "events[0].events"))
297 bpf_link__destroy(link);
298 struct_ops_detach__destroy(skel);
301 void serial_test_struct_ops_module(void)
303 if (test__start_subtest("struct_ops_load"))
304 test_struct_ops_load();
305 if (test__start_subtest("struct_ops_not_zeroed"))
306 test_struct_ops_not_zeroed();
307 if (test__start_subtest("struct_ops_incompatible"))
308 test_struct_ops_incompatible();
309 if (test__start_subtest("struct_ops_null_out_cb"))
310 test_struct_ops_nulled_out_cb();
311 if (test__start_subtest("struct_ops_forgotten_cb"))
312 test_struct_ops_forgotten_cb();
313 if (test__start_subtest("test_detach_link"))
315 RUN_TESTS(unsupported_ops);