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

selftests/bpf: Add tests for open-coded task and css iter

This patch adds 4 subtests to demonstrate these patterns and validating
correctness.

subtest1:

1) We use task_iter to iterate all process in the system and search for the
current process with a given pid.

2) We create some threads in current process context, and use
BPF_TASK_ITER_PROC_THREADS to iterate all threads of current process. As
expected, we would find all the threads of current process.

3) We create some threads and use BPF_TASK_ITER_ALL_THREADS to iterate all
threads in the system. As expected, we would find all the threads which was
created.

subtest2:

We create a cgroup and add the current task to the cgroup. In the
BPF program, we would use bpf_for_each(css_task, task, css) to iterate all
tasks under the cgroup. As expected, we would find the current process.

subtest3:

1) We create a cgroup tree. In the BPF program, we use
bpf_for_each(css, pos, root, XXX) to iterate all descendant under the root
with pre and post order. As expected, we would find all descendant and the
last iterating cgroup in post-order is root cgroup, the first iterating
cgroup in pre-order is root cgroup.

2) We wse BPF_CGROUP_ITER_ANCESTORS_UP to traverse the cgroup tree starting
from leaf and root separately, and record the height. The diff of the
hights would be the total tree-high - 1.

subtest4:

Add some failure testcase when using css_task, task and css iters, e.g,
unlock when using task-iters to iterate tasks.

Signed-off-by: Chuyi Zhou <zhouchuyi@bytedance.com>
Link: https://lore.kernel.org/r/20231018061746.111364-9-zhouchuyi@bytedance.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Chuyi Zhou and committed by
Alexei Starovoitov
130e0f7a ddab78cb

