Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
  3#include <test_progs.h>
  4#include <time.h>
  5
  6#include <sys/epoll.h>
  7
  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"
 13
 14static void check_map_info(struct bpf_map_info *info)
 15{
 16	struct bpf_btf_info btf_info;
 17	char btf_name[256];
 18	u32 btf_info_len = sizeof(btf_info);
 19	int err, fd;
 20
 21	fd = bpf_btf_get_fd_by_id(info->btf_vmlinux_id);
 22	if (!ASSERT_GE(fd, 0, "get_value_type_btf_obj_fd"))
 23		return;
 24
 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"))
 30		goto cleanup;
 31
 32	if (!ASSERT_EQ(strcmp(btf_name, "bpf_testmod"), 0, "get_value_type_btf_obj_name"))
 33		goto cleanup;
 34
 35cleanup:
 36	close(fd);
 37}
 38
 39static int attach_ops_and_check(struct struct_ops_module *skel,
 40				struct bpf_map *map,
 41				int expected_test_2_result)
 42{
 43	struct bpf_link *link;
 44
 45	link = bpf_map__attach_struct_ops(map);
 46	ASSERT_OK_PTR(link, "attach_test_mod_1");
 47	if (!link)
 48		return -1;
 49
 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");
 53
 54	bpf_link__destroy(link);
 55	return 0;
 56}
 57
 58static void test_struct_ops_load(void)
 59{
 60	struct struct_ops_module *skel;
 61	struct bpf_map_info info = {};
 62	int err;
 63	u32 len;
 64
 65	skel = struct_ops_module__open();
 66	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
 67		return;
 68
 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.
 73	 */
 74	bpf_program__set_autoload(skel->progs.test_2, false);
 75	bpf_map__set_autocreate(skel->maps.testmod_zeroed, false);
 76
 77	err = struct_ops_module__load(skel);
 78	if (!ASSERT_OK(err, "struct_ops_module_load"))
 79		goto cleanup;
 80
 81	len = sizeof(info);
 82	err = bpf_map_get_info_by_fd(bpf_map__fd(skel->maps.testmod_1), &info,
 83				     &len);
 84	if (!ASSERT_OK(err, "bpf_map_get_info_by_fd"))
 85		goto cleanup;
 86
 87	check_map_info(&info);
 88	/* test_3() will be called from bpf_dummy_reg() in bpf_testmod.c
 89	 *
 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 +
 92	 * 3).
 93	 */
 94	if (!attach_ops_and_check(skel, skel->maps.testmod_1, 20))
 95		goto cleanup;
 96	if (!attach_ops_and_check(skel, skel->maps.testmod_2, 12))
 97		goto cleanup;
 98
 99cleanup:
100	struct_ops_module__destroy(skel);
101}
102
103static void test_struct_ops_not_zeroed(void)
104{
105	struct struct_ops_module *skel;
106	int err;
107
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"))
111		return;
112
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;
116
117	err = struct_ops_module__load(skel);
118	ASSERT_OK(err, "struct_ops_module_load");
119
120	struct_ops_module__destroy(skel);
121
122	/* zeroed is not 0 */
123	skel = struct_ops_module__open();
124	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open_not_zeroed"))
125		return;
126
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.
130	 */
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");
135
136	struct_ops_module__destroy(skel);
137
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"))
141		return;
142
143	/* libbpf should reject the testmod_zeroed since the value of its
144	 * "zeroed_op" is not null.
145	 */
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");
149
150	struct_ops_module__destroy(skel);
151}
152
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.
158 */
159static void test_struct_ops_incompatible(void)
160{
161	struct struct_ops_module *skel;
162	struct bpf_link *link;
163	int err;
164
165	skel = struct_ops_module__open();
166	if (!ASSERT_OK_PTR(skel, "struct_ops_module_open"))
167		return;
168
169	bpf_map__set_autocreate(skel->maps.testmod_zeroed, false);
170
171	err = struct_ops_module__load(skel);
172	if (!ASSERT_OK(err, "skel_load"))
173		goto cleanup;
174
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);
178
179cleanup:
180	struct_ops_module__destroy(skel);
181}
182
183/* validate that it's ok to "turn off" callback that kernel supports */
184static void test_struct_ops_nulled_out_cb(void)
185{
186	struct struct_ops_nulled_out_cb *skel;
187	int err;
188
189	skel = struct_ops_nulled_out_cb__open();
190	if (!ASSERT_OK_PTR(skel, "skel_open"))
191		return;
192
193	/* kernel knows about test_1, but we still null it out */
194	skel->struct_ops.ops->test_1 = NULL;
195
196	err = struct_ops_nulled_out_cb__load(skel);
197	if (!ASSERT_OK(err, "skel_load"))
198		goto cleanup;
199
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");
202
203cleanup:
204	struct_ops_nulled_out_cb__destroy(skel);
205}
206
207/* validate that libbpf generates reasonable error message if struct_ops is
208 * not referenced in any struct_ops map
209 */
210static void test_struct_ops_forgotten_cb(void)
211{
212	struct struct_ops_forgotten_cb *skel;
213	char *log;
214	int err;
215
216	skel = struct_ops_forgotten_cb__open();
217	if (!ASSERT_OK_PTR(skel, "skel_open"))
218		return;
219
220	start_libbpf_log_capture();
221
222	err = struct_ops_forgotten_cb__load(skel);
223	if (!ASSERT_ERR(err, "skel_load"))
224		goto cleanup;
225
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?",
229			  "libbpf_log");
230	free(log);
231
232	struct_ops_forgotten_cb__destroy(skel);
233
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"))
237		return;
238
239	skel->struct_ops.ops->test_1 = skel->progs.test_1_forgotten; /* not anymore */
240
241	err = struct_ops_forgotten_cb__load(skel);
242	if (!ASSERT_OK(err, "skel_load"))
243		goto cleanup;
244
245cleanup:
246	struct_ops_forgotten_cb__destroy(skel);
247}
248
249/* Detach a link from a user space program */
250static void test_detach_link(void)
251{
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;
256	int err;
257
258	skel = struct_ops_detach__open_and_load();
259	if (!ASSERT_OK_PTR(skel, "struct_ops_detach__open_and_load"))
260		return;
261
262	link = bpf_map__attach_struct_ops(skel->maps.testmod_do_detach);
263	if (!ASSERT_OK_PTR(link, "attach_struct_ops"))
264		goto cleanup;
265
266	fd = bpf_link__fd(link);
267	if (!ASSERT_GE(fd, 0, "link_fd"))
268		goto cleanup;
269
270	epollfd = epoll_create1(0);
271	if (!ASSERT_GE(epollfd, 0, "epoll_create1"))
272		goto cleanup;
273
274	ev.events = EPOLLHUP;
275	ev.data.fd = fd;
276	err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
277	if (!ASSERT_OK(err, "epoll_ctl"))
278		goto cleanup;
279
280	err = bpf_link__detach(link);
281	if (!ASSERT_OK(err, "detach_link"))
282		goto cleanup;
283
284	/* Wait for EPOLLHUP */
285	nfds = epoll_wait(epollfd, events, 2, 500);
286	if (!ASSERT_EQ(nfds, 1, "epoll_wait"))
287		goto cleanup;
288
289	if (!ASSERT_EQ(events[0].data.fd, fd, "epoll_wait_fd"))
290		goto cleanup;
291	if (!ASSERT_TRUE(events[0].events & EPOLLHUP, "events[0].events"))
292		goto cleanup;
293
294cleanup:
295	if (epollfd >= 0)
296		close(epollfd);
297	bpf_link__destroy(link);
298	struct_ops_detach__destroy(skel);
299}
300
301void serial_test_struct_ops_module(void)
302{
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"))
314		test_detach_link();
315	RUN_TESTS(unsupported_ops);
316}
317