[PATCH bpf-next 3/4] selftests/bpf: add thread_wq cgroup test

"Hui Zhu" <[email protected]>
Newsgroups org.kvack.linux-mm,org.kernel.vger.bpf,org.kernel.vger.cgroups,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.netdev
Message-ID <dbc676924bb41d1e943df252460e5068a4cf71ad.1786086076.git.zhuhui@kylinos.cn>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.