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