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
setup_cgroups(void)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
cleanup_cgroups(void)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
read_from_cgroup_iter(struct bpf_program * prog,int cgroup_fd,int order,const char * testname)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. */
test_invalid_cgroup(struct cgroup_iter * skel)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. */
test_invalid_cgroup_spec(struct cgroup_iter * skel)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. */
test_walk_preorder(struct cgroup_iter * skel)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. */
test_walk_postorder(struct cgroup_iter * skel)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. */
test_walk_ancestors_up(struct cgroup_iter * skel)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. */
test_early_termination(struct cgroup_iter * skel)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. */
test_walk_self_only(struct cgroup_iter * skel)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
test_cgroup_iter(void)192 void test_cgroup_iter(void)
193 {
194 struct cgroup_iter *skel = NULL;
195
196 if (setup_cgroup_environment())
197 return;
198
199 if (setup_cgroups())
200 goto out;
201
202 skel = cgroup_iter__open_and_load();
203 if (!ASSERT_OK_PTR(skel, "cgroup_iter__open_and_load"))
204 goto out;
205
206 if (test__start_subtest("cgroup_iter__invalid_cgroup"))
207 test_invalid_cgroup(skel);
208 if (test__start_subtest("cgroup_iter__invalid_cgroup_spec"))
209 test_invalid_cgroup_spec(skel);
210 if (test__start_subtest("cgroup_iter__preorder"))
211 test_walk_preorder(skel);
212 if (test__start_subtest("cgroup_iter__postorder"))
213 test_walk_postorder(skel);
214 if (test__start_subtest("cgroup_iter__ancestors_up_walk"))
215 test_walk_ancestors_up(skel);
216 if (test__start_subtest("cgroup_iter__early_termination"))
217 test_early_termination(skel);
218 if (test__start_subtest("cgroup_iter__self_only"))
219 test_walk_self_only(skel);
220 out:
221 cgroup_iter__destroy(skel);
222 cleanup_cgroups();
223 cleanup_cgroup_environment();
224 }
225