xref: /linux/tools/testing/selftests/bpf/prog_tests/libarena.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1b1487dc1SEmil Tsalapatis // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
2b1487dc1SEmil Tsalapatis /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3b1487dc1SEmil Tsalapatis #include <test_progs.h>
4b1487dc1SEmil Tsalapatis #include <unistd.h>
5b1487dc1SEmil Tsalapatis 
6b1487dc1SEmil Tsalapatis #include <libarena/common.h>
7b1487dc1SEmil Tsalapatis #include <libarena/asan.h>
8b1487dc1SEmil Tsalapatis #include <libarena/buddy.h>
9b1487dc1SEmil Tsalapatis #include <libarena/userspace.h>
10b1487dc1SEmil Tsalapatis 
11b1487dc1SEmil Tsalapatis #include "libarena/libarena.skel.h"
12b1487dc1SEmil Tsalapatis 
run_libarena_test(struct libarena * skel,struct bpf_program * prog,const char * name)13b1487dc1SEmil Tsalapatis static void run_libarena_test(struct libarena *skel, struct bpf_program *prog,
14b1487dc1SEmil Tsalapatis 		const char *name)
15b1487dc1SEmil Tsalapatis {
16b1487dc1SEmil Tsalapatis 	int ret;
17b1487dc1SEmil Tsalapatis 
1814c2b770SEmil Tsalapatis 	if (strstr(name, "test_buddy")) {
1914c2b770SEmil Tsalapatis 		/* Buddy tests initialize the allocator directly. */
2014c2b770SEmil Tsalapatis 		ret = libarena_run_prog(bpf_program__fd(skel->progs.arena_buddy_destroy));
2114c2b770SEmil Tsalapatis 		if (!ASSERT_OK(ret, "arena_buddy_destroy"))
2214c2b770SEmil Tsalapatis 			return;
2314c2b770SEmil Tsalapatis 	} else {
24b1487dc1SEmil Tsalapatis 		ret = libarena_run_prog(bpf_program__fd(skel->progs.arena_buddy_reset));
25b1487dc1SEmil Tsalapatis 		if (!ASSERT_OK(ret, "arena_buddy_reset"))
26b1487dc1SEmil Tsalapatis 			return;
27b1487dc1SEmil Tsalapatis 	}
28b1487dc1SEmil Tsalapatis 
29b1487dc1SEmil Tsalapatis 	ret = libarena_run_prog(bpf_program__fd(prog));
30b1487dc1SEmil Tsalapatis 
31b1487dc1SEmil Tsalapatis 	ASSERT_OK(ret, name);
32b1487dc1SEmil Tsalapatis }
33b1487dc1SEmil Tsalapatis 
run_libarena_parallel_prog(void * arg)3442998f81SEmil Tsalapatis static void *run_libarena_parallel_prog(void *arg)
3542998f81SEmil Tsalapatis {
3642998f81SEmil Tsalapatis 	struct bpf_program *prog = arg;
3742998f81SEmil Tsalapatis 
3842998f81SEmil Tsalapatis 	return (void *)(long)libarena_run_prog(bpf_program__fd(prog));
3942998f81SEmil Tsalapatis }
4042998f81SEmil Tsalapatis 
4142998f81SEmil Tsalapatis /* Max suffix is ceil((lg 2^32) / (lg 10)) + sizeof("__") = 10 + 2 = 12. */
4242998f81SEmil Tsalapatis #define MAX_PARTEST_SUFFIX (12)
4342998f81SEmil Tsalapatis #define MAX_PARTEST_NAME (1024)
4442998f81SEmil Tsalapatis #define MAX_PARTEST_PREFIX (MAX_PARTEST_NAME - MAX_PARTEST_SUFFIX)
4542998f81SEmil Tsalapatis 
run_libarena_parallel_fini(struct libarena * skel,const char * name,size_t prefixlen)4642998f81SEmil Tsalapatis static int run_libarena_parallel_fini(struct libarena *skel, const char *name,
4742998f81SEmil Tsalapatis 				      size_t prefixlen)
4842998f81SEmil Tsalapatis {
4942998f81SEmil Tsalapatis 	char tdname[MAX_PARTEST_NAME];
5042998f81SEmil Tsalapatis 	struct bpf_program *fini_prog;
5142998f81SEmil Tsalapatis 	int ret;
5242998f81SEmil Tsalapatis 
5342998f81SEmil Tsalapatis 	ret = snprintf(tdname, sizeof(tdname), "%.*s__fini", (int)prefixlen, name);
5442998f81SEmil Tsalapatis 	if (!ASSERT_LT(ret, sizeof(tdname), "partest fini name"))
5542998f81SEmil Tsalapatis 		return -ENAMETOOLONG;
5642998f81SEmil Tsalapatis 
5742998f81SEmil Tsalapatis 	fini_prog = bpf_object__find_program_by_name(skel->obj, tdname);
5842998f81SEmil Tsalapatis 	if (!ASSERT_TRUE(fini_prog, "partest fini prog"))
5942998f81SEmil Tsalapatis 		return -ENOENT;
6042998f81SEmil Tsalapatis 
6142998f81SEmil Tsalapatis 	ret = libarena_run_prog(bpf_program__fd(fini_prog));
6242998f81SEmil Tsalapatis 	ASSERT_OK(ret, tdname);
6342998f81SEmil Tsalapatis 
6442998f81SEmil Tsalapatis 	return ret;
6542998f81SEmil Tsalapatis }
6642998f81SEmil Tsalapatis 
run_libarena_parallel_test_workers(struct libarena * skel,const char * name,size_t prefixlen)6742998f81SEmil Tsalapatis static int run_libarena_parallel_test_workers(struct libarena *skel,
6842998f81SEmil Tsalapatis 		const char *name, size_t prefixlen)
6942998f81SEmil Tsalapatis {
7042998f81SEmil Tsalapatis 	pthread_t *threads = NULL, *tmp_threads;
7142998f81SEmil Tsalapatis 	char tdname[MAX_PARTEST_NAME];
7242998f81SEmil Tsalapatis 	struct bpf_program *tdprog;
7342998f81SEmil Tsalapatis 	uint32_t nthreads;
7442998f81SEmil Tsalapatis 	void *thread_ret;
7542998f81SEmil Tsalapatis 	int ret, err = 0;
76*fdec474cSJiayuan Chen 	int worker_err;
7742998f81SEmil Tsalapatis 	int i;
7842998f81SEmil Tsalapatis 
7942998f81SEmil Tsalapatis 	for (nthreads = 0; nthreads < UINT_MAX; nthreads++) {
8042998f81SEmil Tsalapatis 		ret = snprintf(tdname, sizeof(tdname), "%.*s__%u", (int)prefixlen,
8142998f81SEmil Tsalapatis 			       name, nthreads);
8242998f81SEmil Tsalapatis 		if (!ASSERT_LT(ret, sizeof(tdname), "test worker name")) {
8342998f81SEmil Tsalapatis 			err = -ENAMETOOLONG;
8442998f81SEmil Tsalapatis 			break;
8542998f81SEmil Tsalapatis 		}
8642998f81SEmil Tsalapatis 
8742998f81SEmil Tsalapatis 		/*
8842998f81SEmil Tsalapatis 		 * We enumerate the worker threads for a given test with __0, __1,
8942998f81SEmil Tsalapatis 		 * and so on. The suffixes always start from 0 and are contiguous,
9042998f81SEmil Tsalapatis 		 * so if we don't find a program with the requested name we have
9142998f81SEmil Tsalapatis 		 * discovered all available worker programs.
9242998f81SEmil Tsalapatis 		 */
9342998f81SEmil Tsalapatis 		tdprog = bpf_object__find_program_by_name(skel->obj, tdname);
9442998f81SEmil Tsalapatis 		if (!tdprog)
9542998f81SEmil Tsalapatis 			break;
9642998f81SEmil Tsalapatis 
9742998f81SEmil Tsalapatis 		/* Bump the alloc array to accommodate the new thread. */
9842998f81SEmil Tsalapatis 		tmp_threads = realloc(threads, (nthreads + 1) * sizeof(*threads));
9942998f81SEmil Tsalapatis 		if (!ASSERT_TRUE(tmp_threads, "realloc")) {
10042998f81SEmil Tsalapatis 			err = -ENOMEM;
10142998f81SEmil Tsalapatis 			break;
10242998f81SEmil Tsalapatis 		}
10342998f81SEmil Tsalapatis 		threads = tmp_threads;
10442998f81SEmil Tsalapatis 
10542998f81SEmil Tsalapatis 		ret = pthread_create(&threads[nthreads], NULL,
10642998f81SEmil Tsalapatis 				     run_libarena_parallel_prog,
10742998f81SEmil Tsalapatis 				     tdprog);
10842998f81SEmil Tsalapatis 		if (!ASSERT_OK(ret, "pthread_create")) {
10942998f81SEmil Tsalapatis 			err = ret;
11042998f81SEmil Tsalapatis 			break;
11142998f81SEmil Tsalapatis 		}
11242998f81SEmil Tsalapatis 	}
11342998f81SEmil Tsalapatis 
11442998f81SEmil Tsalapatis 
11542998f81SEmil Tsalapatis 	for (i = 0; i < nthreads; i++) {
11642998f81SEmil Tsalapatis 		ret = pthread_join(threads[i], &thread_ret);
11742998f81SEmil Tsalapatis 		if (!ASSERT_OK(ret, "pthread_join")) {
11842998f81SEmil Tsalapatis 			err = err ?: ret;
11942998f81SEmil Tsalapatis 			continue;
12042998f81SEmil Tsalapatis 		}
12142998f81SEmil Tsalapatis 
122*fdec474cSJiayuan Chen 		worker_err = (long)thread_ret;
123*fdec474cSJiayuan Chen 
124*fdec474cSJiayuan Chen 		/*
125*fdec474cSJiayuan Chen 		 * A worker that bails out because another one already gave up
126*fdec474cSJiayuan Chen 		 * reports -EINTR. It is collateral damage that carries no
127*fdec474cSJiayuan Chen 		 * information, so skip it entirely: never let it become the
128*fdec474cSJiayuan Chen 		 * reported error, and don't log it either.
129*fdec474cSJiayuan Chen 		 */
130*fdec474cSJiayuan Chen 		if (!worker_err || worker_err == -EINTR)
131*fdec474cSJiayuan Chen 			continue;
132*fdec474cSJiayuan Chen 
133*fdec474cSJiayuan Chen 		if (!err)
134*fdec474cSJiayuan Chen 			err = worker_err;
135*fdec474cSJiayuan Chen 
136*fdec474cSJiayuan Chen 		fprintf(stdout, "%.*s__%d returned %d\n", (int)prefixlen, name,
137*fdec474cSJiayuan Chen 			i, worker_err);
13842998f81SEmil Tsalapatis 	}
13942998f81SEmil Tsalapatis 
14042998f81SEmil Tsalapatis 	free(threads);
14142998f81SEmil Tsalapatis 
14242998f81SEmil Tsalapatis 	return err;
14342998f81SEmil Tsalapatis }
14442998f81SEmil Tsalapatis 
libarena_parallel_test_enabled(struct libarena * skel,const char * prefix,size_t prefixlen)14542998f81SEmil Tsalapatis static bool libarena_parallel_test_enabled(struct libarena *skel,
14642998f81SEmil Tsalapatis 					   const char *prefix,
14742998f81SEmil Tsalapatis 					   size_t prefixlen)
14842998f81SEmil Tsalapatis {
14942998f81SEmil Tsalapatis 	struct bpf_program *prog;
15042998f81SEmil Tsalapatis 	char progname[MAX_PARTEST_NAME];
15142998f81SEmil Tsalapatis 	int ret;
15242998f81SEmil Tsalapatis 
15342998f81SEmil Tsalapatis 	ret = snprintf(progname, sizeof(progname), "%.*s__enabled", (int)prefixlen,
15442998f81SEmil Tsalapatis 		       prefix);
15542998f81SEmil Tsalapatis 	if (!ASSERT_LT(ret, sizeof(progname), "partest enabled name"))
15642998f81SEmil Tsalapatis 		return false;
15742998f81SEmil Tsalapatis 
15842998f81SEmil Tsalapatis 	prog = bpf_object__find_program_by_name(skel->obj, progname);
15942998f81SEmil Tsalapatis 	if (!prog)
16042998f81SEmil Tsalapatis 		return true;
16142998f81SEmil Tsalapatis 
16242998f81SEmil Tsalapatis 	ret = libarena_run_prog(bpf_program__fd(prog));
16342998f81SEmil Tsalapatis 	if (ret == -EOPNOTSUPP)
16442998f81SEmil Tsalapatis 		return false;
16542998f81SEmil Tsalapatis 	if (!ASSERT_OK(ret, progname))
16642998f81SEmil Tsalapatis 		return false;
16742998f81SEmil Tsalapatis 	return true;
16842998f81SEmil Tsalapatis }
16942998f81SEmil Tsalapatis 
run_libarena_parallel_test(struct libarena * skel,struct bpf_program * prog,const char * name)17042998f81SEmil Tsalapatis static void run_libarena_parallel_test(struct libarena *skel, struct bpf_program *prog,
17142998f81SEmil Tsalapatis 		const char *name)
17242998f81SEmil Tsalapatis {
17342998f81SEmil Tsalapatis 	char testname[MAX_PARTEST_NAME];
17442998f81SEmil Tsalapatis 	size_t prefixlen;
17542998f81SEmil Tsalapatis 	const char *pos;
17642998f81SEmil Tsalapatis 	int ret;
17742998f81SEmil Tsalapatis 
17842998f81SEmil Tsalapatis 	/*
17942998f81SEmil Tsalapatis 	 * We annotate the initialization prog with __init. If the current prog does
18042998f81SEmil Tsalapatis 	 * not match, it is one of the parallel threads instead and is ignored.
18142998f81SEmil Tsalapatis 	 *
18242998f81SEmil Tsalapatis 	 * We assume the test writer knows what they are doing and do not add __init
18342998f81SEmil Tsalapatis 	 * randomly in the middle of a test name.
18442998f81SEmil Tsalapatis 	 */
18542998f81SEmil Tsalapatis 	pos = strstr(name, "__init");
18642998f81SEmil Tsalapatis 	if (!pos)
18742998f81SEmil Tsalapatis 		return;
18842998f81SEmil Tsalapatis 
18942998f81SEmil Tsalapatis 	prefixlen = pos - name;
19042998f81SEmil Tsalapatis 	if (!ASSERT_LT(prefixlen, MAX_PARTEST_PREFIX, "partest prefix too long"))
19142998f81SEmil Tsalapatis 		return;
19242998f81SEmil Tsalapatis 
19342998f81SEmil Tsalapatis 	/* The name of the test without the __init suffix. Looks nicer in the test log. */
19442998f81SEmil Tsalapatis 	ret = snprintf(testname, sizeof(testname), "%.*s", (int)prefixlen, name);
19542998f81SEmil Tsalapatis 	if (!ASSERT_LT(ret, sizeof(testname), "partest test name"))
19642998f81SEmil Tsalapatis 		return;
19742998f81SEmil Tsalapatis 
19842998f81SEmil Tsalapatis 	if (!test__start_subtest(testname))
19942998f81SEmil Tsalapatis 		return;
20042998f81SEmil Tsalapatis 
20142998f81SEmil Tsalapatis 	if (!libarena_parallel_test_enabled(skel, testname, prefixlen)) {
20242998f81SEmil Tsalapatis 		test__skip();
20342998f81SEmil Tsalapatis 		return;
20442998f81SEmil Tsalapatis 	}
20542998f81SEmil Tsalapatis 
20642998f81SEmil Tsalapatis 	ret = libarena_run_prog(bpf_program__fd(skel->progs.arena_buddy_reset));
20742998f81SEmil Tsalapatis 	if (!ASSERT_OK(ret, "arena_buddy_reset"))
20842998f81SEmil Tsalapatis 		return;
20942998f81SEmil Tsalapatis 
21042998f81SEmil Tsalapatis 	ret = libarena_run_prog(bpf_program__fd(prog));
21142998f81SEmil Tsalapatis 	if (!ASSERT_OK(ret, testname))
21242998f81SEmil Tsalapatis 		return;
21342998f81SEmil Tsalapatis 
21442998f81SEmil Tsalapatis 	ret = run_libarena_parallel_test_workers(skel, name, prefixlen);
21542998f81SEmil Tsalapatis 
21642998f81SEmil Tsalapatis 	ASSERT_OK(ret, testname);
21742998f81SEmil Tsalapatis 
21842998f81SEmil Tsalapatis 	run_libarena_parallel_fini(skel, name, prefixlen);
21942998f81SEmil Tsalapatis }
22042998f81SEmil Tsalapatis 
serial_test_libarena(void)221a14f9ba0SJiayuan Chen void serial_test_libarena(void)
222b1487dc1SEmil Tsalapatis {
223b1487dc1SEmil Tsalapatis 	struct arena_alloc_reserve_args args;
224b1487dc1SEmil Tsalapatis 	struct libarena *skel;
225b1487dc1SEmil Tsalapatis 	struct bpf_program *prog;
226b1487dc1SEmil Tsalapatis 	int ret;
227b1487dc1SEmil Tsalapatis 
228b1487dc1SEmil Tsalapatis 	skel = libarena__open_and_load();
229b1487dc1SEmil Tsalapatis 	if (!ASSERT_OK_PTR(skel, "open_and_load"))
230b1487dc1SEmil Tsalapatis 		return;
231b1487dc1SEmil Tsalapatis 
232b1487dc1SEmil Tsalapatis 	ret = libarena__attach(skel);
233b1487dc1SEmil Tsalapatis 	if (!ASSERT_OK(ret, "attach"))
234b1487dc1SEmil Tsalapatis 		goto out;
235b1487dc1SEmil Tsalapatis 
236b1487dc1SEmil Tsalapatis 	args.nr_pages = ARENA_RESERVE_PAGES_DFL;
237b1487dc1SEmil Tsalapatis 
238b1487dc1SEmil Tsalapatis 	ret = libarena_run_prog_args(bpf_program__fd(skel->progs.arena_alloc_reserve),
239b1487dc1SEmil Tsalapatis 			&args, sizeof(args));
240b1487dc1SEmil Tsalapatis 	if (!ASSERT_OK(ret, "arena_alloc_reserve"))
241b1487dc1SEmil Tsalapatis 		goto out;
242b1487dc1SEmil Tsalapatis 
243b1487dc1SEmil Tsalapatis 	bpf_object__for_each_program(prog, skel->obj) {
244b1487dc1SEmil Tsalapatis 		const char *name = bpf_program__name(prog);
245b1487dc1SEmil Tsalapatis 
24642998f81SEmil Tsalapatis 		/*
24742998f81SEmil Tsalapatis 		 * Handle parallel test progs separately. For those
24842998f81SEmil Tsalapatis 		 * progs it's not a matter of test/skip, because each
24942998f81SEmil Tsalapatis 		 * parallel test prog includes an initialization prog
25042998f81SEmil Tsalapatis 		 * and a set of progs to be run in parallel. For the
25142998f81SEmil Tsalapatis 		 * latter we do not record them as skipped or run,
25242998f81SEmil Tsalapatis 		 * because we run them all at once when we come across
25342998f81SEmil Tsalapatis 		 * the initialization prog. For more details on how we
25442998f81SEmil Tsalapatis 		 * discover the progs see the comment on
25542998f81SEmil Tsalapatis 		 * run_libarena_parallel_test.
25642998f81SEmil Tsalapatis 		 */
25742998f81SEmil Tsalapatis 		if (libarena_is_parallel_test_prog(name)) {
25842998f81SEmil Tsalapatis 			run_libarena_parallel_test(skel, prog, name);
25942998f81SEmil Tsalapatis 			continue;
26042998f81SEmil Tsalapatis 		}
26142998f81SEmil Tsalapatis 
262b1487dc1SEmil Tsalapatis 		if (!libarena_is_test_prog(name))
263b1487dc1SEmil Tsalapatis 			continue;
264b1487dc1SEmil Tsalapatis 
265b1487dc1SEmil Tsalapatis 		if (!test__start_subtest(name))
266b1487dc1SEmil Tsalapatis 			continue;
267b1487dc1SEmil Tsalapatis 
268b1487dc1SEmil Tsalapatis 		run_libarena_test(skel, prog, name);
269b1487dc1SEmil Tsalapatis 	}
270b1487dc1SEmil Tsalapatis 
271b1487dc1SEmil Tsalapatis out:
272b1487dc1SEmil Tsalapatis 	libarena__destroy(skel);
273b1487dc1SEmil Tsalapatis }
274