1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2022 Google */ 3 4 #include <test_progs.h> 5 #include <bpf/libbpf.h> 6 #include <bpf/btf.h> 7 #include "iters_css_task.skel.h" 8 #include "cgroup_iter.skel.h" 9 #include "cgroup_helpers.h" 10 11 #define ROOT 0 12 #define PARENT 1 13 #define CHILD1 2 14 #define CHILD2 3 15 #define NUM_CGROUPS 4 16 17 #define PROLOGUE "prologue\n" 18 #define EPILOGUE "epilogue\n" 19 20 static const char *cg_path[] = { 21 "/", "/parent", "/parent/child1", "/parent/child2" 22 }; 23 24 static int cg_fd[] = {-1, -1, -1, -1}; 25 static unsigned long long cg_id[] = {0, 0, 0, 0}; 26 static char expected_output[64]; 27 28 static int setup_cgroups(void) 29 { 30 int fd, i = 0; 31 32 for (i = 0; i < NUM_CGROUPS; i++) { 33 fd = create_and_get_cgroup(cg_path[i]); 34 if (fd < 0) 35 return fd; 36 37 cg_fd[i] = fd; 38 cg_id[i] = get_cgroup_id(cg_path[i]); 39 } 40 return 0; 41 } 42 43 static void cleanup_cgroups(void) 44 { 45 int i; 46 47 for (i = 0; i < NUM_CGROUPS; i++) 48 close(cg_fd[i]); 49 } 50 51 static void read_from_cgroup_iter(struct bpf_program *prog, int cgroup_fd, 52 int order, const char *testname) 53 { 54 DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 55 union bpf_iter_link_info linfo; 56 struct bpf_link *link; 57 int len, iter_fd; 58 static char buf[128]; 59 size_t left; 60 char *p; 61 62 memset(&linfo, 0, sizeof(linfo)); 63 linfo.cgroup.cgroup_fd = cgroup_fd; 64 linfo.cgroup.order = order; 65 opts.link_info = &linfo; 66 opts.link_info_len = sizeof(linfo); 67 68 link = bpf_program__attach_iter(prog, &opts); 69 if (!ASSERT_OK_PTR(link, "attach_iter")) 70 return; 71 72 iter_fd = bpf_iter_create(bpf_link__fd(link)); 73 if (iter_fd < 0) 74 goto free_link; 75 76 memset(buf, 0, sizeof(buf)); 77 left = ARRAY_SIZE(buf); 78 p = buf; 79 while ((len = read(iter_fd, p, left)) > 0) { 80 p += len; 81 left -= len; 82 } 83 84 ASSERT_STREQ(buf, expected_output, testname); 85 86 /* read() after iter finishes should be ok. */ 87 if (len == 0) 88 ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read"); 89 90 close(iter_fd); 91 free_link: 92 bpf_link__destroy(link); 93 } 94 95 /* Invalid cgroup. */ 96 static void test_invalid_cgroup(struct cgroup_iter *skel) 97 { 98 DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 99 union bpf_iter_link_info linfo; 100 struct bpf_link *link; 101 102 memset(&linfo, 0, sizeof(linfo)); 103 linfo.cgroup.cgroup_fd = (__u32)-1; 104 opts.link_info = &linfo; 105 opts.link_info_len = sizeof(linfo); 106 107 link = bpf_program__attach_iter(skel->progs.cgroup_id_printer, &opts); 108 ASSERT_ERR_PTR(link, "attach_iter"); 109 bpf_link__destroy(link); 110 } 111 112 /* Specifying both cgroup_fd and cgroup_id is invalid. */ 113 static void test_invalid_cgroup_spec(struct cgroup_iter *skel) 114 { 115 DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 116 union bpf_iter_link_info linfo; 117 struct bpf_link *link; 118 119 memset(&linfo, 0, sizeof(linfo)); 120 linfo.cgroup.cgroup_fd = (__u32)cg_fd[PARENT]; 121 linfo.cgroup.cgroup_id = (__u64)cg_id[PARENT]; 122 opts.link_info = &linfo; 123 opts.link_info_len = sizeof(linfo); 124 125 link = bpf_program__attach_iter(skel->progs.cgroup_id_printer, &opts); 126 ASSERT_ERR_PTR(link, "attach_iter"); 127 bpf_link__destroy(link); 128 } 129 130 /* Preorder walk prints parent and child in order. */ 131 static void test_walk_preorder(struct cgroup_iter *skel) 132 { 133 snprintf(expected_output, sizeof(expected_output), 134 PROLOGUE "%8llu\n%8llu\n%8llu\n" EPILOGUE, 135 cg_id[PARENT], cg_id[CHILD1], cg_id[CHILD2]); 136 137 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[PARENT], 138 BPF_CGROUP_ITER_DESCENDANTS_PRE, "preorder"); 139 } 140 141 /* Postorder walk prints child and parent in order. */ 142 static void test_walk_postorder(struct cgroup_iter *skel) 143 { 144 snprintf(expected_output, sizeof(expected_output), 145 PROLOGUE "%8llu\n%8llu\n%8llu\n" EPILOGUE, 146 cg_id[CHILD1], cg_id[CHILD2], cg_id[PARENT]); 147 148 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[PARENT], 149 BPF_CGROUP_ITER_DESCENDANTS_POST, "postorder"); 150 } 151 152 /* Walking parents prints parent and then root. */ 153 static void test_walk_ancestors_up(struct cgroup_iter *skel) 154 { 155 /* terminate the walk when ROOT is met. */ 156 skel->bss->terminal_cgroup = cg_id[ROOT]; 157 158 snprintf(expected_output, sizeof(expected_output), 159 PROLOGUE "%8llu\n%8llu\n" EPILOGUE, 160 cg_id[PARENT], cg_id[ROOT]); 161 162 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[PARENT], 163 BPF_CGROUP_ITER_ANCESTORS_UP, "ancestors_up"); 164 165 skel->bss->terminal_cgroup = 0; 166 } 167 168 /* Early termination prints parent only. */ 169 static void test_early_termination(struct cgroup_iter *skel) 170 { 171 /* terminate the walk after the first element is processed. */ 172 skel->bss->terminate_early = 1; 173 174 snprintf(expected_output, sizeof(expected_output), 175 PROLOGUE "%8llu\n" EPILOGUE, cg_id[PARENT]); 176 177 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[PARENT], 178 BPF_CGROUP_ITER_DESCENDANTS_PRE, "early_termination"); 179 180 skel->bss->terminate_early = 0; 181 } 182 183 /* Waling self prints self only. */ 184 static void test_walk_self_only(struct cgroup_iter *skel) 185 { 186 snprintf(expected_output, sizeof(expected_output), 187 PROLOGUE "%8llu\n" EPILOGUE, cg_id[PARENT]); 188 189 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[PARENT], 190 BPF_CGROUP_ITER_SELF_ONLY, "self_only"); 191 } 192 193 static void test_walk_children(struct cgroup_iter *skel) 194 { 195 snprintf(expected_output, sizeof(expected_output), 196 PROLOGUE "%8llu\n%8llu\n" EPILOGUE, cg_id[CHILD1], 197 cg_id[CHILD2]); 198 199 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[PARENT], 200 BPF_CGROUP_ITER_CHILDREN, "children"); 201 } 202 203 static void test_walk_dead_self_only(struct cgroup_iter *skel) 204 { 205 DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 206 char expected_output[128], buf[128]; 207 const char *cgrp_name = "/dead"; 208 union bpf_iter_link_info linfo; 209 int len, cgrp_fd, iter_fd; 210 struct bpf_link *link; 211 size_t left; 212 char *p; 213 214 cgrp_fd = create_and_get_cgroup(cgrp_name); 215 if (!ASSERT_GE(cgrp_fd, 0, "create cgrp")) 216 return; 217 218 /* The cgroup will be dead during read() iteration, so it only has 219 * epilogue in the output 220 */ 221 snprintf(expected_output, sizeof(expected_output), EPILOGUE); 222 223 memset(&linfo, 0, sizeof(linfo)); 224 linfo.cgroup.cgroup_fd = cgrp_fd; 225 linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY; 226 opts.link_info = &linfo; 227 opts.link_info_len = sizeof(linfo); 228 229 link = bpf_program__attach_iter(skel->progs.cgroup_id_printer, &opts); 230 if (!ASSERT_OK_PTR(link, "attach_iter")) 231 goto close_cgrp; 232 233 iter_fd = bpf_iter_create(bpf_link__fd(link)); 234 if (!ASSERT_GE(iter_fd, 0, "iter_create")) 235 goto free_link; 236 237 /* Close link fd and cgroup fd */ 238 bpf_link__destroy(link); 239 close(cgrp_fd); 240 241 /* Remove cgroup to mark it as dead */ 242 remove_cgroup(cgrp_name); 243 244 /* Two kern_sync_rcu() and usleep() pairs are used to wait for the 245 * releases of cgroup css, and the last kern_sync_rcu() and usleep() 246 * pair is used to wait for the free of cgroup itself. 247 */ 248 kern_sync_rcu(); 249 usleep(8000); 250 kern_sync_rcu(); 251 usleep(8000); 252 kern_sync_rcu(); 253 usleep(1000); 254 255 memset(buf, 0, sizeof(buf)); 256 left = ARRAY_SIZE(buf); 257 p = buf; 258 while ((len = read(iter_fd, p, left)) > 0) { 259 p += len; 260 left -= len; 261 } 262 263 ASSERT_STREQ(buf, expected_output, "dead cgroup output"); 264 265 /* read() after iter finishes should be ok. */ 266 if (len == 0) 267 ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read"); 268 269 close(iter_fd); 270 return; 271 free_link: 272 bpf_link__destroy(link); 273 close_cgrp: 274 close(cgrp_fd); 275 } 276 277 static void test_walk_self_only_css_task(void) 278 { 279 struct iters_css_task *skel; 280 int err; 281 282 skel = iters_css_task__open(); 283 if (!ASSERT_OK_PTR(skel, "skel_open")) 284 return; 285 286 bpf_program__set_autoload(skel->progs.cgroup_id_printer, true); 287 288 err = iters_css_task__load(skel); 289 if (!ASSERT_OK(err, "skel_load")) 290 goto cleanup; 291 292 err = join_cgroup(cg_path[CHILD2]); 293 if (!ASSERT_OK(err, "join_cgroup")) 294 goto cleanup; 295 296 skel->bss->target_pid = getpid(); 297 snprintf(expected_output, sizeof(expected_output), 298 PROLOGUE "%8llu\n" EPILOGUE, cg_id[CHILD2]); 299 read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[CHILD2], 300 BPF_CGROUP_ITER_SELF_ONLY, "test_walk_self_only_css_task"); 301 ASSERT_EQ(skel->bss->css_task_cnt, 1, "css_task_cnt"); 302 cleanup: 303 iters_css_task__destroy(skel); 304 } 305 306 void test_cgroup_iter(void) 307 { 308 struct cgroup_iter *skel = NULL; 309 310 if (setup_cgroup_environment()) 311 return; 312 313 if (setup_cgroups()) 314 goto out; 315 316 skel = cgroup_iter__open_and_load(); 317 if (!ASSERT_OK_PTR(skel, "cgroup_iter__open_and_load")) 318 goto out; 319 320 if (test__start_subtest("cgroup_iter__invalid_cgroup")) 321 test_invalid_cgroup(skel); 322 if (test__start_subtest("cgroup_iter__invalid_cgroup_spec")) 323 test_invalid_cgroup_spec(skel); 324 if (test__start_subtest("cgroup_iter__preorder")) 325 test_walk_preorder(skel); 326 if (test__start_subtest("cgroup_iter__postorder")) 327 test_walk_postorder(skel); 328 if (test__start_subtest("cgroup_iter__ancestors_up_walk")) 329 test_walk_ancestors_up(skel); 330 if (test__start_subtest("cgroup_iter__early_termination")) 331 test_early_termination(skel); 332 if (test__start_subtest("cgroup_iter__self_only")) 333 test_walk_self_only(skel); 334 if (test__start_subtest("cgroup_iter__dead_self_only")) 335 test_walk_dead_self_only(skel); 336 if (test__start_subtest("cgroup_iter__self_only_css_task")) 337 test_walk_self_only_css_task(); 338 if (test__start_subtest("cgroup_iter__children")) 339 test_walk_children(skel); 340 341 out: 342 cgroup_iter__destroy(skel); 343 cleanup_cgroups(); 344 cleanup_cgroup_environment(); 345 } 346