+415
+150
tools/testing/selftests/bpf/prog_tests/iters.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 3 4 + #include <sys/syscall.h> 5 + #include <sys/mman.h> 6 + #include <sys/wait.h> 7 + #include <unistd.h> 8 + #include <malloc.h> 9 + #include <stdlib.h> 4 10 #include <test_progs.h> 11 + #include "cgroup_helpers.h" 5 12 6 13 #include "iters.skel.h" 7 14 #include "iters_state_safety.skel.h" ··· 16 9 #include "iters_num.skel.h" 17 10 #include "iters_testmod_seq.skel.h" 18 11 #include "iters_task_vma.skel.h" 12 + #include "iters_task.skel.h" 13 + #include "iters_css_task.skel.h" 14 + #include "iters_css.skel.h" 15 + #include "iters_task_failure.skel.h" 19 16 20 17 static void subtest_num_iters(void) 21 18 { ··· 157 146 iters_task_vma__destroy(skel); 158 147 } 159 148 149 + static pthread_mutex_t do_nothing_mutex; 150 + 151 + static void *do_nothing_wait(void *arg) 152 + { 153 + pthread_mutex_lock(&do_nothing_mutex); 154 + pthread_mutex_unlock(&do_nothing_mutex); 155 + 156 + pthread_exit(arg); 157 + } 158 + 159 + #define thread_num 2 160 + 161 + static void subtest_task_iters(void) 162 + { 163 + struct iters_task *skel = NULL; 164 + pthread_t thread_ids[thread_num]; 165 + void *ret; 166 + int err; 167 + 168 + skel = iters_task__open_and_load(); 169 + if (!ASSERT_OK_PTR(skel, "open_and_load")) 170 + goto cleanup; 171 + skel->bss->target_pid = getpid(); 172 + err = iters_task__attach(skel); 173 + if (!ASSERT_OK(err, "iters_task__attach")) 174 + goto cleanup; 175 + pthread_mutex_lock(&do_nothing_mutex); 176 + for (int i = 0; i < thread_num; i++) 177 + ASSERT_OK(pthread_create(&thread_ids[i], NULL, &do_nothing_wait, NULL), 178 + "pthread_create"); 179 + 180 + syscall(SYS_getpgid); 181 + iters_task__detach(skel); 182 + ASSERT_EQ(skel->bss->procs_cnt, 1, "procs_cnt"); 183 + ASSERT_EQ(skel->bss->threads_cnt, thread_num + 1, "threads_cnt"); 184 + ASSERT_EQ(skel->bss->proc_threads_cnt, thread_num + 1, "proc_threads_cnt"); 185 + pthread_mutex_unlock(&do_nothing_mutex); 186 + for (int i = 0; i < thread_num; i++) 187 + ASSERT_OK(pthread_join(thread_ids[i], &ret), "pthread_join"); 188 + cleanup: 189 + iters_task__destroy(skel); 190 + } 191 + 192 + extern int stack_mprotect(void); 193 + 194 + static void subtest_css_task_iters(void) 195 + { 196 + struct iters_css_task *skel = NULL; 197 + int err, cg_fd, cg_id; 198 + const char *cgrp_path = "/cg1"; 199 + 200 + err = setup_cgroup_environment(); 201 + if (!ASSERT_OK(err, "setup_cgroup_environment")) 202 + goto cleanup; 203 + cg_fd = create_and_get_cgroup(cgrp_path); 204 + if (!ASSERT_GE(cg_fd, 0, "create_and_get_cgroup")) 205 + goto cleanup; 206 + cg_id = get_cgroup_id(cgrp_path); 207 + err = join_cgroup(cgrp_path); 208 + if (!ASSERT_OK(err, "join_cgroup")) 209 + goto cleanup; 210 + 211 + skel = iters_css_task__open_and_load(); 212 + if (!ASSERT_OK_PTR(skel, "open_and_load")) 213 + goto cleanup; 214 + 215 + skel->bss->target_pid = getpid(); 216 + skel->bss->cg_id = cg_id; 217 + err = iters_css_task__attach(skel); 218 + if (!ASSERT_OK(err, "iters_task__attach")) 219 + goto cleanup; 220 + err = stack_mprotect(); 221 + if (!ASSERT_EQ(err, -1, "stack_mprotect") || 222 + !ASSERT_EQ(errno, EPERM, "stack_mprotect")) 223 + goto cleanup; 224 + iters_css_task__detach(skel); 225 + ASSERT_EQ(skel->bss->css_task_cnt, 1, "css_task_cnt"); 226 + 227 + cleanup: 228 + cleanup_cgroup_environment(); 229 + iters_css_task__destroy(skel); 230 + } 231 + 232 + static void subtest_css_iters(void) 233 + { 234 + struct iters_css *skel = NULL; 235 + struct { 236 + const char *path; 237 + int fd; 238 + } cgs[] = { 239 + { "/cg1" }, 240 + { "/cg1/cg2" }, 241 + { "/cg1/cg2/cg3" }, 242 + { "/cg1/cg2/cg3/cg4" }, 243 + }; 244 + int err, cg_nr = ARRAY_SIZE(cgs); 245 + int i; 246 + 247 + err = setup_cgroup_environment(); 248 + if (!ASSERT_OK(err, "setup_cgroup_environment")) 249 + goto cleanup; 250 + for (i = 0; i < cg_nr; i++) { 251 + cgs[i].fd = create_and_get_cgroup(cgs[i].path); 252 + if (!ASSERT_GE(cgs[i].fd, 0, "create_and_get_cgroup")) 253 + goto cleanup; 254 + } 255 + 256 + skel = iters_css__open_and_load(); 257 + if (!ASSERT_OK_PTR(skel, "open_and_load")) 258 + goto cleanup; 259 + 260 + skel->bss->target_pid = getpid(); 261 + skel->bss->root_cg_id = get_cgroup_id(cgs[0].path); 262 + skel->bss->leaf_cg_id = get_cgroup_id(cgs[cg_nr - 1].path); 263 + err = iters_css__attach(skel); 264 + 265 + if (!ASSERT_OK(err, "iters_task__attach")) 266 + goto cleanup; 267 + 268 + syscall(SYS_getpgid); 269 + ASSERT_EQ(skel->bss->pre_order_cnt, cg_nr, "pre_order_cnt"); 270 + ASSERT_EQ(skel->bss->first_cg_id, get_cgroup_id(cgs[0].path), "first_cg_id"); 271 + 272 + ASSERT_EQ(skel->bss->post_order_cnt, cg_nr, "post_order_cnt"); 273 + ASSERT_EQ(skel->bss->last_cg_id, get_cgroup_id(cgs[0].path), "last_cg_id"); 274 + ASSERT_EQ(skel->bss->tree_high, cg_nr - 1, "tree_high"); 275 + iters_css__detach(skel); 276 + cleanup: 277 + cleanup_cgroup_environment(); 278 + iters_css__destroy(skel); 279 + } 280 + 160 281 void test_iters(void) 161 282 { 162 283 RUN_TESTS(iters_state_safety); ··· 304 161 subtest_testmod_seq_iters(); 305 162 if (test__start_subtest("task_vma")) 306 163 subtest_task_vma_iters(); 164 + if (test__start_subtest("task")) 165 + subtest_task_iters(); 166 + if (test__start_subtest("css_task")) 167 + subtest_css_task_iters(); 168 + if (test__start_subtest("css")) 169 + subtest_css_iters(); 170 + RUN_TESTS(iters_task_failure); 307 171 }
+72
tools/testing/selftests/bpf/progs/iters_css.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2023 Chuyi Zhou <zhouchuyi@bytedance.com> */ 3 + 4 + #include "vmlinux.h" 5 + #include <bpf/bpf_helpers.h> 6 + #include <bpf/bpf_tracing.h> 7 + #include "bpf_misc.h" 8 + #include "bpf_experimental.h" 9 + 10 + char _license[] SEC("license") = "GPL"; 11 + 12 + pid_t target_pid; 13 + u64 root_cg_id, leaf_cg_id; 14 + u64 first_cg_id, last_cg_id; 15 + 16 + int pre_order_cnt, post_order_cnt, tree_high; 17 + 18 + struct cgroup *bpf_cgroup_from_id(u64 cgid) __ksym; 19 + void bpf_cgroup_release(struct cgroup *p) __ksym; 20 + void bpf_rcu_read_lock(void) __ksym; 21 + void bpf_rcu_read_unlock(void) __ksym; 22 + 23 + SEC("fentry.s/" SYS_PREFIX "sys_getpgid") 24 + int iter_css_for_each(const void *ctx) 25 + { 26 + struct task_struct *cur_task = bpf_get_current_task_btf(); 27 + struct cgroup_subsys_state *root_css, *leaf_css, *pos; 28 + struct cgroup *root_cgrp, *leaf_cgrp, *cur_cgrp; 29 + 30 + if (cur_task->pid != target_pid) 31 + return 0; 32 + 33 + root_cgrp = bpf_cgroup_from_id(root_cg_id); 34 + 35 + if (!root_cgrp) 36 + return 0; 37 + 38 + leaf_cgrp = bpf_cgroup_from_id(leaf_cg_id); 39 + 40 + if (!leaf_cgrp) { 41 + bpf_cgroup_release(root_cgrp); 42 + return 0; 43 + } 44 + root_css = &root_cgrp->self; 45 + leaf_css = &leaf_cgrp->self; 46 + pre_order_cnt = post_order_cnt = tree_high = 0; 47 + first_cg_id = last_cg_id = 0; 48 + 49 + bpf_rcu_read_lock(); 50 + bpf_for_each(css, pos, root_css, BPF_CGROUP_ITER_DESCENDANTS_POST) { 51 + cur_cgrp = pos->cgroup; 52 + post_order_cnt++; 53 + last_cg_id = cur_cgrp->kn->id; 54 + } 55 + 56 + bpf_for_each(css, pos, root_css, BPF_CGROUP_ITER_DESCENDANTS_PRE) { 57 + cur_cgrp = pos->cgroup; 58 + pre_order_cnt++; 59 + if (!first_cg_id) 60 + first_cg_id = cur_cgrp->kn->id; 61 + } 62 + 63 + bpf_for_each(css, pos, leaf_css, BPF_CGROUP_ITER_ANCESTORS_UP) 64 + tree_high++; 65 + 66 + bpf_for_each(css, pos, root_css, BPF_CGROUP_ITER_ANCESTORS_UP) 67 + tree_high--; 68 + bpf_rcu_read_unlock(); 69 + bpf_cgroup_release(root_cgrp); 70 + bpf_cgroup_release(leaf_cgrp); 71 + return 0; 72 + }
+47
tools/testing/selftests/bpf/progs/iters_css_task.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2023 Chuyi Zhou <zhouchuyi@bytedance.com> */ 3 + 4 + #include "vmlinux.h" 5 + #include <errno.h> 6 + #include <bpf/bpf_helpers.h> 7 + #include <bpf/bpf_tracing.h> 8 + #include "bpf_misc.h" 9 + #include "bpf_experimental.h" 10 + 11 + char _license[] SEC("license") = "GPL"; 12 + 13 + struct cgroup *bpf_cgroup_from_id(u64 cgid) __ksym; 14 + void bpf_cgroup_release(struct cgroup *p) __ksym; 15 + 16 + pid_t target_pid; 17 + int css_task_cnt; 18 + u64 cg_id; 19 + 20 + SEC("lsm/file_mprotect") 21 + int BPF_PROG(iter_css_task_for_each, struct vm_area_struct *vma, 22 + unsigned long reqprot, unsigned long prot, int ret) 23 + { 24 + struct task_struct *cur_task = bpf_get_current_task_btf(); 25 + struct cgroup_subsys_state *css; 26 + struct task_struct *task; 27 + struct cgroup *cgrp; 28 + 29 + if (cur_task->pid != target_pid) 30 + return ret; 31 + 32 + cgrp = bpf_cgroup_from_id(cg_id); 33 + 34 + if (!cgrp) 35 + return -EPERM; 36 + 37 + css = &cgrp->self; 38 + css_task_cnt = 0; 39 + 40 + bpf_for_each(css_task, task, css, CSS_TASK_ITER_PROCS) 41 + if (task->pid == target_pid) 42 + css_task_cnt++; 43 + 44 + bpf_cgroup_release(cgrp); 45 + 46 + return -EPERM; 47 + }
+41
tools/testing/selftests/bpf/progs/iters_task.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2023 Chuyi Zhou <zhouchuyi@bytedance.com> */ 3 + 4 + #include "vmlinux.h" 5 + #include <bpf/bpf_helpers.h> 6 + #include <bpf/bpf_tracing.h> 7 + #include "bpf_misc.h" 8 + #include "bpf_experimental.h" 9 + 10 + char _license[] SEC("license") = "GPL"; 11 + 12 + pid_t target_pid; 13 + int procs_cnt, threads_cnt, proc_threads_cnt; 14 + 15 + void bpf_rcu_read_lock(void) __ksym; 16 + void bpf_rcu_read_unlock(void) __ksym; 17 + 18 + SEC("fentry.s/" SYS_PREFIX "sys_getpgid") 19 + int iter_task_for_each_sleep(void *ctx) 20 + { 21 + struct task_struct *cur_task = bpf_get_current_task_btf(); 22 + struct task_struct *pos; 23 + 24 + if (cur_task->pid != target_pid) 25 + return 0; 26 + procs_cnt = threads_cnt = proc_threads_cnt = 0; 27 + 28 + bpf_rcu_read_lock(); 29 + bpf_for_each(task, pos, NULL, BPF_TASK_ITER_ALL_PROCS) 30 + if (pos->pid == target_pid) 31 + procs_cnt++; 32 + 33 + bpf_for_each(task, pos, cur_task, BPF_TASK_ITER_PROC_THREADS) 34 + proc_threads_cnt++; 35 + 36 + bpf_for_each(task, pos, NULL, BPF_TASK_ITER_ALL_THREADS) 37 + if (pos->tgid == target_pid) 38 + threads_cnt++; 39 + bpf_rcu_read_unlock(); 40 + return 0; 41 + }
+105
tools/testing/selftests/bpf/progs/iters_task_failure.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (C) 2023 Chuyi Zhou <zhouchuyi@bytedance.com> */ 3 + 4 + #include "vmlinux.h" 5 + #include <bpf/bpf_helpers.h> 6 + #include <bpf/bpf_tracing.h> 7 + #include "bpf_misc.h" 8 + #include "bpf_experimental.h" 9 + 10 + char _license[] SEC("license") = "GPL"; 11 + 12 + struct cgroup *bpf_cgroup_from_id(u64 cgid) __ksym; 13 + void bpf_cgroup_release(struct cgroup *p) __ksym; 14 + void bpf_rcu_read_lock(void) __ksym; 15 + void bpf_rcu_read_unlock(void) __ksym; 16 + 17 + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 18 + __failure __msg("expected an RCU CS when using bpf_iter_task_next") 19 + int BPF_PROG(iter_tasks_without_lock) 20 + { 21 + struct task_struct *pos; 22 + 23 + bpf_for_each(task, pos, NULL, BPF_TASK_ITER_ALL_PROCS) { 24 + 25 + } 26 + return 0; 27 + } 28 + 29 + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 30 + __failure __msg("expected an RCU CS when using bpf_iter_css_next") 31 + int BPF_PROG(iter_css_without_lock) 32 + { 33 + u64 cg_id = bpf_get_current_cgroup_id(); 34 + struct cgroup *cgrp = bpf_cgroup_from_id(cg_id); 35 + struct cgroup_subsys_state *root_css, *pos; 36 + 37 + if (!cgrp) 38 + return 0; 39 + root_css = &cgrp->self; 40 + 41 + bpf_for_each(css, pos, root_css, BPF_CGROUP_ITER_DESCENDANTS_POST) { 42 + 43 + } 44 + bpf_cgroup_release(cgrp); 45 + return 0; 46 + } 47 + 48 + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 49 + __failure __msg("expected an RCU CS when using bpf_iter_task_next") 50 + int BPF_PROG(iter_tasks_lock_and_unlock) 51 + { 52 + struct task_struct *pos; 53 + 54 + bpf_rcu_read_lock(); 55 + bpf_for_each(task, pos, NULL, BPF_TASK_ITER_ALL_PROCS) { 56 + bpf_rcu_read_unlock(); 57 + 58 + bpf_rcu_read_lock(); 59 + } 60 + bpf_rcu_read_unlock(); 61 + return 0; 62 + } 63 + 64 + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 65 + __failure __msg("expected an RCU CS when using bpf_iter_css_next") 66 + int BPF_PROG(iter_css_lock_and_unlock) 67 + { 68 + u64 cg_id = bpf_get_current_cgroup_id(); 69 + struct cgroup *cgrp = bpf_cgroup_from_id(cg_id); 70 + struct cgroup_subsys_state *root_css, *pos; 71 + 72 + if (!cgrp) 73 + return 0; 74 + root_css = &cgrp->self; 75 + 76 + bpf_rcu_read_lock(); 77 + bpf_for_each(css, pos, root_css, BPF_CGROUP_ITER_DESCENDANTS_POST) { 78 + bpf_rcu_read_unlock(); 79 + 80 + bpf_rcu_read_lock(); 81 + } 82 + bpf_rcu_read_unlock(); 83 + bpf_cgroup_release(cgrp); 84 + return 0; 85 + } 86 + 87 + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") 88 + __failure __msg("css_task_iter is only allowed in bpf_lsm and bpf iter-s") 89 + int BPF_PROG(iter_css_task_for_each) 90 + { 91 + u64 cg_id = bpf_get_current_cgroup_id(); 92 + struct cgroup *cgrp = bpf_cgroup_from_id(cg_id); 93 + struct cgroup_subsys_state *css; 94 + struct task_struct *task; 95 + 96 + if (cgrp == NULL) 97 + return 0; 98 + css = &cgrp->self; 99 + 100 + bpf_for_each(css_task, task, css, CSS_TASK_ITER_PROCS) { 101 + 102 + } 103 + bpf_cgroup_release(cgrp); 104 + return 0; 105 + }