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