1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2020 Red Hat, Inc.
4 * Copyright (c) 2020 Li Wang <liwang@redhat.com>
5 * Copyright (c) 2020-2021 SUSE LLC <rpalethorpe@suse.com>
6 */
7
8 #define TST_NO_DEFAULT_MAIN
9
10 #include <stdio.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <mntent.h>
14 #include <sys/mount.h>
15
16 #include "tst_test.h"
17 #include "lapi/fcntl.h"
18 #include "lapi/mount.h"
19 #include "lapi/mkdirat.h"
20 #include "tst_safe_file_at.h"
21 #include "tst_cgroup.h"
22
23 struct cgroup_root;
24
25 /* A node in a single CGroup hierarchy. It exists mainly for
26 * convenience so that we do not have to traverse the CGroup structure
27 * for frequent operations.
28 *
29 * This is actually a single-linked list not a tree. We only need to
30 * traverse from leaf towards root.
31 */
32 struct cgroup_dir {
33 const char *dir_name;
34 const struct cgroup_dir *dir_parent;
35
36 /* Shortcut to root */
37 const struct cgroup_root *dir_root;
38
39 /* Subsystems (controllers) bit field. Only controllers which
40 * were required and configured for this node are added to
41 * this field. So it may be different from root->css_field.
42 */
43 uint32_t ctrl_field;
44
45 /* In general we avoid having sprintfs everywhere and use
46 * openat, linkat, etc.
47 */
48 int dir_fd;
49
50 int we_created_it:1;
51 };
52
53 /* The root of a CGroup hierarchy/tree */
54 struct cgroup_root {
55 enum tst_cgroup_ver ver;
56 /* A mount path */
57 char mnt_path[PATH_MAX];
58 /* Subsystems (controllers) bit field. Includes all
59 * controllers found while scanning this root.
60 */
61 uint32_t ctrl_field;
62
63 /* CGroup hierarchy: mnt -> ltp -> {drain, test -> ??? } We
64 * keep a flat reference to ltp, drain and test for
65 * convenience.
66 */
67
68 /* Mount directory */
69 struct cgroup_dir mnt_dir;
70 /* LTP CGroup directory, contains drain and test dirs */
71 struct cgroup_dir ltp_dir;
72 /* Drain CGroup, see cgroup_cleanup */
73 struct cgroup_dir drain_dir;
74 /* CGroup for current test. Which may have children. */
75 struct cgroup_dir test_dir;
76
77 int we_mounted_it:1;
78 /* cpuset is in compatability mode */
79 int no_cpuset_prefix:1;
80 };
81
82 /* Controller sub-systems */
83 enum cgroup_ctrl_indx {
84 CTRL_MEMORY = 1,
85 CTRL_CPU,
86 CTRL_CPUSET,
87 };
88 #define CTRLS_MAX CTRL_CPUSET
89
90 /* At most we can have one cgroup V1 tree for each controller and one
91 * (empty) v2 tree.
92 */
93 #define ROOTS_MAX (CTRLS_MAX + 1)
94
95 /* Describes a controller file or knob
96 *
97 * The primary purpose of this is to map V2 names to V1
98 * names.
99 */
100 struct cgroup_file {
101 /* Canonical name. Is the V2 name unless an item is V1 only */
102 const char *const file_name;
103 /* V1 name or NULL if item is V2 only */
104 const char *const file_name_v1;
105
106 /* The controller this item belongs to or zero for
107 * 'cgroup.<item>'.
108 */
109 const enum cgroup_ctrl_indx ctrl_indx;
110 };
111
112 /* Describes a Controller or subsystem
113 *
114 * Internally the kernel seems to call controllers subsystems and uses
115 * the abbreviations subsys and css.
116 */
117 struct cgroup_ctrl {
118 /* Userland name of the controller (e.g. 'memory' not 'memcg') */
119 const char *const ctrl_name;
120 /* List of files belonging to this controller */
121 const struct cgroup_file *const files;
122 /* Our index for the controller */
123 const enum cgroup_ctrl_indx ctrl_indx;
124
125 /* Runtime; hierarchy the controller is attached to */
126 struct cgroup_root *ctrl_root;
127 /* Runtime; whether we required the controller */
128 int we_require_it:1;
129 };
130
131 struct tst_cgroup_group {
132 char group_name[NAME_MAX + 1];
133 /* Maps controller ID to the tree which contains it. The V2
134 * tree is at zero even if it contains no controllers.
135 */
136 struct cgroup_dir *dirs_by_ctrl[ROOTS_MAX];
137 /* NULL terminated list of trees */
138 struct cgroup_dir *dirs[ROOTS_MAX + 1];
139 };
140
141 /* Always use first item for unified hierarchy */
142 static struct cgroup_root roots[ROOTS_MAX + 1];
143
144 static const struct cgroup_file cgroup_ctrl_files[] = {
145 /* procs exists on V1, however it was read-only until kernel v3.0. */
146 { "cgroup.procs", "tasks", 0 },
147 { "cgroup.controllers", NULL, 0 },
148 { "cgroup.subtree_control", NULL, 0 },
149 { "cgroup.clone_children", "cgroup.clone_children", 0 },
150 { }
151 };
152
153 static const struct cgroup_file memory_ctrl_files[] = {
154 { "memory.current", "memory.usage_in_bytes", CTRL_MEMORY },
155 { "memory.max", "memory.limit_in_bytes", CTRL_MEMORY },
156 { "memory.stat", "memory.stat", CTRL_MEMORY },
157 { "memory.swappiness", "memory.swappiness", CTRL_MEMORY },
158 { "memory.swap.current", "memory.memsw.usage_in_bytes", CTRL_MEMORY },
159 { "memory.swap.max", "memory.memsw.limit_in_bytes", CTRL_MEMORY },
160 { "memory.kmem.usage_in_bytes", "memory.kmem.usage_in_bytes", CTRL_MEMORY },
161 { "memory.kmem.limit_in_bytes", "memory.kmem.limit_in_bytes", CTRL_MEMORY },
162 { }
163 };
164
165 static const struct cgroup_file cpu_ctrl_files[] = {
166 /* The V1 quota and period files were combined in the V2 max
167 * file. The quota is in the first column and if we just print
168 * a single value to the file, it will be treated as the
169 * quota. To get or set the period we need to branch on the
170 * API version.
171 */
172 { "cpu.max", "cpu.cfs_quota_us", CTRL_CPU },
173 { "cpu.cfs_period_us", "cpu.cfs_period_us", CTRL_CPU },
174 { }
175 };
176
177 static const struct cgroup_file cpuset_ctrl_files[] = {
178 { "cpuset.cpus", "cpuset.cpus", CTRL_CPUSET },
179 { "cpuset.mems", "cpuset.mems", CTRL_CPUSET },
180 { "cpuset.memory_migrate", "cpuset.memory_migrate", CTRL_CPUSET },
181 { }
182 };
183
184 /* Lookup tree for item names. */
185 static struct cgroup_ctrl controllers[] = {
186 [0] = { "cgroup", cgroup_ctrl_files, 0, NULL, 0 },
187 [CTRL_MEMORY] = {
188 "memory", memory_ctrl_files, CTRL_MEMORY, NULL, 0
189 },
190 [CTRL_CPU] = {
191 "cpu", cpu_ctrl_files, CTRL_CPU, NULL, 0
192 },
193 [CTRL_CPUSET] = {
194 "cpuset", cpuset_ctrl_files, CTRL_CPUSET, NULL, 0
195 },
196 { }
197 };
198
199 static const struct tst_cgroup_opts default_opts = { 0 };
200
201 /* We should probably allow these to be set in environment
202 * variables
203 */
204 static const char *cgroup_ltp_dir = "ltp";
205 static const char *cgroup_ltp_drain_dir = "drain";
206 static char cgroup_test_dir[NAME_MAX + 1];
207 static const char *cgroup_mount_ltp_prefix = "/tmp/cgroup_";
208 static const char *cgroup_v2_ltp_mount = "unified";
209
210 #define first_root \
211 (roots[0].ver ? roots : roots + 1)
212 #define for_each_root(r) \
213 for ((r) = first_root; (r)->ver; (r)++)
214 #define for_each_v1_root(r) \
215 for ((r) = roots + 1; (r)->ver; (r)++)
216 #define for_each_ctrl(ctrl) \
217 for ((ctrl) = controllers + 1; (ctrl)->ctrl_name; (ctrl)++)
218
219 /* In all cases except one, this only loops once.
220 *
221 * If (ctrl) == 0 and multiple V1 (and a V2) hierarchies are mounted,
222 * then we need to loop over multiple directories. For example if we
223 * need to write to "tasks"/"cgroup.procs" which exists for each
224 * hierarchy.
225 */
226 #define for_each_dir(cg, ctrl, t) \
227 for ((t) = (ctrl) ? (cg)->dirs_by_ctrl + (ctrl) : (cg)->dirs; \
228 *(t); \
229 (t) = (ctrl) ? (cg)->dirs + ROOTS_MAX : (t) + 1)
230
231 __attribute__ ((nonnull))
has_ctrl(const uint32_t ctrl_field,const struct cgroup_ctrl * const ctrl)232 static int has_ctrl(const uint32_t ctrl_field,
233 const struct cgroup_ctrl *const ctrl)
234 {
235 return !!(ctrl_field & (1 << ctrl->ctrl_indx));
236 }
237
238 __attribute__ ((nonnull))
add_ctrl(uint32_t * const ctrl_field,const struct cgroup_ctrl * const ctrl)239 static void add_ctrl(uint32_t *const ctrl_field,
240 const struct cgroup_ctrl *const ctrl)
241 {
242 *ctrl_field |= 1 << ctrl->ctrl_indx;
243 }
244
cgroup_v2_mounted(void)245 static int cgroup_v2_mounted(void)
246 {
247 return !!roots[0].ver;
248 }
249
cgroup_v1_mounted(void)250 static int cgroup_v1_mounted(void)
251 {
252 return !!roots[1].ver;
253 }
254
cgroup_mounted(void)255 static int cgroup_mounted(void)
256 {
257 return cgroup_v2_mounted() || cgroup_v1_mounted();
258 }
259
260 __attribute__ ((nonnull, warn_unused_result))
cgroup_ctrl_on_v2(const struct cgroup_ctrl * const ctrl)261 static int cgroup_ctrl_on_v2(const struct cgroup_ctrl *const ctrl)
262 {
263 return ctrl->ctrl_root && ctrl->ctrl_root->ver == TST_CGROUP_V2;
264 }
265
266 __attribute__ ((nonnull))
cgroup_dir_mk(const struct cgroup_dir * const parent,const char * const dir_name,struct cgroup_dir * const new)267 static void cgroup_dir_mk(const struct cgroup_dir *const parent,
268 const char *const dir_name,
269 struct cgroup_dir *const new)
270 {
271 const char *dpath;
272
273 new->dir_root = parent->dir_root;
274 new->dir_name = dir_name;
275 new->dir_parent = parent;
276 new->ctrl_field = parent->ctrl_field;
277 new->we_created_it = 0;
278
279 if (!mkdirat(parent->dir_fd, dir_name, 0777)) {
280 new->we_created_it = 1;
281 goto opendir;
282 }
283
284 if (errno == EEXIST)
285 goto opendir;
286
287 dpath = tst_decode_fd(parent->dir_fd);
288
289 if (errno == EACCES) {
290 tst_brk(TCONF | TERRNO,
291 "Lack permission to make '%s/%s'; premake it or run as root",
292 dpath, dir_name);
293 } else {
294 tst_brk(TBROK | TERRNO,
295 "mkdirat(%d<%s>, '%s', 0777)",
296 parent->dir_fd, dpath, dir_name);
297 }
298
299 opendir:
300 new->dir_fd = SAFE_OPENAT(parent->dir_fd, dir_name,
301 O_PATH | O_DIRECTORY);
302 }
303
tst_cgroup_print_config(void)304 void tst_cgroup_print_config(void)
305 {
306 struct cgroup_root *root;
307 const struct cgroup_ctrl *ctrl;
308
309 tst_res(TINFO, "Detected Controllers:");
310
311 for_each_ctrl(ctrl) {
312 root = ctrl->ctrl_root;
313
314 if (!root)
315 continue;
316
317 tst_res(TINFO, "\t%.10s %s @ %s:%s",
318 ctrl->ctrl_name,
319 root->no_cpuset_prefix ? "[noprefix]" : "",
320 root->ver == TST_CGROUP_V1 ? "V1" : "V2",
321 root->mnt_path);
322 }
323 }
324
325 __attribute__ ((nonnull, warn_unused_result))
cgroup_find_ctrl(const char * const ctrl_name)326 static struct cgroup_ctrl *cgroup_find_ctrl(const char *const ctrl_name)
327 {
328 struct cgroup_ctrl *ctrl = controllers;
329
330 while (ctrl->ctrl_name && strcmp(ctrl_name, ctrl->ctrl_name))
331 ctrl++;
332
333 if (!ctrl->ctrl_name)
334 ctrl = NULL;
335
336 return ctrl;
337 }
338
339 /* Determine if a mounted cgroup hierarchy is unique and record it if so.
340 *
341 * For CGroups V2 this is very simple as there is only one
342 * hierarchy. We just record which controllers are available and check
343 * if this matches what we read from any previous mount points.
344 *
345 * For V1 the set of controllers S is partitioned into sets {P_1, P_2,
346 * ..., P_n} with one or more controllers in each partion. Each
347 * partition P_n can be mounted multiple times, but the same
348 * controller can not appear in more than one partition. Usually each
349 * partition contains a single controller, but, for example, cpu and
350 * cpuacct are often mounted together in the same partiion.
351 *
352 * Each controller partition has its own hierarchy (root) which we
353 * must track and update independently.
354 */
355 __attribute__ ((nonnull))
cgroup_root_scan(const char * const mnt_type,const char * const mnt_dir,char * const mnt_opts)356 static void cgroup_root_scan(const char *const mnt_type,
357 const char *const mnt_dir,
358 char *const mnt_opts)
359 {
360 struct cgroup_root *root = roots;
361 const struct cgroup_ctrl *const_ctrl;
362 struct cgroup_ctrl *ctrl;
363 uint32_t ctrl_field = 0;
364 int no_prefix = 0;
365 char buf[BUFSIZ];
366 char *tok;
367 const int mnt_dfd = SAFE_OPEN(mnt_dir, O_PATH | O_DIRECTORY);
368
369 if (!strcmp(mnt_type, "cgroup"))
370 goto v1;
371
372 SAFE_FILE_READAT(mnt_dfd, "cgroup.controllers", buf, sizeof(buf));
373
374 for (tok = strtok(buf, " "); tok; tok = strtok(NULL, " ")) {
375 const_ctrl = cgroup_find_ctrl(tok);
376 if (const_ctrl)
377 add_ctrl(&ctrl_field, const_ctrl);
378 }
379
380 if (root->ver && ctrl_field == root->ctrl_field)
381 goto discard;
382
383 if (root->ctrl_field)
384 tst_brk(TBROK, "Available V2 controllers are changing between scans?");
385
386 root->ver = TST_CGROUP_V2;
387
388 goto backref;
389
390 v1:
391 for (tok = strtok(mnt_opts, ","); tok; tok = strtok(NULL, ",")) {
392 const_ctrl = cgroup_find_ctrl(tok);
393 if (const_ctrl)
394 add_ctrl(&ctrl_field, const_ctrl);
395
396 no_prefix |= !strcmp("noprefix", tok);
397 }
398
399 if (!ctrl_field)
400 goto discard;
401
402 for_each_v1_root(root) {
403 if (!(ctrl_field & root->ctrl_field))
404 continue;
405
406 if (ctrl_field == root->ctrl_field)
407 goto discard;
408
409 tst_brk(TBROK,
410 "The intersection of two distinct sets of mounted controllers should be null? "
411 "Check '%s' and '%s'", root->mnt_path, mnt_dir);
412 }
413
414 if (root >= roots + ROOTS_MAX) {
415 tst_brk(TBROK,
416 "Unique controller mounts have exceeded our limit %d?",
417 ROOTS_MAX);
418 }
419
420 root->ver = TST_CGROUP_V1;
421
422 backref:
423 strcpy(root->mnt_path, mnt_dir);
424 root->mnt_dir.dir_root = root;
425 root->mnt_dir.dir_name = root->mnt_path;
426 root->mnt_dir.dir_fd = mnt_dfd;
427 root->ctrl_field = ctrl_field;
428 root->no_cpuset_prefix = no_prefix;
429
430 for_each_ctrl(ctrl) {
431 if (has_ctrl(root->ctrl_field, ctrl))
432 ctrl->ctrl_root = root;
433 }
434
435 return;
436
437 discard:
438 close(mnt_dfd);
439 }
440
tst_cgroup_scan(void)441 void tst_cgroup_scan(void)
442 {
443 struct mntent *mnt;
444 FILE *f = setmntent("/proc/self/mounts", "r");
445
446 if (!f) {
447 tst_brk(TBROK | TERRNO, "Can't open /proc/self/mounts");
448 return;
449 }
450
451 mnt = getmntent(f);
452 if (!mnt) {
453 tst_brk(TBROK | TERRNO, "Can't read mounts or no mounts?");
454 return;
455 }
456
457 do {
458 if (strncmp(mnt->mnt_type, "cgroup", 6))
459 continue;
460
461 cgroup_root_scan(mnt->mnt_type, mnt->mnt_dir, mnt->mnt_opts);
462 } while ((mnt = getmntent(f)));
463 }
464
cgroup_mount_v2(void)465 static void cgroup_mount_v2(void)
466 {
467 char mnt_path[PATH_MAX];
468
469 sprintf(mnt_path, "%s%s", cgroup_mount_ltp_prefix, cgroup_v2_ltp_mount);
470
471 if (!mkdir(mnt_path, 0777)) {
472 roots[0].mnt_dir.we_created_it = 1;
473 goto mount;
474 }
475
476 if (errno == EEXIST)
477 goto mount;
478
479 if (errno == EACCES) {
480 tst_res(TINFO | TERRNO,
481 "Lack permission to make %s, premake it or run as root",
482 mnt_path);
483 return;
484 }
485
486 tst_brk(TBROK | TERRNO, "mkdir(%s, 0777)", mnt_path);
487 return;
488
489 mount:
490 if (!mount("cgroup2", mnt_path, "cgroup2", 0, NULL)) {
491 tst_res(TINFO, "Mounted V2 CGroups on %s", mnt_path);
492 tst_cgroup_scan();
493 roots[0].we_mounted_it = 1;
494 return;
495 }
496
497 tst_res(TINFO | TERRNO, "Could not mount V2 CGroups on %s", mnt_path);
498
499 if (roots[0].mnt_dir.we_created_it) {
500 roots[0].mnt_dir.we_created_it = 0;
501 SAFE_RMDIR(mnt_path);
502 }
503 }
504
505 __attribute__ ((nonnull))
cgroup_mount_v1(struct cgroup_ctrl * const ctrl)506 static void cgroup_mount_v1(struct cgroup_ctrl *const ctrl)
507 {
508 char mnt_path[PATH_MAX];
509 int made_dir = 0;
510
511 sprintf(mnt_path, "%s%s", cgroup_mount_ltp_prefix, ctrl->ctrl_name);
512
513 if (!mkdir(mnt_path, 0777)) {
514 made_dir = 1;
515 goto mount;
516 }
517
518 if (errno == EEXIST)
519 goto mount;
520
521 if (errno == EACCES) {
522 tst_res(TINFO | TERRNO,
523 "Lack permission to make %s, premake it or run as root",
524 mnt_path);
525 return;
526 }
527
528 tst_brk(TBROK | TERRNO, "mkdir(%s, 0777)", mnt_path);
529 return;
530
531 mount:
532 if (mount(ctrl->ctrl_name, mnt_path, "cgroup", 0, ctrl->ctrl_name)) {
533 tst_res(TINFO | TERRNO,
534 "Could not mount V1 CGroup on %s", mnt_path);
535
536 if (made_dir)
537 SAFE_RMDIR(mnt_path);
538 return;
539 }
540
541 tst_res(TINFO, "Mounted V1 %s CGroup on %s", ctrl->ctrl_name, mnt_path);
542 tst_cgroup_scan();
543 if (!ctrl->ctrl_root)
544 return;
545
546 ctrl->ctrl_root->we_mounted_it = 1;
547 ctrl->ctrl_root->mnt_dir.we_created_it = made_dir;
548
549 if (ctrl->ctrl_indx == CTRL_MEMORY) {
550 SAFE_FILE_PRINTFAT(ctrl->ctrl_root->mnt_dir.dir_fd,
551 "memory.use_hierarchy", "%d", 1);
552 }
553 }
554
555 __attribute__ ((nonnull))
cgroup_copy_cpuset(const struct cgroup_root * const root)556 static void cgroup_copy_cpuset(const struct cgroup_root *const root)
557 {
558 char knob_val[BUFSIZ];
559 int i;
560 const char *const n0[] = {"mems", "cpus"};
561 const char *const n1[] = {"cpuset.mems", "cpuset.cpus"};
562 const char *const *const fname = root->no_cpuset_prefix ? n0 : n1;
563
564 for (i = 0; i < 2; i++) {
565 SAFE_FILE_READAT(root->mnt_dir.dir_fd,
566 fname[i], knob_val, sizeof(knob_val));
567 SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
568 fname[i], "%s", knob_val);
569 }
570 }
571
572 /* Ensure the specified controller is available.
573 *
574 * First we check if the specified controller has a known mount point,
575 * if not then we scan the system. If we find it then we goto ensuring
576 * the LTP group exists in the hierarchy the controller is using.
577 *
578 * If we can't find the controller, then we try to create it. First we
579 * check if the V2 hierarchy/tree is mounted. If it isn't then we try
580 * mounting it and look for the controller. If it is already mounted
581 * then we know the controller is not available on V2 on this system.
582 *
583 * If we can't mount V2 or the controller is not on V2, then we try
584 * mounting it on its own V1 tree.
585 *
586 * Once we have mounted the controller somehow, we create a hierarchy
587 * of cgroups. If we are on V2 we first need to enable the controller
588 * for all children of root. Then we create hierarchy described in
589 * tst_cgroup.h.
590 *
591 * If we are using V1 cpuset then we copy the available mems and cpus
592 * from root to the ltp group and set clone_children on the ltp group
593 * to distribute these settings to the test cgroups. This means the
594 * test author does not have to copy these settings before using the
595 * cpuset.
596 *
597 */
tst_cgroup_require(const char * const ctrl_name,const struct tst_cgroup_opts * options)598 void tst_cgroup_require(const char *const ctrl_name,
599 const struct tst_cgroup_opts *options)
600 {
601 const char *const cgsc = "cgroup.subtree_control";
602 struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name);
603 struct cgroup_root *root;
604
605 if (!ctrl) {
606 tst_brk(TBROK, "'%s' controller is unknown to LTP", ctrl_name);
607 tst_brk(TBROK, "Calling %s in cleanup?", __func__);
608 return;
609 }
610
611 if (!options)
612 options = &default_opts;
613
614 if (ctrl->we_require_it)
615 tst_res(TWARN, "Duplicate %s(%s, )", __func__, ctrl->ctrl_name);
616
617 ctrl->we_require_it = 1;
618
619 if (ctrl->ctrl_root)
620 goto mkdirs;
621
622 tst_cgroup_scan();
623
624 if (ctrl->ctrl_root)
625 goto mkdirs;
626
627 if (!cgroup_v2_mounted() && !options->only_mount_v1)
628 cgroup_mount_v2();
629
630 if (ctrl->ctrl_root)
631 goto mkdirs;
632
633 cgroup_mount_v1(ctrl);
634
635 if (!ctrl->ctrl_root) {
636 tst_brk(TCONF,
637 "'%s' controller required, but not available",
638 ctrl->ctrl_name);
639 return;
640 }
641
642 mkdirs:
643 root = ctrl->ctrl_root;
644 add_ctrl(&root->mnt_dir.ctrl_field, ctrl);
645
646 if (cgroup_ctrl_on_v2(ctrl)) {
647 if (root->we_mounted_it) {
648 SAFE_FILE_PRINTFAT(root->mnt_dir.dir_fd,
649 cgsc, "+%s", ctrl->ctrl_name);
650 } else {
651 tst_file_printfat(root->mnt_dir.dir_fd,
652 cgsc, "+%s", ctrl->ctrl_name);
653 }
654 }
655
656 if (!root->ltp_dir.dir_fd)
657 cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
658 else
659 root->ltp_dir.ctrl_field |= root->mnt_dir.ctrl_field;
660
661 if (cgroup_ctrl_on_v2(ctrl)) {
662 SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
663 cgsc, "+%s", ctrl->ctrl_name);
664 } else {
665 SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
666 "cgroup.clone_children", "%d", 1);
667
668 if (ctrl->ctrl_indx == CTRL_CPUSET)
669 cgroup_copy_cpuset(root);
670 }
671
672 cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
673
674 sprintf(cgroup_test_dir, "test-%d", getpid());
675 cgroup_dir_mk(&root->ltp_dir, cgroup_test_dir, &root->test_dir);
676 }
677
cgroup_drain(const enum tst_cgroup_ver ver,const int source_dfd,const int dest_dfd)678 static void cgroup_drain(const enum tst_cgroup_ver ver,
679 const int source_dfd, const int dest_dfd)
680 {
681 char pid_list[BUFSIZ];
682 char *tok;
683 const char *const file_name =
684 ver == TST_CGROUP_V1 ? "tasks" : "cgroup.procs";
685 int fd;
686 ssize_t ret;
687
688 ret = SAFE_FILE_READAT(source_dfd, file_name,
689 pid_list, sizeof(pid_list));
690 if (ret < 0)
691 return;
692
693 fd = SAFE_OPENAT(dest_dfd, file_name, O_WRONLY);
694 if (fd < 0)
695 return;
696
697 for (tok = strtok(pid_list, "\n"); tok; tok = strtok(NULL, "\n")) {
698 ret = dprintf(fd, "%s", tok);
699
700 if (ret < (ssize_t)strlen(tok))
701 tst_brk(TBROK | TERRNO, "Failed to drain %s", tok);
702 }
703 SAFE_CLOSE(fd);
704 }
705
706 __attribute__ ((nonnull))
close_path_fds(struct cgroup_root * const root)707 static void close_path_fds(struct cgroup_root *const root)
708 {
709 if (root->test_dir.dir_fd > 0)
710 SAFE_CLOSE(root->test_dir.dir_fd);
711 if (root->ltp_dir.dir_fd > 0)
712 SAFE_CLOSE(root->ltp_dir.dir_fd);
713 if (root->drain_dir.dir_fd > 0)
714 SAFE_CLOSE(root->drain_dir.dir_fd);
715 if (root->mnt_dir.dir_fd > 0)
716 SAFE_CLOSE(root->mnt_dir.dir_fd);
717 }
718
719 /* Maybe remove CGroups used during testing and clear our data
720 *
721 * This will never remove CGroups we did not create to allow tests to
722 * be run in parallel.
723 *
724 * Each test process is given its own unique CGroup. Unless we want to
725 * stress test the CGroup system. We should at least remove these
726 * unique per test CGroups.
727 *
728 * We probably also want to remove the LTP parent CGroup, although
729 * this may have been created by the system manager or another test
730 * (see notes on parallel testing).
731 *
732 * On systems with no initial CGroup setup we may try to destroy the
733 * CGroup roots we mounted so that they can be recreated by another
734 * test. Note that successfully unmounting a CGroup root does not
735 * necessarily indicate that it was destroyed.
736 *
737 * The ltp/drain CGroup is required for cleaning up test CGroups when
738 * we can not move them to the root CGroup. CGroups can only be
739 * removed when they have no members and only leaf or root CGroups may
740 * have processes within them. As test processes create and destroy
741 * their own CGroups they must move themselves either to root or
742 * another leaf CGroup. So we move them to drain while destroying the
743 * unique test CGroup.
744 *
745 * If we have access to root and created the LTP CGroup we then move
746 * the test process to root and destroy the drain and LTP
747 * CGroups. Otherwise we just leave the test process to die in the
748 * drain, much like many a unwanted terrapin.
749 *
750 * Finally we clear any data we have collected on CGroups. This will
751 * happen regardless of whether anything was removed.
752 */
tst_cgroup_cleanup(void)753 void tst_cgroup_cleanup(void)
754 {
755 struct cgroup_root *root;
756 struct cgroup_ctrl *ctrl;
757
758 if (!cgroup_mounted())
759 goto clear_data;
760
761 for_each_root(root) {
762 if (!root->test_dir.dir_name)
763 continue;
764
765 cgroup_drain(root->ver,
766 root->test_dir.dir_fd, root->drain_dir.dir_fd);
767 SAFE_UNLINKAT(root->ltp_dir.dir_fd, root->test_dir.dir_name,
768 AT_REMOVEDIR);
769 }
770
771 for_each_root(root) {
772 if (!root->ltp_dir.we_created_it)
773 continue;
774
775 cgroup_drain(root->ver,
776 root->drain_dir.dir_fd, root->mnt_dir.dir_fd);
777
778 if (root->drain_dir.dir_name) {
779 SAFE_UNLINKAT(root->ltp_dir.dir_fd,
780 root->drain_dir.dir_name, AT_REMOVEDIR);
781 }
782
783 if (root->ltp_dir.dir_name) {
784 SAFE_UNLINKAT(root->mnt_dir.dir_fd,
785 root->ltp_dir.dir_name, AT_REMOVEDIR);
786 }
787 }
788
789 for_each_ctrl(ctrl) {
790 if (!cgroup_ctrl_on_v2(ctrl) || !ctrl->ctrl_root->we_mounted_it)
791 continue;
792
793 SAFE_FILE_PRINTFAT(ctrl->ctrl_root->mnt_dir.dir_fd,
794 "cgroup.subtree_control",
795 "-%s", ctrl->ctrl_name);
796 }
797
798 for_each_root(root) {
799 if (!root->we_mounted_it)
800 continue;
801
802 /* This probably does not result in the CGroup root
803 * being destroyed
804 */
805 if (umount2(root->mnt_path, MNT_DETACH))
806 continue;
807
808 SAFE_RMDIR(root->mnt_path);
809 }
810
811 clear_data:
812 for_each_ctrl(ctrl) {
813 ctrl->ctrl_root = NULL;
814 ctrl->we_require_it = 0;
815 }
816
817 for_each_root(root)
818 close_path_fds(root);
819
820 memset(roots, 0, sizeof(roots));
821 }
822
823 __attribute__((nonnull(1)))
cgroup_group_init(struct tst_cgroup_group * const cg,const char * const group_name)824 static void cgroup_group_init(struct tst_cgroup_group *const cg,
825 const char *const group_name)
826 {
827 memset(cg, 0, sizeof(*cg));
828
829 if (!group_name)
830 return;
831
832 if (strlen(group_name) > NAME_MAX)
833 tst_brk(TBROK, "Group name is too long");
834
835 strcpy(cg->group_name, group_name);
836 }
837
838 __attribute__((nonnull(2, 3)))
cgroup_group_add_dir(const struct tst_cgroup_group * const parent,struct tst_cgroup_group * const cg,struct cgroup_dir * const dir)839 static void cgroup_group_add_dir(const struct tst_cgroup_group *const parent,
840 struct tst_cgroup_group *const cg,
841 struct cgroup_dir *const dir)
842 {
843 const struct cgroup_ctrl *ctrl;
844 int i;
845
846 if (dir->dir_root->ver != TST_CGROUP_V1)
847 cg->dirs_by_ctrl[0] = dir;
848
849 for_each_ctrl(ctrl) {
850 if (!has_ctrl(dir->ctrl_field, ctrl))
851 continue;
852
853 cg->dirs_by_ctrl[ctrl->ctrl_indx] = dir;
854
855 if (!parent || dir->dir_root->ver == TST_CGROUP_V1)
856 continue;
857
858 SAFE_CGROUP_PRINTF(parent, "cgroup.subtree_control",
859 "+%s", ctrl->ctrl_name);
860 }
861
862 for (i = 0; cg->dirs[i]; i++)
863 ;
864 cg->dirs[i] = dir;
865 }
866
867 struct tst_cgroup_group *
tst_cgroup_group_mk(const struct tst_cgroup_group * const parent,const char * const group_name)868 tst_cgroup_group_mk(const struct tst_cgroup_group *const parent,
869 const char *const group_name)
870 {
871 struct tst_cgroup_group *cg;
872 struct cgroup_dir *const *dir;
873 struct cgroup_dir *new_dir;
874
875 cg = SAFE_MALLOC(sizeof(*cg));
876 cgroup_group_init(cg, group_name);
877
878 for_each_dir(parent, 0, dir) {
879 new_dir = SAFE_MALLOC(sizeof(*new_dir));
880 cgroup_dir_mk(*dir, group_name, new_dir);
881 cgroup_group_add_dir(parent, cg, new_dir);
882 }
883
884 return cg;
885 }
886
tst_cgroup_group_name(const struct tst_cgroup_group * const cg)887 const char *tst_cgroup_group_name(const struct tst_cgroup_group *const cg)
888 {
889 return cg->group_name;
890 }
891
tst_cgroup_group_rm(struct tst_cgroup_group * const cg)892 struct tst_cgroup_group *tst_cgroup_group_rm(struct tst_cgroup_group *const cg)
893 {
894 struct cgroup_dir **dir;
895
896 for_each_dir(cg, 0, dir) {
897 close((*dir)->dir_fd);
898 SAFE_UNLINKAT((*dir)->dir_parent->dir_fd,
899 (*dir)->dir_name,
900 AT_REMOVEDIR);
901 free(*dir);
902 }
903
904 free(cg);
905 return NULL;
906 }
907
908 __attribute__ ((nonnull, warn_unused_result))
cgroup_file_find(const char * const file,const int lineno,const char * const file_name)909 static const struct cgroup_file *cgroup_file_find(const char *const file,
910 const int lineno,
911 const char *const file_name)
912 {
913 const struct cgroup_file *cfile;
914 const struct cgroup_ctrl *ctrl;
915 char ctrl_name[32];
916 const char *const sep = strchr(file_name, '.');
917 size_t len;
918
919 if (!sep) {
920 tst_brk_(file, lineno, TBROK,
921 "Invalid file name '%s'; did not find controller separator '.'",
922 file_name);
923 return NULL;
924 }
925
926 len = sep - file_name;
927 memcpy(ctrl_name, file_name, len);
928 ctrl_name[len] = '\0';
929
930 ctrl = cgroup_find_ctrl(ctrl_name);
931
932 if (!ctrl) {
933 tst_brk_(file, lineno, TBROK,
934 "Did not find controller '%s'\n", ctrl_name);
935 return NULL;
936 }
937
938 for (cfile = ctrl->files; cfile->file_name; cfile++) {
939 if (!strcmp(file_name, cfile->file_name))
940 break;
941 }
942
943 if (!cfile->file_name) {
944 tst_brk_(file, lineno, TBROK,
945 "Did not find '%s' in '%s'\n",
946 file_name, ctrl->ctrl_name);
947 return NULL;
948 }
949
950 return cfile;
951 }
952
tst_cgroup_ver(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const ctrl_name)953 enum tst_cgroup_ver tst_cgroup_ver(const char *const file, const int lineno,
954 const struct tst_cgroup_group *const cg,
955 const char *const ctrl_name)
956 {
957 const struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name);
958 const struct cgroup_dir *dir;
959
960 if (!strcmp(ctrl_name, "cgroup")) {
961 tst_brk_(file, lineno,
962 TBROK,
963 "cgroup may be present on both V1 and V2 hierarchies");
964 return 0;
965 }
966
967 if (!ctrl) {
968 tst_brk_(file, lineno,
969 TBROK, "Unknown controller '%s'", ctrl_name);
970 return 0;
971 }
972
973 dir = cg->dirs_by_ctrl[ctrl->ctrl_indx];
974
975 if (!dir) {
976 tst_brk_(file, lineno,
977 TBROK, "%s controller not attached to CGroup %s",
978 ctrl_name, cg->group_name);
979 return 0;
980 }
981
982 return dir->dir_root->ver;
983 }
984
985 __attribute__ ((nonnull, warn_unused_result))
cgroup_file_alias(const struct cgroup_file * const cfile,const struct cgroup_dir * const dir)986 static const char *cgroup_file_alias(const struct cgroup_file *const cfile,
987 const struct cgroup_dir *const dir)
988 {
989 if (dir->dir_root->ver != TST_CGROUP_V1)
990 return cfile->file_name;
991
992 if (cfile->ctrl_indx == CTRL_CPUSET &&
993 dir->dir_root->no_cpuset_prefix &&
994 cfile->file_name_v1) {
995 return strchr(cfile->file_name_v1, '.') + 1;
996 }
997
998 return cfile->file_name_v1;
999 }
1000
safe_cgroup_has(const char * const file,const int lineno,const struct tst_cgroup_group * cg,const char * const file_name)1001 int safe_cgroup_has(const char *const file, const int lineno,
1002 const struct tst_cgroup_group *cg,
1003 const char *const file_name)
1004 {
1005 const struct cgroup_file *const cfile =
1006 cgroup_file_find(file, lineno, file_name);
1007 struct cgroup_dir *const *dir;
1008 const char *alias;
1009
1010 if (!cfile)
1011 return 0;
1012
1013 for_each_dir(cg, cfile->ctrl_indx, dir) {
1014 alias = cgroup_file_alias(cfile, *dir);
1015 if (!alias)
1016 continue;
1017
1018 if (!faccessat((*dir)->dir_fd, alias, F_OK, 0))
1019 return 1;
1020
1021 if (errno == ENOENT)
1022 continue;
1023
1024 tst_brk_(file, lineno, TBROK | TERRNO,
1025 "faccessat(%d<%s>, %s, F_OK, 0)",
1026 (*dir)->dir_fd, tst_decode_fd((*dir)->dir_fd), alias);
1027 }
1028
1029 return 0;
1030 }
1031
1032 __attribute__ ((warn_unused_result))
cgroup_group_from_roots(const size_t tree_off)1033 static struct tst_cgroup_group *cgroup_group_from_roots(const size_t tree_off)
1034 {
1035 struct cgroup_root *root;
1036 struct cgroup_dir *dir;
1037 struct tst_cgroup_group *cg;
1038
1039 cg = tst_alloc(sizeof(*cg));
1040 cgroup_group_init(cg, NULL);
1041
1042 for_each_root(root) {
1043 dir = (typeof(dir))(((char *)root) + tree_off);
1044
1045 if (dir->ctrl_field)
1046 cgroup_group_add_dir(NULL, cg, dir);
1047 }
1048
1049 if (cg->dirs[0]) {
1050 strncpy(cg->group_name, cg->dirs[0]->dir_name, NAME_MAX);
1051 return cg;
1052 }
1053
1054 tst_brk(TBROK,
1055 "No CGroups found; maybe you forgot to call tst_cgroup_require?");
1056
1057 return cg;
1058 }
1059
tst_cgroup_get_test_group(void)1060 const struct tst_cgroup_group *tst_cgroup_get_test_group(void)
1061 {
1062 return cgroup_group_from_roots(offsetof(struct cgroup_root, test_dir));
1063 }
1064
tst_cgroup_get_drain_group(void)1065 const struct tst_cgroup_group *tst_cgroup_get_drain_group(void)
1066 {
1067 return cgroup_group_from_roots(offsetof(struct cgroup_root, drain_dir));
1068 }
1069
safe_cgroup_read(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,char * const out,const size_t len)1070 ssize_t safe_cgroup_read(const char *const file, const int lineno,
1071 const struct tst_cgroup_group *const cg,
1072 const char *const file_name,
1073 char *const out, const size_t len)
1074 {
1075 const struct cgroup_file *const cfile =
1076 cgroup_file_find(file, lineno, file_name);
1077 struct cgroup_dir *const *dir;
1078 const char *alias;
1079 size_t prev_len = 0;
1080 char prev_buf[BUFSIZ];
1081 ssize_t read_ret = 0;
1082
1083 for_each_dir(cg, cfile->ctrl_indx, dir) {
1084 alias = cgroup_file_alias(cfile, *dir);
1085 if (!alias)
1086 continue;
1087
1088 if (prev_len)
1089 memcpy(prev_buf, out, prev_len);
1090
1091 read_ret = safe_file_readat(file, lineno,
1092 (*dir)->dir_fd, alias, out, len);
1093 if (read_ret < 0)
1094 continue;
1095
1096 if (prev_len && memcmp(out, prev_buf, prev_len)) {
1097 tst_brk_(file, lineno, TBROK,
1098 "%s has different value across roots",
1099 file_name);
1100 break;
1101 }
1102
1103 prev_len = MIN(sizeof(prev_buf), (size_t)read_ret);
1104 }
1105
1106 out[MAX(read_ret, 0)] = '\0';
1107
1108 return read_ret;
1109 }
1110
safe_cgroup_printf(const char * const file,const int lineno,const struct tst_cgroup_group * cg,const char * const file_name,const char * const fmt,...)1111 void safe_cgroup_printf(const char *const file, const int lineno,
1112 const struct tst_cgroup_group *cg,
1113 const char *const file_name,
1114 const char *const fmt, ...)
1115 {
1116 const struct cgroup_file *const cfile =
1117 cgroup_file_find(file, lineno, file_name);
1118 struct cgroup_dir *const *dir;
1119 const char *alias;
1120 va_list va;
1121
1122 for_each_dir(cg, cfile->ctrl_indx, dir) {
1123 alias = cgroup_file_alias(cfile, *dir);
1124 if (!alias)
1125 continue;
1126
1127 va_start(va, fmt);
1128 safe_file_vprintfat(file, lineno,
1129 (*dir)->dir_fd, alias, fmt, va);
1130 va_end(va);
1131 }
1132 }
1133
safe_cgroup_scanf(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,const char * const fmt,...)1134 void safe_cgroup_scanf(const char *const file, const int lineno,
1135 const struct tst_cgroup_group *const cg,
1136 const char *const file_name,
1137 const char *const fmt, ...)
1138 {
1139 va_list va;
1140 char buf[BUFSIZ];
1141 ssize_t len = safe_cgroup_read(file, lineno,
1142 cg, file_name, buf, sizeof(buf));
1143 const int conv_cnt = tst_count_scanf_conversions(fmt);
1144 int ret;
1145
1146 if (len < 1)
1147 return;
1148
1149 va_start(va, fmt);
1150 ret = vsscanf(buf, fmt, va);
1151 if (ret < 1) {
1152 tst_brk_(file, lineno, TBROK | TERRNO,
1153 "'%s': vsscanf('%s', '%s', ...)", file_name, buf, fmt);
1154 }
1155 va_end(va);
1156
1157 if (conv_cnt == ret)
1158 return;
1159
1160 tst_brk_(file, lineno, TBROK,
1161 "'%s': vsscanf('%s', '%s', ..): Less conversions than expected: %d != %d",
1162 file_name, buf, fmt, ret, conv_cnt);
1163 }
1164
safe_cgroup_lines_scanf(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,const char * const fmt,...)1165 void safe_cgroup_lines_scanf(const char *const file, const int lineno,
1166 const struct tst_cgroup_group *const cg,
1167 const char *const file_name,
1168 const char *const fmt, ...)
1169 {
1170 va_list va;
1171 char buf[BUFSIZ];
1172 ssize_t len = safe_cgroup_read(file, lineno,
1173 cg, file_name, buf, sizeof(buf));
1174 const int conv_cnt = tst_count_scanf_conversions(fmt);
1175 int ret = 0;
1176 char *line, *buf_ptr;
1177
1178 if (len < 1)
1179 return;
1180
1181 line = strtok_r(buf, "\n", &buf_ptr);
1182 while (line && ret != conv_cnt) {
1183 va_start(va, fmt);
1184 ret = vsscanf(line, fmt, va);
1185 va_end(va);
1186
1187 line = strtok_r(NULL, "\n", &buf_ptr);
1188 }
1189
1190 if (conv_cnt == ret)
1191 return;
1192
1193 tst_brk_(file, lineno, TBROK,
1194 "'%s': vsscanf('%s', '%s', ..): Less conversions than expected: %d != %d",
1195 file_name, buf, fmt, ret, conv_cnt);
1196 }
1197
safe_cgroup_occursin(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,const char * const needle)1198 int safe_cgroup_occursin(const char *const file, const int lineno,
1199 const struct tst_cgroup_group *const cg,
1200 const char *const file_name,
1201 const char *const needle)
1202 {
1203 char buf[BUFSIZ];
1204
1205 safe_cgroup_read(file, lineno, cg, file_name, buf, sizeof(buf));
1206
1207 return !!strstr(buf, needle);
1208 }
1209