Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add a test for bpf_cgroup_from_id lookup in non-root cgns

Make sure that we only switch the cgroup namespace and enter a new
cgroup in a child process separate from test_progs, to not mess up the
environment for subsequent tests.

To remove this cgroup, we need to wait for the child to exit, and then
rmdir its cgroup. If the read call fails, or waitpid succeeds, we know
the child exited (read call would fail when the last pipe end is closed,
otherwise waitpid waits until exit(2) is called). We then invoke a newly
introduced remove_cgroup_pid() helper, that identifies cgroup path using
the passed in pid of the now dead child, instead of using the current
process pid (getpid()).

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250915032618.1551762-3-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Kumar Kartikeya Dwivedi and committed by
Alexei Starovoitov
a8250d16 2c895133

+104
+20
tools/testing/selftests/bpf/cgroup_helpers.c
··· 412 412 log_err("rmdiring cgroup %s .. %s", relative_path, cgroup_path); 413 413 } 414 414 415 + /* 416 + * remove_cgroup_pid() - Remove a cgroup setup by process identified by PID 417 + * @relative_path: The cgroup path, relative to the workdir, to remove 418 + * @pid: PID to be used to find cgroup_path 419 + * 420 + * This function expects a cgroup to already be created, relative to the cgroup 421 + * work dir. It also expects the cgroup doesn't have any children or live 422 + * processes and it removes the cgroup. 423 + * 424 + * On failure, it will print an error to stderr. 425 + */ 426 + void remove_cgroup_pid(const char *relative_path, int pid) 427 + { 428 + char cgroup_path[PATH_MAX + 1]; 429 + 430 + format_cgroup_path_pid(cgroup_path, relative_path, pid); 431 + if (rmdir(cgroup_path)) 432 + log_err("rmdiring cgroup %s .. %s", relative_path, cgroup_path); 433 + } 434 + 415 435 /** 416 436 * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD 417 437 * @relative_path: The cgroup path, relative to the workdir, to join
+1
tools/testing/selftests/bpf/cgroup_helpers.h
··· 19 19 int get_root_cgroup(void); 20 20 int create_and_get_cgroup(const char *relative_path); 21 21 void remove_cgroup(const char *relative_path); 22 + void remove_cgroup_pid(const char *relative_path, int pid); 22 23 unsigned long long get_cgroup_id(const char *relative_path); 23 24 int get_cgroup1_hierarchy_id(const char *subsys_name); 24 25
+71
tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c
··· 4 4 #define _GNU_SOURCE 5 5 #include <cgroup_helpers.h> 6 6 #include <test_progs.h> 7 + #include <sched.h> 8 + #include <sys/wait.h> 7 9 8 10 #include "cgrp_kfunc_failure.skel.h" 9 11 #include "cgrp_kfunc_success.skel.h" ··· 89 87 "test_cgrp_from_id", 90 88 }; 91 89 90 + static void test_cgrp_from_id_ns(void) 91 + { 92 + LIBBPF_OPTS(bpf_test_run_opts, opts); 93 + struct cgrp_kfunc_success *skel; 94 + struct bpf_program *prog; 95 + int pid, pipe_fd[2]; 96 + 97 + skel = open_load_cgrp_kfunc_skel(); 98 + if (!ASSERT_OK_PTR(skel, "open_load_skel")) 99 + return; 100 + 101 + if (!ASSERT_OK(skel->bss->err, "pre_mkdir_err")) 102 + goto cleanup; 103 + 104 + prog = skel->progs.test_cgrp_from_id_ns; 105 + 106 + if (!ASSERT_OK(pipe(pipe_fd), "pipe")) 107 + goto cleanup; 108 + 109 + pid = fork(); 110 + if (!ASSERT_GE(pid, 0, "fork result")) { 111 + close(pipe_fd[0]); 112 + close(pipe_fd[1]); 113 + goto cleanup; 114 + } 115 + 116 + if (pid == 0) { 117 + int ret = 0; 118 + 119 + close(pipe_fd[0]); 120 + 121 + if (!ASSERT_GE(cgroup_setup_and_join("cgrp_from_id_ns"), 0, "join cgroup")) 122 + exit(1); 123 + 124 + if (!ASSERT_OK(unshare(CLONE_NEWCGROUP), "unshare cgns")) 125 + exit(1); 126 + 127 + ret = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts); 128 + if (!ASSERT_OK(ret, "test run ret")) 129 + exit(1); 130 + 131 + if (!ASSERT_OK(opts.retval, "test run retval")) 132 + exit(1); 133 + 134 + if (!ASSERT_EQ(write(pipe_fd[1], &ret, sizeof(ret)), sizeof(ret), "write pipe")) 135 + exit(1); 136 + 137 + exit(0); 138 + } else { 139 + int res; 140 + 141 + close(pipe_fd[1]); 142 + 143 + ASSERT_EQ(read(pipe_fd[0], &res, sizeof(res)), sizeof(res), "read res"); 144 + ASSERT_EQ(waitpid(pid, NULL, 0), pid, "wait on child"); 145 + 146 + remove_cgroup_pid("cgrp_from_id_ns", pid); 147 + 148 + ASSERT_OK(res, "result from run"); 149 + } 150 + 151 + close(pipe_fd[0]); 152 + cleanup: 153 + cgrp_kfunc_success__destroy(skel); 154 + } 155 + 92 156 void test_cgrp_kfunc(void) 93 157 { 94 158 int i, err; ··· 169 101 170 102 run_success_test(success_tests[i]); 171 103 } 104 + 105 + if (test__start_subtest("test_cgrp_from_id_ns")) 106 + test_cgrp_from_id_ns(); 172 107 173 108 RUN_TESTS(cgrp_kfunc_failure); 174 109
+12
tools/testing/selftests/bpf/progs/cgrp_kfunc_success.c
··· 221 221 222 222 return 0; 223 223 } 224 + 225 + SEC("syscall") 226 + int test_cgrp_from_id_ns(void *ctx) 227 + { 228 + struct cgroup *cg; 229 + 230 + cg = bpf_cgroup_from_id(1); 231 + if (!cg) 232 + return 42; 233 + bpf_cgroup_release(cg); 234 + return 0; 235 + }