From: Hui Zhu <[email protected]> Add test cases for bpf_thread_wq with cgroup attachment:
- Test thread_wq execution in a specified cgroup and verify callback runs in the target cgroup - Test thread_wq execution without cgroup attachment and verify callback runs in a different cgroup This validates that bpf_thread_wq properly attaches to and executes callbacks within the specified cgroup context. Signed-off-by: Hui Zhu <[email protected]> --- .../bpf/prog_tests/thread_wq_cgroup.c | 87 +++++++++++++++++++ .../selftests/bpf/progs/thread_wq_cgroup.c | 56 ++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/thread_wq_cgroup.c create mode 100644 tools/testing/selftests/bpf/progs/thread_wq_cgroup.c diff --git a/tools/testing/selftests/bpf/prog_tests/thread_wq_cgroup.c b/tools/testing/selftests/bpf/prog_tests/thread_wq_cgroup.c new file mode 100644 index 000000000000..7537b03f17e2 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/thread_wq_cgroup.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include <unistd.h> +#include "cgroup_helpers.h" +#include "thread_wq_cgroup.skel.h" + +#define TEST_CGROUP "/thread_wq_test" +#define WAIT_TIMEOUT_SECS 30 + +void test_thread_wq_cgroup(void) +{ + struct thread_wq_cgroup *skel = NULL; + int err, prog_fd, cg_fd = -1; + unsigned long long cg_id; + int waited_secs; + + LIBBPF_OPTS(bpf_test_run_opts, topts); + + err = setup_cgroup_environment(); + if (!ASSERT_OK(err, "setup_cgroup_environment")) + return; + cg_fd = create_and_get_cgroup(TEST_CGROUP); + if (!ASSERT_GE(cg_fd, 0, "create_and_get_cgroup")) + goto cleanup; + cg_id = get_cgroup_id(TEST_CGROUP); + if (!ASSERT_GT(cg_id, 0ULL, "get_cgroup_id")) + goto cleanup; + + skel = thread_wq_cgroup__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + goto cleanup; + + prog_fd = bpf_program__fd(skel->progs.start_thread_wq); + + /* Run bpf_thread_wq in the specified cgroup. */ + skel->bss->test_key = 0; + skel->bss->target_cgroup_id = cg_id; + skel->bss->callback_cgroup_id = 0; + skel->bss->twq_done = 0; + if (!ASSERT_OK(bpf_prog_test_run_opts(prog_fd, &topts), + "bpf_prog_test_run_opts in cgroup")) + goto cleanup; + if (!ASSERT_OK(topts.retval, "retval in cgroup")) + goto cleanup; + for (waited_secs = 0; waited_secs < WAIT_TIMEOUT_SECS; waited_secs++) { + if (skel->bss->twq_done) + break; + sleep(1); + } + if (!ASSERT_TRUE(skel->bss->twq_done, "twq_done in cgroup")) + goto cleanup; + if (!ASSERT_EQ(skel->bss->callback_cgroup_id, cg_id, + "callback_cgroup_id in cgroup")) + goto cleanup; + + /* Run bpf_thread_wq without cgroup attachment (cgroup_id = 0). */ + LIBBPF_OPTS_RESET(topts); + skel->bss->test_key = 1; + skel->bss->target_cgroup_id = 0; + skel->bss->callback_cgroup_id = 0; + skel->bss->twq_done = 0; + if (!ASSERT_OK(bpf_prog_test_run_opts(prog_fd, &topts), + "bpf_prog_test_run_opts without cgroup")) + goto cleanup; + if (!ASSERT_OK(topts.retval, "retval without cgroup")) + goto cleanup; + for (waited_secs = 0; waited_secs < WAIT_TIMEOUT_SECS; waited_secs++) { + if (skel->bss->twq_done) + break; + sleep(1); + } + if (!ASSERT_TRUE(skel->bss->twq_done, "twq_done without cgroup")) + goto cleanup; + if (!ASSERT_NEQ(skel->bss->callback_cgroup_id, cg_id, + "callback_cgroup_id without cgroup")) + goto cleanup; + +cleanup: + if (skel) { + thread_wq_cgroup__destroy(skel); + /* Wait thread_wq kthread quit. */ + sleep(2); + } + if (cg_fd >= 0) + close(cg_fd); + cleanup_cgroup_environment(); +} diff --git a/tools/testing/selftests/bpf/progs/thread_wq_cgroup.c b/tools/testing/selftests/bpf/progs/thread_wq_cgroup.c new file mode 100644 index 000000000000..c70a37f55397 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/thread_wq_cgroup.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 */ + +#include "bpf_experimental.h" +#include <bpf/bpf_helpers.h> +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +struct elem { + struct bpf_thread_wq twq; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 2); + __type(key, int); + __type(value, struct elem); +} map_arr SEC(".maps"); + +__u64 target_cgroup_id; +__u64 callback_cgroup_id; +int twq_done; +int test_key; + +static int twq_callback(void *map, int *key, void *value) +{ + callback_cgroup_id = bpf_get_current_cgroup_id(); + twq_done = 1; + return 0; +} + +SEC("syscall") +int start_thread_wq(void *ctx) +{ + struct elem *val; + int key = test_key; + int ret; + + val = bpf_map_lookup_elem(&map_arr, &key); + if (!val) + return -1; + + ret = bpf_thread_wq_init(&val->twq, &map_arr, target_cgroup_id, 0); + if (ret) + goto out; + + ret = bpf_thread_wq_set_callback(&val->twq, twq_callback, 0); + if (ret) + goto out; + + ret = bpf_thread_wq_start(&val->twq, 0); + +out: + return ret; +} -- 2.53.0

