xref: /linux/tools/testing/selftests/bpf/prog_tests/cgroup_iter.c (revision 6f7e6393d1ce636bb7ec77a7fe7b77458fddf701)
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