]> Git Repo - J-linux.git/blob - tools/testing/selftests/bpf/prog_tests/cgroup_iter.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / bpf / prog_tests / cgroup_iter.c
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_dead_self_only(struct cgroup_iter *skel)
194 {
195         DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts);
196         char expected_output[128], buf[128];
197         const char *cgrp_name = "/dead";
198         union bpf_iter_link_info linfo;
199         int len, cgrp_fd, iter_fd;
200         struct bpf_link *link;
201         size_t left;
202         char *p;
203
204         cgrp_fd = create_and_get_cgroup(cgrp_name);
205         if (!ASSERT_GE(cgrp_fd, 0, "create cgrp"))
206                 return;
207
208         /* The cgroup will be dead during read() iteration, so it only has
209          * epilogue in the output
210          */
211         snprintf(expected_output, sizeof(expected_output), EPILOGUE);
212
213         memset(&linfo, 0, sizeof(linfo));
214         linfo.cgroup.cgroup_fd = cgrp_fd;
215         linfo.cgroup.order = BPF_CGROUP_ITER_SELF_ONLY;
216         opts.link_info = &linfo;
217         opts.link_info_len = sizeof(linfo);
218
219         link = bpf_program__attach_iter(skel->progs.cgroup_id_printer, &opts);
220         if (!ASSERT_OK_PTR(link, "attach_iter"))
221                 goto close_cgrp;
222
223         iter_fd = bpf_iter_create(bpf_link__fd(link));
224         if (!ASSERT_GE(iter_fd, 0, "iter_create"))
225                 goto free_link;
226
227         /* Close link fd and cgroup fd */
228         bpf_link__destroy(link);
229         close(cgrp_fd);
230
231         /* Remove cgroup to mark it as dead */
232         remove_cgroup(cgrp_name);
233
234         /* Two kern_sync_rcu() and usleep() pairs are used to wait for the
235          * releases of cgroup css, and the last kern_sync_rcu() and usleep()
236          * pair is used to wait for the free of cgroup itself.
237          */
238         kern_sync_rcu();
239         usleep(8000);
240         kern_sync_rcu();
241         usleep(8000);
242         kern_sync_rcu();
243         usleep(1000);
244
245         memset(buf, 0, sizeof(buf));
246         left = ARRAY_SIZE(buf);
247         p = buf;
248         while ((len = read(iter_fd, p, left)) > 0) {
249                 p += len;
250                 left -= len;
251         }
252
253         ASSERT_STREQ(buf, expected_output, "dead cgroup output");
254
255         /* read() after iter finishes should be ok. */
256         if (len == 0)
257                 ASSERT_OK(read(iter_fd, buf, sizeof(buf)), "second_read");
258
259         close(iter_fd);
260         return;
261 free_link:
262         bpf_link__destroy(link);
263 close_cgrp:
264         close(cgrp_fd);
265 }
266
267 static void test_walk_self_only_css_task(void)
268 {
269         struct iters_css_task *skel;
270         int err;
271
272         skel = iters_css_task__open();
273         if (!ASSERT_OK_PTR(skel, "skel_open"))
274                 return;
275
276         bpf_program__set_autoload(skel->progs.cgroup_id_printer, true);
277
278         err = iters_css_task__load(skel);
279         if (!ASSERT_OK(err, "skel_load"))
280                 goto cleanup;
281
282         err = join_cgroup(cg_path[CHILD2]);
283         if (!ASSERT_OK(err, "join_cgroup"))
284                 goto cleanup;
285
286         skel->bss->target_pid = getpid();
287         snprintf(expected_output, sizeof(expected_output),
288                 PROLOGUE "%8llu\n" EPILOGUE, cg_id[CHILD2]);
289         read_from_cgroup_iter(skel->progs.cgroup_id_printer, cg_fd[CHILD2],
290                 BPF_CGROUP_ITER_SELF_ONLY, "test_walk_self_only_css_task");
291         ASSERT_EQ(skel->bss->css_task_cnt, 1, "css_task_cnt");
292 cleanup:
293         iters_css_task__destroy(skel);
294 }
295
296 void test_cgroup_iter(void)
297 {
298         struct cgroup_iter *skel = NULL;
299
300         if (setup_cgroup_environment())
301                 return;
302
303         if (setup_cgroups())
304                 goto out;
305
306         skel = cgroup_iter__open_and_load();
307         if (!ASSERT_OK_PTR(skel, "cgroup_iter__open_and_load"))
308                 goto out;
309
310         if (test__start_subtest("cgroup_iter__invalid_cgroup"))
311                 test_invalid_cgroup(skel);
312         if (test__start_subtest("cgroup_iter__invalid_cgroup_spec"))
313                 test_invalid_cgroup_spec(skel);
314         if (test__start_subtest("cgroup_iter__preorder"))
315                 test_walk_preorder(skel);
316         if (test__start_subtest("cgroup_iter__postorder"))
317                 test_walk_postorder(skel);
318         if (test__start_subtest("cgroup_iter__ancestors_up_walk"))
319                 test_walk_ancestors_up(skel);
320         if (test__start_subtest("cgroup_iter__early_termination"))
321                 test_early_termination(skel);
322         if (test__start_subtest("cgroup_iter__self_only"))
323                 test_walk_self_only(skel);
324         if (test__start_subtest("cgroup_iter__dead_self_only"))
325                 test_walk_dead_self_only(skel);
326         if (test__start_subtest("cgroup_iter__self_only_css_task"))
327                 test_walk_self_only_css_task();
328
329 out:
330         cgroup_iter__destroy(skel);
331         cleanup_cgroups();
332         cleanup_cgroup_environment();
333 }
This page took 0.045635 seconds and 4 git commands to generate.