1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2020 Red Hat, Inc.
4 * Copyright (c) 2020 Li Wang <[email protected]>
5 * Copyright (c) 2020-2021 SUSE LLC <[email protected]>
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
15 #include "tst_test.h"
16 #include "lapi/fcntl.h"
17 #include "lapi/mount.h"
18 #include "tst_safe_file_at.h"
19
20 struct cgroup_root;
21
22 /* A node in a single CGroup hierarchy. It exists mainly for
23 * convenience so that we do not have to traverse the CGroup structure
24 * for frequent operations.
25 *
26 * This is actually a single-linked list not a tree. We only need to
27 * traverse from leaf towards root.
28 */
29 struct cgroup_dir {
30 const char *dir_name;
31 const struct cgroup_dir *dir_parent;
32
33 /* Shortcut to root */
34 const struct cgroup_root *dir_root;
35
36 /* Subsystems (controllers) bit field. Only controllers which
37 * were required and configured for this node are added to
38 * this field. So it may be different from root->css_field.
39 */
40 uint32_t ctrl_field;
41
42 /* In general we avoid having sprintfs everywhere and use
43 * openat, linkat, etc.
44 */
45 int dir_fd;
46
47 int we_created_it:1;
48 };
49
50 /* The root of a CGroup hierarchy/tree */
51 struct cgroup_root {
52 enum tst_cg_ver ver;
53 /* A mount path */
54 char mnt_path[PATH_MAX];
55 /* Subsystems (controllers) bit field. Includes all
56 * controllers found while scanning this root.
57 */
58 uint32_t ctrl_field;
59
60 /* CGroup hierarchy: mnt -> ltp -> {drain, test -> ??? } We
61 * keep a flat reference to ltp, drain and test for
62 * convenience.
63 */
64
65 /* Mount directory */
66 struct cgroup_dir mnt_dir;
67 /* LTP CGroup directory, contains drain and test dirs */
68 struct cgroup_dir ltp_dir;
69 /* Drain CGroup, see cgroup_cleanup */
70 struct cgroup_dir drain_dir;
71 /* CGroup for current test. Which may have children. */
72 struct cgroup_dir test_dir;
73
74 int nsdelegate:1;
75
76 int we_mounted_it:1;
77 /* cpuset is in compatability mode */
78 int no_cpuset_prefix:1;
79 };
80
81 /* Controller sub-systems */
82 enum cgroup_ctrl_indx {
83 CTRL_MEMORY = 1,
84 CTRL_CPU,
85 CTRL_CPUSET,
86 CTRL_IO,
87 CTRL_PIDS,
88 CTRL_HUGETLB,
89 CTRL_CPUACCT,
90 CTRL_DEVICES,
91 CTRL_FREEZER,
92 CTRL_NETCLS,
93 CTRL_NETPRIO,
94 CTRL_BLKIO,
95 CTRL_MISC,
96 CTRL_PERFEVENT,
97 CTRL_DEBUG,
98 CTRL_RDMA,
99 CTRL_BASE
100 };
101 #define CTRLS_MAX CTRL_BASE
102
103 /* At most we can have one cgroup V1 tree for each controller and one
104 * (empty) v2 tree.
105 */
106 #define ROOTS_MAX (CTRLS_MAX + 1)
107
108 /* Describes a controller file or knob
109 *
110 * The primary purpose of this is to map V2 names to V1
111 * names.
112 */
113 struct cgroup_file {
114 /* Canonical name. Is the V2 name unless an item is V1 only */
115 const char *const file_name;
116 /* V1 name or NULL if item is V2 only */
117 const char *const file_name_v1;
118
119 /* The controller this item belongs to or zero for
120 * 'cgroup.<item>'.
121 */
122 const enum cgroup_ctrl_indx ctrl_indx;
123 };
124
125 /* Describes a Controller or subsystem
126 *
127 * Internally the kernel seems to call controllers subsystems and uses
128 * the abbreviations subsys and css.
129 */
130 struct cgroup_ctrl {
131 /* Userland name of the controller (e.g. 'memory' not 'memcg') */
132 const char *const ctrl_name;
133 /* List of files belonging to this controller */
134 const struct cgroup_file *const files;
135 /* Our index for the controller */
136 const enum cgroup_ctrl_indx ctrl_indx;
137
138 /* Runtime; hierarchy the controller is attached to */
139 struct cgroup_root *ctrl_root;
140 /* Runtime; whether we required the controller */
141 int we_require_it:1;
142 };
143
144 struct tst_cg_group {
145 char group_name[NAME_MAX + 1];
146 /* Maps controller ID to the tree which contains it. The V2
147 * tree is at zero even if it contains no controllers.
148 */
149 struct cgroup_dir *dirs_by_ctrl[ROOTS_MAX];
150 /* NULL terminated list of trees */
151 struct cgroup_dir *dirs[ROOTS_MAX + 1];
152 };
153
154 /* If controllers are required via the tst_test struct then this is
155 * populated with the test's CGroup.
156 */
157 static struct tst_cg_group test_group;
158 static struct tst_cg_group drain_group;
159 const struct tst_cg_group *const tst_cg = &test_group;
160 const struct tst_cg_group *const tst_cg_drain = &drain_group;
161
162 /* Always use first item for unified hierarchy */
163 static struct cgroup_root roots[ROOTS_MAX + 1];
164
165 static const struct cgroup_file cgroup_ctrl_files[] = {
166 /* procs exists on V1, however it was read-only until kernel v3.0. */
167 { "cgroup.procs", "tasks", 0 },
168 { "cgroup.controllers", NULL, 0 },
169 { "cgroup.subtree_control", NULL, 0 },
170 { "cgroup.clone_children", "cgroup.clone_children", 0 },
171 { "cgroup.kill", NULL, 0 },
172 { }
173 };
174
175 static const struct cgroup_file memory_ctrl_files[] = {
176 { "memory.current", "memory.usage_in_bytes", CTRL_MEMORY },
177 { "memory.events", NULL, CTRL_MEMORY },
178 { "memory.low", NULL, CTRL_MEMORY },
179 { "memory.min", NULL, CTRL_MEMORY },
180 { "memory.max", "memory.limit_in_bytes", CTRL_MEMORY },
181 { "memory.stat", "memory.stat", CTRL_MEMORY },
182 { "memory.swappiness", "memory.swappiness", CTRL_MEMORY },
183 { "memory.swap.current", "memory.memsw.usage_in_bytes", CTRL_MEMORY },
184 { "memory.swap.max", "memory.memsw.limit_in_bytes", CTRL_MEMORY },
185 { "memory.kmem.usage_in_bytes", "memory.kmem.usage_in_bytes", CTRL_MEMORY },
186 { "memory.kmem.limit_in_bytes", "memory.kmem.limit_in_bytes", CTRL_MEMORY },
187 { }
188 };
189
190 static const struct cgroup_file cpu_ctrl_files[] = {
191 /* The V1 quota and period files were combined in the V2 max
192 * file. The quota is in the first column and if we just print
193 * a single value to the file, it will be treated as the
194 * quota. To get or set the period we need to branch on the
195 * API version.
196 */
197 { "cpu.max", "cpu.cfs_quota_us", CTRL_CPU },
198 { "cpu.cfs_period_us", "cpu.cfs_period_us", CTRL_CPU },
199 { }
200 };
201
202 static const struct cgroup_file cpuset_ctrl_files[] = {
203 { "cpuset.cpus", "cpuset.cpus", CTRL_CPUSET },
204 { "cpuset.mems", "cpuset.mems", CTRL_CPUSET },
205 { "cpuset.memory_migrate", "cpuset.memory_migrate", CTRL_CPUSET },
206 { }
207 };
208
209 static const struct cgroup_file io_ctrl_files[] = {
210 { "io.stat", NULL, CTRL_IO },
211 { }
212 };
213
214 static const struct cgroup_file pids_ctrl_files[] = {
215 { "pids.max", "pids.max", CTRL_PIDS },
216 { "pids.current", "pids.current", CTRL_PIDS },
217 { }
218 };
219
220 static const struct cgroup_file hugetlb_ctrl_files[] = {
221 { }
222 };
223
224 static const struct cgroup_file cpuacct_ctrl_files[] = {
225 { }
226 };
227
228 static const struct cgroup_file devices_ctrl_files[] = {
229 { }
230 };
231
232 static const struct cgroup_file freezer_ctrl_files[] = {
233 { }
234 };
235
236 static const struct cgroup_file net_cls_ctrl_files[] = {
237 { }
238 };
239
240 static const struct cgroup_file net_prio_ctrl_files[] = {
241 { }
242 };
243
244 static const struct cgroup_file blkio_ctrl_files[] = {
245 { }
246 };
247
248 static const struct cgroup_file misc_ctrl_files[] = {
249 { }
250 };
251
252 static const struct cgroup_file perf_event_ctrl_files[] = {
253 { }
254 };
255
256 static const struct cgroup_file debug_ctrl_files[] = {
257 { }
258 };
259
260 static const struct cgroup_file rdma_ctrl_files[] = {
261 { }
262 };
263
264 static const struct cgroup_file base_ctrl_files[] = {
265 { }
266 };
267
268 #define CTRL_NAME_MAX 31
269 #define CGROUP_CTRL_MEMBER(x, y)[y] = { .ctrl_name = #x, .files = \
270 x ## _ctrl_files, .ctrl_indx = y, NULL, 0 }
271
272 /* Lookup tree for item names. */
273 static struct cgroup_ctrl controllers[] = {
274 CGROUP_CTRL_MEMBER(cgroup, 0),
275 CGROUP_CTRL_MEMBER(memory, CTRL_MEMORY),
276 CGROUP_CTRL_MEMBER(cpu, CTRL_CPU),
277 CGROUP_CTRL_MEMBER(cpuset, CTRL_CPUSET),
278 CGROUP_CTRL_MEMBER(io, CTRL_IO),
279 CGROUP_CTRL_MEMBER(pids, CTRL_PIDS),
280 CGROUP_CTRL_MEMBER(hugetlb, CTRL_HUGETLB),
281 CGROUP_CTRL_MEMBER(cpuacct, CTRL_CPUACCT),
282 CGROUP_CTRL_MEMBER(devices, CTRL_DEVICES),
283 CGROUP_CTRL_MEMBER(freezer, CTRL_FREEZER),
284 CGROUP_CTRL_MEMBER(net_cls, CTRL_NETCLS),
285 CGROUP_CTRL_MEMBER(net_prio, CTRL_NETPRIO),
286 CGROUP_CTRL_MEMBER(blkio, CTRL_BLKIO),
287 CGROUP_CTRL_MEMBER(misc, CTRL_MISC),
288 CGROUP_CTRL_MEMBER(perf_event, CTRL_PERFEVENT),
289 CGROUP_CTRL_MEMBER(debug, CTRL_DEBUG),
290 CGROUP_CTRL_MEMBER(rdma, CTRL_RDMA),
291 CGROUP_CTRL_MEMBER(base, CTRL_BASE),
292 { }
293 };
294
295 /* We should probably allow these to be set in environment
296 * variables
297 */
298 static const char *cgroup_ltp_dir = "ltp";
299 static const char *cgroup_ltp_drain_dir = "drain";
300 static char cgroup_test_dir[NAME_MAX + 1];
301 static const char *cgroup_mount_ltp_prefix = "cgroup_";
302 static const char *cgroup_v2_ltp_mount = "unified";
303
304 #define first_root \
305 (roots[0].ver ? roots : roots + 1)
306 #define for_each_root(r) \
307 for ((r) = first_root; (r)->ver; (r)++)
308 #define for_each_v1_root(r) \
309 for ((r) = roots + 1; (r)->ver; (r)++)
310 #define for_each_ctrl(ctrl) \
311 for ((ctrl) = controllers; (ctrl)->ctrl_name; (ctrl)++)
312
313 /* In all cases except one, this only loops once.
314 *
315 * If (ctrl) == 0 and multiple V1 (and a V2) hierarchies are mounted,
316 * then we need to loop over multiple directories. For example if we
317 * need to write to "tasks"/"cgroup.procs" which exists for each
318 * hierarchy.
319 */
320 #define for_each_dir(cg, ctrl, t) \
321 for ((t) = (ctrl) ? (cg)->dirs_by_ctrl + (ctrl) : (cg)->dirs; \
322 *(t); \
323 (t) = (ctrl) ? (cg)->dirs + ROOTS_MAX : (t) + 1)
324
325 __attribute__ ((nonnull))
has_ctrl(const uint32_t ctrl_field,const struct cgroup_ctrl * const ctrl)326 static int has_ctrl(const uint32_t ctrl_field,
327 const struct cgroup_ctrl *const ctrl)
328 {
329 return !!(ctrl_field & (1 << ctrl->ctrl_indx));
330 }
331
332 __attribute__ ((nonnull))
add_ctrl(uint32_t * const ctrl_field,const struct cgroup_ctrl * const ctrl)333 static void add_ctrl(uint32_t *const ctrl_field,
334 const struct cgroup_ctrl *const ctrl)
335 {
336 *ctrl_field |= 1 << ctrl->ctrl_indx;
337 }
338
cgroup_v2_mounted(void)339 static int cgroup_v2_mounted(void)
340 {
341 return !!roots[0].ver;
342 }
343
cgroup_v1_mounted(void)344 static int cgroup_v1_mounted(void)
345 {
346 return !!roots[1].ver;
347 }
348
cgroup_v2_nsdelegate(void)349 static int cgroup_v2_nsdelegate(void)
350 {
351 return !!roots[0].nsdelegate;
352 }
353
cgroup_mounted(void)354 static int cgroup_mounted(void)
355 {
356 return cgroup_v2_mounted() || cgroup_v1_mounted();
357 }
358
359 __attribute__ ((nonnull, warn_unused_result))
cgroup_ctrl_on_v2(const struct cgroup_ctrl * const ctrl)360 static int cgroup_ctrl_on_v2(const struct cgroup_ctrl *const ctrl)
361 {
362 return ctrl->ctrl_root && ctrl->ctrl_root->ver == TST_CG_V2;
363 }
364
365 __attribute__ ((nonnull))
cgroup_dir_mk(const struct cgroup_dir * const parent,const char * const dir_name,struct cgroup_dir * const new)366 static void cgroup_dir_mk(const struct cgroup_dir *const parent,
367 const char *const dir_name,
368 struct cgroup_dir *const new)
369 {
370 const char *dpath;
371 mode_t old_umask = umask(0);
372
373 new->dir_root = parent->dir_root;
374 new->dir_name = dir_name;
375 new->dir_parent = parent;
376 new->ctrl_field = parent->ctrl_field;
377 new->we_created_it = 0;
378
379 if (!mkdirat(parent->dir_fd, dir_name, 0777)) {
380 new->we_created_it = 1;
381 goto opendir;
382 }
383
384 if (errno == EEXIST)
385 goto opendir;
386
387 dpath = tst_decode_fd(parent->dir_fd);
388
389 if (errno == EACCES) {
390 tst_brk(TCONF | TERRNO,
391 "Lack permission to make '%s/%s'; premake it or run as root",
392 dpath, dir_name);
393 } else if (errno == EROFS) {
394 tst_brk(TCONF | TERRNO, "'%s/%s' must not be read-only",
395 dpath, dir_name);
396 } else {
397 tst_brk(TBROK | TERRNO,
398 "mkdirat(%d<%s>, '%s', 0777)",
399 parent->dir_fd, dpath, dir_name);
400 }
401
402 opendir:
403 new->dir_fd = SAFE_OPENAT(parent->dir_fd, dir_name,
404 O_PATH | O_DIRECTORY);
405 umask(old_umask);
406 }
407
408 #define PATH_MAX_STRLEN 4095
409 #define CONFIG_HEADER "ctrl_name ver we_require_it mnt_path we_mounted_it ltp_dir.we_created_it test_dir.dir_name"
410 #define CONFIG_FORMAT "%" TST_TO_STR(CTRL_NAME_MAX) "s\t%d\t%d\t%" TST_TO_STR(PATH_MAX_STRLEN) "s\t%d\t%d\t%" TST_TO_STR(NAME_MAX) "s"
411 /* Prints out the state associated with each controller to be consumed by
412 * tst_cg_load_config.
413 *
414 * The config keeps track of the minimal state needed for tst_cg_cleanup
415 * to cleanup mounts and directories made by tst_cg_require.
416 */
tst_cg_print_config(void)417 void tst_cg_print_config(void)
418 {
419 const struct cgroup_ctrl *ctrl;
420
421 printf("%s\n", CONFIG_HEADER);
422
423 for_each_ctrl(ctrl) {
424 struct cgroup_root *root = ctrl->ctrl_root;
425
426 if (!root)
427 continue;
428
429 printf("%s\t%d\t%d\t%s\t%d\t%d\t%s\n",
430 ctrl->ctrl_name,
431 root->ver,
432 ctrl->we_require_it,
433 root->mnt_path,
434 root->we_mounted_it,
435 root->ltp_dir.we_created_it,
436 root->test_dir.dir_name ? root->test_dir.dir_name : "NULL");
437 }
438 }
439
440 __attribute__ ((nonnull, warn_unused_result))
cgroup_find_ctrl(const char * const ctrl_name,unsigned int strict)441 static struct cgroup_ctrl *cgroup_find_ctrl(const char *const ctrl_name,
442 unsigned int strict)
443 {
444 struct cgroup_ctrl *ctrl;
445 int l = 0;
446 char c = ctrl_name[l];
447
448 while (c == '_' || (c >= 'a' && c <= 'z'))
449 c = ctrl_name[++l];
450
451 if (l > 32 && strict)
452 tst_res(TWARN, "Subsys name len greater than max known value of MAX_CGROUP_TYPE_NAMELEN: %d > 32", l);
453
454 if (!(c == '\n' || c == '\0')) {
455 if (!strict)
456 return NULL;
457
458 tst_brk(TBROK, "Unexpected char in %s: %c", ctrl_name, c);
459 }
460
461 for_each_ctrl(ctrl) {
462 if (!strncmp(ctrl_name, ctrl->ctrl_name, l))
463 return ctrl;
464 }
465
466 return NULL;
467 }
468
cgroup_find_root(const char * const mnt_path)469 static struct cgroup_root *cgroup_find_root(const char *const mnt_path)
470 {
471 struct cgroup_root *root;
472
473 for_each_root(root) {
474 if (!strcmp(root->mnt_path, mnt_path))
475 return root;
476 }
477
478 return NULL;
479 }
480
cgroup_parse_config_line(const char * const config_entry)481 static void cgroup_parse_config_line(const char *const config_entry)
482 {
483 char ctrl_name[CTRL_NAME_MAX + 1], mnt_path[PATH_MAX_STRLEN + 1], test_dir_name[NAME_MAX + 1];
484 int ver, we_require_it, we_mounted_it, ltp_dir_we_created_it, vars_read;
485 struct cgroup_root *root;
486 struct cgroup_ctrl *ctrl;
487
488 vars_read = sscanf(config_entry, CONFIG_FORMAT,
489 ctrl_name, &ver, &we_require_it, mnt_path, &we_mounted_it,
490 <p_dir_we_created_it, test_dir_name);
491
492 if (vars_read != 7)
493 tst_brk(TBROK, "Incorrect number of vars read from config. Config possibly malformed?");
494
495 ctrl = cgroup_find_ctrl(ctrl_name, 1);
496 if (!ctrl)
497 tst_brk(TBROK, "Could not find ctrl from config. Ctrls changing between calls?");
498
499 ctrl->we_require_it = we_require_it;
500
501 root = cgroup_find_root(mnt_path);
502 if (!root)
503 tst_brk(TBROK, "Could not find root from config. Config possibly malformed?");
504
505 if (we_mounted_it)
506 root->we_mounted_it = 1;
507
508 if (!root->ltp_dir.dir_name) {
509 cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
510 cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
511 if (ltp_dir_we_created_it) {
512 root->ltp_dir.we_created_it = 1;
513 root->drain_dir.we_created_it = 1;
514 }
515 }
516
517 if (!root->test_dir.dir_name && strcmp(test_dir_name, "NULL")) {
518 strncpy(cgroup_test_dir, test_dir_name, NAME_MAX + 1);
519 cgroup_dir_mk(&root->ltp_dir, cgroup_test_dir, &root->test_dir);
520 root->test_dir.we_created_it = 1;
521 }
522 }
523
524 /* Load the test state config provided by tst_cg_print_config
525 *
526 * This will reload some internal tst_cgroup state given by the config
527 * that might otherwise have been lost between calls or between different
528 * processes. In particular this is used by testcases/lib/tst_cgctl to
529 * provide access to this C api to shell scripts.
530 *
531 * The config keeps track of the minimal state needed for tst_cg_cleanup
532 * to cleanup mounts and directories created by tst_cg_require.
533 */
tst_cg_load_config(const char * const config)534 void tst_cg_load_config(const char *const config)
535 {
536 char temp_config[BUFSIZ];
537 char *line;
538 const size_t config_len = strlen(config) + 1;
539
540 if (config_len >= BUFSIZ)
541 tst_brk(TBROK, "Config has exceeded buffer size?");
542
543 memcpy(temp_config, config, config_len);
544 temp_config[config_len] = '\0';
545
546 line = strtok(temp_config, "\n");
547 /* Make sure to consume the header. */
548 for (line = strtok(NULL, "\n"); line; line = strtok(NULL, "\n"))
549 cgroup_parse_config_line(line);
550 }
551
552 /* Determine if a mounted cgroup hierarchy is unique and record it if so.
553 *
554 * For CGroups V2 this is very simple as there is only one
555 * hierarchy. We just record which controllers are available and check
556 * if this matches what we read from any previous mount points.
557 *
558 * For V1 the set of controllers S is partitioned into sets {P_1, P_2,
559 * ..., P_n} with one or more controllers in each partion. Each
560 * partition P_n can be mounted multiple times, but the same
561 * controller can not appear in more than one partition. Usually each
562 * partition contains a single controller, but, for example, cpu and
563 * cpuacct are often mounted together in the same partiion.
564 *
565 * Each controller partition has its own hierarchy (root) which we
566 * must track and update independently.
567 */
568 __attribute__ ((nonnull))
cgroup_root_scan(const char * const mnt_type,const char * const mnt_dir,char * const mnt_opts)569 static void cgroup_root_scan(const char *const mnt_type,
570 const char *const mnt_dir,
571 char *const mnt_opts)
572 {
573 struct cgroup_root *root = roots;
574 const struct cgroup_ctrl *const_ctrl;
575 struct cgroup_ctrl *ctrl;
576 uint32_t ctrl_field = 0;
577 int no_prefix = 0;
578 int nsdelegate = 0;
579 char buf[BUFSIZ];
580 char *tok;
581 const int mnt_dfd = SAFE_OPEN(mnt_dir, O_PATH | O_DIRECTORY);
582
583 if (!strcmp(mnt_type, "cgroup"))
584 goto v1;
585
586 SAFE_FILE_READAT(mnt_dfd, "cgroup.controllers", buf, sizeof(buf));
587
588 for (tok = strtok(buf, " "); tok; tok = strtok(NULL, " ")) {
589 const_ctrl = cgroup_find_ctrl(tok, 1);
590 if (const_ctrl)
591 add_ctrl(&ctrl_field, const_ctrl);
592 }
593 for (tok = strtok(mnt_opts, ","); tok; tok = strtok(NULL, ",")) {
594 nsdelegate |= !strcmp("nsdelegate", tok);
595 }
596
597 if (root->ver && ctrl_field == root->ctrl_field)
598 goto discard;
599
600 if (root->ctrl_field)
601 tst_brk(TBROK, "Available V2 controllers are changing between scans?");
602
603 root->ver = TST_CG_V2;
604
605 goto backref;
606
607 v1:
608 for (tok = strtok(mnt_opts, ","); tok; tok = strtok(NULL, ",")) {
609 const_ctrl = cgroup_find_ctrl(tok, 0);
610 if (const_ctrl)
611 add_ctrl(&ctrl_field, const_ctrl);
612
613 no_prefix |= !strcmp("noprefix", tok);
614 }
615
616 if (!ctrl_field)
617 goto discard;
618
619 for_each_v1_root(root) {
620 if (!(ctrl_field & root->ctrl_field))
621 continue;
622
623 if (ctrl_field == root->ctrl_field)
624 goto discard;
625
626 tst_brk(TBROK,
627 "The intersection of two distinct sets of mounted controllers should be null? "
628 "Check '%s' and '%s'", root->mnt_path, mnt_dir);
629 }
630
631 if (root >= roots + ROOTS_MAX) {
632 tst_brk(TBROK,
633 "Unique controller mounts have exceeded our limit %d?",
634 ROOTS_MAX);
635 }
636
637 root->ver = TST_CG_V1;
638
639 backref:
640 strcpy(root->mnt_path, mnt_dir);
641 root->mnt_dir.dir_root = root;
642 root->mnt_dir.dir_name = root->mnt_path;
643 root->mnt_dir.dir_fd = mnt_dfd;
644 root->ctrl_field = ctrl_field;
645 root->no_cpuset_prefix = no_prefix;
646 root->nsdelegate = nsdelegate;
647
648 for_each_ctrl(ctrl) {
649 if (has_ctrl(root->ctrl_field, ctrl))
650 ctrl->ctrl_root = root;
651 }
652
653 return;
654
655 discard:
656 close(mnt_dfd);
657 }
658
tst_cg_scan(void)659 void tst_cg_scan(void)
660 {
661 struct mntent *mnt;
662 FILE *f = setmntent("/proc/self/mounts", "r");
663
664 if (!f) {
665 tst_brk(TBROK | TERRNO, "Can't open /proc/self/mounts");
666 return;
667 }
668
669 mnt = getmntent(f);
670 if (!mnt) {
671 tst_brk(TBROK | TERRNO, "Can't read mounts or no mounts?");
672 return;
673 }
674
675 do {
676 if (strncmp(mnt->mnt_type, "cgroup", 6))
677 continue;
678
679 cgroup_root_scan(mnt->mnt_type, mnt->mnt_dir, mnt->mnt_opts);
680 } while ((mnt = getmntent(f)));
681 }
682
cgroup_mount_v2(void)683 static void cgroup_mount_v2(void)
684 {
685 int ret;
686 char mnt_path[PATH_MAX];
687 const char *tmpdir = tst_get_tmpdir_root();
688
689 sprintf(mnt_path, "%s/%s%s",
690 tmpdir, cgroup_mount_ltp_prefix, cgroup_v2_ltp_mount);
691
692 if (!mkdir(mnt_path, 0777)) {
693 roots[0].mnt_dir.we_created_it = 1;
694 goto mount;
695 }
696
697 if (errno == EEXIST)
698 goto mount;
699
700 if (errno == EACCES) {
701 tst_res(TINFO | TERRNO,
702 "Lack permission to make %s, premake it or run as root",
703 mnt_path);
704 return;
705 }
706
707 tst_brk(TBROK | TERRNO, "mkdir(%s, 0777)", mnt_path);
708 return;
709
710 mount:
711 ret = mount("cgroup2", mnt_path, "cgroup2",
712 0, "memory_recursiveprot");
713
714 if (ret && errno == EINVAL)
715 ret = mount("cgroup2", mnt_path, "cgroup2", 0, NULL);
716
717 if (!ret) {
718 tst_res(TINFO, "Mounted V2 CGroups on %s", mnt_path);
719 tst_cg_scan();
720 roots[0].we_mounted_it = 1;
721 return;
722 }
723
724 tst_res(TINFO | TERRNO, "Could not mount V2 CGroups on %s", mnt_path);
725
726 if (roots[0].mnt_dir.we_created_it) {
727 roots[0].mnt_dir.we_created_it = 0;
728 SAFE_RMDIR(mnt_path);
729 }
730 }
731
732 __attribute__ ((nonnull))
cgroup_mount_v1(struct cgroup_ctrl * const ctrl)733 static void cgroup_mount_v1(struct cgroup_ctrl *const ctrl)
734 {
735 char mnt_path[PATH_MAX];
736 int made_dir = 0;
737 const char *tmpdir = tst_get_tmpdir_root();
738
739 if (ctrl->ctrl_indx == CTRL_BLKIO && controllers[CTRL_IO].ctrl_root) {
740 tst_res(TCONF,
741 "IO controller found on V2 root, skipping blkio mount that would unmount IO controller");
742 return;
743 }
744
745 sprintf(mnt_path, "%s/%s%s",
746 tmpdir, cgroup_mount_ltp_prefix, ctrl->ctrl_name);
747
748 if (!mkdir(mnt_path, 0777)) {
749 made_dir = 1;
750 goto mount;
751 }
752
753 if (errno == EEXIST)
754 goto mount;
755
756 if (errno == EACCES) {
757 tst_res(TINFO | TERRNO,
758 "Lack permission to make %s, premake it or run as root",
759 mnt_path);
760 return;
761 }
762
763 tst_brk(TBROK | TERRNO, "mkdir(%s, 0777)", mnt_path);
764 return;
765
766 mount:
767 if (mount(ctrl->ctrl_name, mnt_path, "cgroup", 0, ctrl->ctrl_name)) {
768 tst_res(TINFO | TERRNO,
769 "Could not mount V1 CGroup on %s", mnt_path);
770
771 if (made_dir)
772 SAFE_RMDIR(mnt_path);
773 return;
774 }
775
776 tst_res(TINFO, "Mounted V1 %s CGroup on %s", ctrl->ctrl_name, mnt_path);
777 tst_cg_scan();
778 if (!ctrl->ctrl_root)
779 return;
780
781 ctrl->ctrl_root->we_mounted_it = 1;
782 ctrl->ctrl_root->mnt_dir.we_created_it = made_dir;
783
784 if (ctrl->ctrl_indx == CTRL_MEMORY) {
785 SAFE_FILE_PRINTFAT(ctrl->ctrl_root->mnt_dir.dir_fd,
786 "memory.use_hierarchy", "%d", 1);
787 }
788 }
789
790 __attribute__ ((nonnull))
cgroup_copy_cpuset(const struct cgroup_root * const root)791 static void cgroup_copy_cpuset(const struct cgroup_root *const root)
792 {
793 char knob_val[BUFSIZ];
794 int i;
795 const char *const n0[] = {"mems", "cpus"};
796 const char *const n1[] = {"cpuset.mems", "cpuset.cpus"};
797 const char *const *const fname = root->no_cpuset_prefix ? n0 : n1;
798
799 for (i = 0; i < 2; i++) {
800 SAFE_FILE_READAT(root->mnt_dir.dir_fd,
801 fname[i], knob_val, sizeof(knob_val));
802 SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
803 fname[i], "%s", knob_val);
804 }
805 }
806
807 /* Ensure the specified controller is available.
808 *
809 * First we check if the specified controller has a known mount point,
810 * if not then we scan the system. If we find it then we goto ensuring
811 * the LTP group exists in the hierarchy the controller is using.
812 *
813 * If we can't find the controller, then we try to create it. First we
814 * check if the V2 hierarchy/tree is mounted. If it isn't then we try
815 * mounting it and look for the controller. If it is already mounted
816 * then we know the controller is not available on V2 on this system.
817 *
818 * If we can't mount V2 or the controller is not on V2, then we try
819 * mounting it on its own V1 tree.
820 *
821 * Once we have mounted the controller somehow, we create a hierarchy
822 * of cgroups. If we are on V2 we first need to enable the controller
823 * for all children of root. Then we create hierarchy described in
824 * tst_cgroup.h.
825 *
826 * If we are using V1 cpuset then we copy the available mems and cpus
827 * from root to the ltp group and set clone_children on the ltp group
828 * to distribute these settings to the test cgroups. This means the
829 * test author does not have to copy these settings before using the
830 * cpuset.
831 *
832 */
tst_cg_require(const char * const ctrl_name,const struct tst_cg_opts * options)833 void tst_cg_require(const char *const ctrl_name,
834 const struct tst_cg_opts *options)
835 {
836 const char *const cgsc = "cgroup.subtree_control";
837 struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name, 1);
838 struct cgroup_root *root;
839 int base = !strcmp(ctrl->ctrl_name, "base");
840
841 if (base && options->needs_ver != TST_CG_V2)
842 tst_brk(TCONF, "Base control only support needs_ver TST_CG_V2!");
843
844 if (!ctrl) {
845 tst_brk(TBROK, "'%s' controller is unknown to LTP", ctrl_name);
846 tst_brk(TBROK, "Calling %s in cleanup?", __func__);
847 return;
848 }
849
850 if (ctrl->we_require_it)
851 tst_res(TWARN, "Duplicate %s(%s, )", __func__, ctrl->ctrl_name);
852
853 ctrl->we_require_it = 1;
854
855 if (ctrl->ctrl_root)
856 goto mkdirs;
857
858 tst_cg_scan();
859
860 if (ctrl->ctrl_root)
861 goto mkdirs;
862
863 if (!cgroup_v2_mounted() && options->needs_ver != TST_CG_V1)
864 cgroup_mount_v2();
865
866 if (ctrl->ctrl_root)
867 goto mkdirs;
868
869 if (options->needs_ver != TST_CG_V2)
870 cgroup_mount_v1(ctrl);
871
872 if (base)
873 ctrl->ctrl_root = roots;
874
875 if (!ctrl->ctrl_root) {
876 tst_brk(TCONF,
877 "'%s' controller required, but not available",
878 ctrl->ctrl_name);
879 return;
880 }
881
882 mkdirs:
883 root = ctrl->ctrl_root;
884
885 if (options->needs_nsdelegate && cgroup_v2_mounted() && !cgroup_v2_nsdelegate())
886 tst_brk(TCONF, "Requires cgroup2 to be mounted with nsdelegate");
887
888 add_ctrl(&root->mnt_dir.ctrl_field, ctrl);
889
890 if (cgroup_ctrl_on_v2(ctrl) && options->needs_ver == TST_CG_V1) {
891 tst_brk(TCONF,
892 "V1 '%s' controller required, but it's mounted on V2",
893 ctrl->ctrl_name);
894 }
895 if (!cgroup_ctrl_on_v2(ctrl) && options->needs_ver == TST_CG_V2) {
896 tst_brk(TCONF,
897 "V2 '%s' controller required, but it's mounted on V1",
898 ctrl->ctrl_name);
899 }
900
901 if (cgroup_ctrl_on_v2(ctrl) && !base) {
902 if (root->we_mounted_it) {
903 SAFE_FILE_PRINTFAT(root->mnt_dir.dir_fd,
904 cgsc, "+%s", ctrl->ctrl_name);
905 } else {
906 tst_file_printfat(root->mnt_dir.dir_fd,
907 cgsc, "+%s", ctrl->ctrl_name);
908 }
909 }
910
911 if (!root->ltp_dir.dir_fd)
912 cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
913 else
914 root->ltp_dir.ctrl_field |= root->mnt_dir.ctrl_field;
915
916 if (!base) {
917 if (cgroup_ctrl_on_v2(ctrl)) {
918 SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
919 cgsc, "+%s", ctrl->ctrl_name);
920 } else {
921 SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
922 "cgroup.clone_children", "%d", 1);
923
924 if (ctrl->ctrl_indx == CTRL_CPUSET)
925 cgroup_copy_cpuset(root);
926 }
927 }
928
929 cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
930
931 if (options->test_pid)
932 sprintf(cgroup_test_dir, "test-%d", options->test_pid);
933 else
934 sprintf(cgroup_test_dir, "test-%d", getpid());
935
936 cgroup_dir_mk(&root->ltp_dir, cgroup_test_dir, &root->test_dir);
937 }
938
cgroup_drain(const enum tst_cg_ver ver,const int source_dfd,const int dest_dfd)939 static void cgroup_drain(const enum tst_cg_ver ver,
940 const int source_dfd, const int dest_dfd)
941 {
942 char pid_list[BUFSIZ];
943 char *tok;
944 const char *const file_name =
945 ver == TST_CG_V1 ? "tasks" : "cgroup.procs";
946 int fd;
947 ssize_t ret;
948
949 ret = SAFE_FILE_READAT(source_dfd, file_name,
950 pid_list, sizeof(pid_list));
951 if (ret < 0)
952 return;
953
954 fd = SAFE_OPENAT(dest_dfd, file_name, O_WRONLY);
955 if (fd < 0)
956 return;
957
958 for (tok = strtok(pid_list, "\n"); tok; tok = strtok(NULL, "\n")) {
959 ret = dprintf(fd, "%s", tok);
960
961 if (ret < (ssize_t)strlen(tok))
962 tst_brk(TBROK | TERRNO, "Failed to drain %s", tok);
963 }
964 SAFE_CLOSE(fd);
965 }
966
967 __attribute__ ((nonnull))
close_path_fds(struct cgroup_root * const root)968 static void close_path_fds(struct cgroup_root *const root)
969 {
970 if (root->test_dir.dir_fd > 0)
971 SAFE_CLOSE(root->test_dir.dir_fd);
972 if (root->ltp_dir.dir_fd > 0)
973 SAFE_CLOSE(root->ltp_dir.dir_fd);
974 if (root->drain_dir.dir_fd > 0)
975 SAFE_CLOSE(root->drain_dir.dir_fd);
976 if (root->mnt_dir.dir_fd > 0)
977 SAFE_CLOSE(root->mnt_dir.dir_fd);
978 }
979
980 /* Maybe remove CGroups used during testing and clear our data
981 *
982 * This will never remove CGroups we did not create to allow tests to
983 * be run in parallel.
984 *
985 * Each test process is given its own unique CGroup. Unless we want to
986 * stress test the CGroup system. We should at least remove these
987 * unique per test CGroups.
988 *
989 * We probably also want to remove the LTP parent CGroup, although
990 * this may have been created by the system manager or another test
991 * (see notes on parallel testing).
992 *
993 * On systems with no initial CGroup setup we may try to destroy the
994 * CGroup roots we mounted so that they can be recreated by another
995 * test. Note that successfully unmounting a CGroup root does not
996 * necessarily indicate that it was destroyed.
997 *
998 * The ltp/drain CGroup is required for cleaning up test CGroups when
999 * we can not move them to the root CGroup. CGroups can only be
1000 * removed when they have no members and only leaf or root CGroups may
1001 * have processes within them. As test processes create and destroy
1002 * their own CGroups they must move themselves either to root or
1003 * another leaf CGroup. So we move them to drain while destroying the
1004 * unique test CGroup.
1005 *
1006 * If we have access to root and created the LTP CGroup we then move
1007 * the test process to root and destroy the drain and LTP
1008 * CGroups. Otherwise we just leave the test process to die in the
1009 * drain, much like many a unwanted terrapin.
1010 *
1011 * Finally we clear any data we have collected on CGroups. This will
1012 * happen regardless of whether anything was removed.
1013 */
tst_cg_cleanup(void)1014 void tst_cg_cleanup(void)
1015 {
1016 struct cgroup_root *root;
1017 struct cgroup_ctrl *ctrl;
1018
1019 if (!cgroup_mounted())
1020 goto clear_data;
1021
1022 for_each_root(root) {
1023 if (!root->test_dir.dir_name)
1024 continue;
1025
1026 cgroup_drain(root->ver,
1027 root->test_dir.dir_fd, root->drain_dir.dir_fd);
1028 SAFE_UNLINKAT(root->ltp_dir.dir_fd, root->test_dir.dir_name,
1029 AT_REMOVEDIR);
1030 }
1031
1032 for_each_root(root) {
1033 if (!root->ltp_dir.we_created_it)
1034 continue;
1035
1036 cgroup_drain(root->ver,
1037 root->drain_dir.dir_fd, root->mnt_dir.dir_fd);
1038
1039 if (root->drain_dir.dir_name) {
1040 SAFE_UNLINKAT(root->ltp_dir.dir_fd,
1041 root->drain_dir.dir_name, AT_REMOVEDIR);
1042 }
1043
1044 if (root->ltp_dir.dir_name) {
1045 SAFE_UNLINKAT(root->mnt_dir.dir_fd,
1046 root->ltp_dir.dir_name, AT_REMOVEDIR);
1047 }
1048 }
1049
1050 for_each_ctrl(ctrl) {
1051 if (!cgroup_ctrl_on_v2(ctrl) || !ctrl->ctrl_root->we_mounted_it
1052 || !strcmp(ctrl->ctrl_name, "base"))
1053 continue;
1054
1055 SAFE_FILE_PRINTFAT(ctrl->ctrl_root->mnt_dir.dir_fd,
1056 "cgroup.subtree_control",
1057 "-%s", ctrl->ctrl_name);
1058 }
1059
1060 for_each_root(root) {
1061 if (!root->we_mounted_it)
1062 continue;
1063
1064 /* This probably does not result in the CGroup root
1065 * being destroyed
1066 */
1067 if (umount2(root->mnt_path, MNT_DETACH))
1068 continue;
1069
1070 SAFE_RMDIR(root->mnt_path);
1071 }
1072
1073 clear_data:
1074 for_each_ctrl(ctrl) {
1075 ctrl->ctrl_root = NULL;
1076 ctrl->we_require_it = 0;
1077 }
1078
1079 for_each_root(root)
1080 close_path_fds(root);
1081
1082 memset(roots, 0, sizeof(roots));
1083 }
1084
1085 __attribute__((nonnull(2, 3)))
cgroup_group_add_dir(const struct tst_cg_group * const parent,struct tst_cg_group * const cg,struct cgroup_dir * const dir)1086 static void cgroup_group_add_dir(const struct tst_cg_group *const parent,
1087 struct tst_cg_group *const cg,
1088 struct cgroup_dir *const dir)
1089 {
1090 const struct cgroup_ctrl *ctrl;
1091 int i;
1092
1093 if (dir->dir_root->ver != TST_CG_V1)
1094 cg->dirs_by_ctrl[0] = dir;
1095
1096 for_each_ctrl(ctrl) {
1097 if (!has_ctrl(dir->ctrl_field, ctrl))
1098 continue;
1099
1100 cg->dirs_by_ctrl[ctrl->ctrl_indx] = dir;
1101
1102 if (!parent || dir->dir_root->ver == TST_CG_V1)
1103 continue;
1104
1105 if (strcmp(ctrl->ctrl_name, "base")) {
1106 SAFE_CG_PRINTF(parent, "cgroup.subtree_control",
1107 "+%s", ctrl->ctrl_name);
1108 }
1109 }
1110
1111 for (i = 0; cg->dirs[i]; i++)
1112 ;
1113 cg->dirs[i] = dir;
1114 }
1115
1116 struct tst_cg_group *
tst_cg_group_mk(const struct tst_cg_group * const parent,const char * const group_name_fmt,...)1117 tst_cg_group_mk(const struct tst_cg_group *const parent,
1118 const char *const group_name_fmt, ...)
1119 {
1120 struct tst_cg_group *cg;
1121 struct cgroup_dir *const *dir;
1122 struct cgroup_dir *new_dir;
1123 va_list ap;
1124 size_t name_len;
1125
1126 cg = SAFE_MALLOC(sizeof(*cg));
1127 memset(cg, 0, sizeof(*cg));
1128
1129 va_start(ap, group_name_fmt);
1130 name_len = vsnprintf(cg->group_name, NAME_MAX,
1131 group_name_fmt, ap);
1132 va_end(ap);
1133
1134 if (name_len >= NAME_MAX)
1135 tst_brk(TBROK, "CGroup name is too long");
1136
1137 for_each_dir(parent, 0, dir) {
1138 new_dir = SAFE_MALLOC(sizeof(*new_dir));
1139 cgroup_dir_mk(*dir, cg->group_name, new_dir);
1140 cgroup_group_add_dir(parent, cg, new_dir);
1141 }
1142
1143 return cg;
1144 }
1145
tst_cg_group_name(const struct tst_cg_group * const cg)1146 const char *tst_cg_group_name(const struct tst_cg_group *const cg)
1147 {
1148 return cg->group_name;
1149 }
1150
tst_cg_group_unified_dir_fd(const struct tst_cg_group * const cg)1151 int tst_cg_group_unified_dir_fd(const struct tst_cg_group *const cg)
1152 {
1153 if(cg->dirs_by_ctrl[0])
1154 return cg->dirs_by_ctrl[0]->dir_fd;
1155
1156 return -1;
1157 }
1158
tst_cg_group_rm(struct tst_cg_group * const cg)1159 struct tst_cg_group *tst_cg_group_rm(struct tst_cg_group *const cg)
1160 {
1161 struct cgroup_dir **dir;
1162
1163 for_each_dir(cg, 0, dir) {
1164 close((*dir)->dir_fd);
1165 SAFE_UNLINKAT((*dir)->dir_parent->dir_fd,
1166 (*dir)->dir_name,
1167 AT_REMOVEDIR);
1168 free(*dir);
1169 }
1170
1171 free(cg);
1172 return NULL;
1173 }
1174
1175 __attribute__ ((nonnull, warn_unused_result))
cgroup_file_find(const char * const file,const int lineno,const char * const file_name)1176 static const struct cgroup_file *cgroup_file_find(const char *const file,
1177 const int lineno,
1178 const char *const file_name)
1179 {
1180 const struct cgroup_file *cfile;
1181 const struct cgroup_ctrl *ctrl;
1182 char ctrl_name[CTRL_NAME_MAX + 1];
1183 const char *const sep = strchr(file_name, '.');
1184 size_t len;
1185
1186 if (!sep) {
1187 tst_brk_(file, lineno, TBROK,
1188 "Invalid file name '%s'; did not find controller separator '.'",
1189 file_name);
1190 return NULL;
1191 }
1192
1193 len = sep - file_name;
1194 memcpy(ctrl_name, file_name, len);
1195 ctrl_name[len] = '\0';
1196
1197 ctrl = cgroup_find_ctrl(ctrl_name, 1);
1198
1199 if (!ctrl) {
1200 tst_brk_(file, lineno, TBROK,
1201 "Did not find controller '%s'\n", ctrl_name);
1202 return NULL;
1203 }
1204
1205 for (cfile = ctrl->files; cfile->file_name; cfile++) {
1206 if (!strcmp(file_name, cfile->file_name))
1207 break;
1208 }
1209
1210 if (!cfile->file_name) {
1211 tst_brk_(file, lineno, TBROK,
1212 "Did not find '%s' in '%s'\n",
1213 file_name, ctrl->ctrl_name);
1214 return NULL;
1215 }
1216
1217 return cfile;
1218 }
1219
tst_cg_ver(const char * const file,const int lineno,const struct tst_cg_group * const cg,const char * const ctrl_name)1220 enum tst_cg_ver tst_cg_ver(const char *const file, const int lineno,
1221 const struct tst_cg_group *const cg,
1222 const char *const ctrl_name)
1223 {
1224 const struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name, 1);
1225 const struct cgroup_dir *dir;
1226
1227 if (!strcmp(ctrl_name, "cgroup")) {
1228 tst_brk_(file, lineno,
1229 TBROK,
1230 "cgroup may be present on both V1 and V2 hierarchies");
1231 return 0;
1232 }
1233
1234 if (!ctrl) {
1235 tst_brk_(file, lineno,
1236 TBROK, "Unknown controller '%s'", ctrl_name);
1237 return 0;
1238 }
1239
1240 dir = cg->dirs_by_ctrl[ctrl->ctrl_indx];
1241
1242 if (!dir) {
1243 tst_brk_(file, lineno,
1244 TBROK, "%s controller not attached to CGroup %s",
1245 ctrl_name, cg->group_name);
1246 return 0;
1247 }
1248
1249 return dir->dir_root->ver;
1250 }
1251
1252 __attribute__ ((nonnull, warn_unused_result))
cgroup_file_alias(const struct cgroup_file * const cfile,const struct cgroup_dir * const dir)1253 static const char *cgroup_file_alias(const struct cgroup_file *const cfile,
1254 const struct cgroup_dir *const dir)
1255 {
1256 if (dir->dir_root->ver != TST_CG_V1)
1257 return cfile->file_name;
1258
1259 if (cfile->ctrl_indx == CTRL_CPUSET &&
1260 dir->dir_root->no_cpuset_prefix &&
1261 cfile->file_name_v1) {
1262 return strchr(cfile->file_name_v1, '.') + 1;
1263 }
1264
1265 return cfile->file_name_v1;
1266 }
1267
safe_cg_has(const char * const file,const int lineno,const struct tst_cg_group * cg,const char * const file_name)1268 int safe_cg_has(const char *const file, const int lineno,
1269 const struct tst_cg_group *cg,
1270 const char *const file_name)
1271 {
1272 const struct cgroup_file *const cfile =
1273 cgroup_file_find(file, lineno, file_name);
1274 struct cgroup_dir *const *dir;
1275 const char *alias;
1276
1277 if (!cfile)
1278 return 0;
1279
1280 for_each_dir(cg, cfile->ctrl_indx, dir) {
1281 alias = cgroup_file_alias(cfile, *dir);
1282 if (!alias)
1283 continue;
1284
1285 if (!faccessat((*dir)->dir_fd, alias, F_OK, 0))
1286 return 1;
1287
1288 if (errno == ENOENT)
1289 continue;
1290
1291 tst_brk_(file, lineno, TBROK | TERRNO,
1292 "faccessat(%d<%s>, %s, F_OK, 0)",
1293 (*dir)->dir_fd, tst_decode_fd((*dir)->dir_fd), alias);
1294 }
1295
1296 return 0;
1297 }
1298
group_from_roots(struct tst_cg_group * const cg)1299 static void group_from_roots(struct tst_cg_group *const cg)
1300 {
1301 struct cgroup_root *root;
1302
1303 if (cg->group_name[0]) {
1304 tst_brk(TBROK,
1305 "%s CGroup already initialized",
1306 cg == &test_group ? "Test" : "Drain");
1307 }
1308
1309 for_each_root(root) {
1310 struct cgroup_dir *dir =
1311 cg == &test_group ? &root->test_dir : &root->drain_dir;
1312
1313 if (dir->ctrl_field)
1314 cgroup_group_add_dir(NULL, cg, dir);
1315 }
1316
1317 if (cg->dirs[0]) {
1318 strncpy(cg->group_name, cg->dirs[0]->dir_name, NAME_MAX);
1319 return;
1320 }
1321
1322 tst_brk(TBROK,
1323 "No CGroups found; maybe you forgot to call tst_cg_require?");
1324 }
1325
tst_cg_init(void)1326 void tst_cg_init(void)
1327 {
1328 group_from_roots(&test_group);
1329 group_from_roots(&drain_group);
1330 }
1331
safe_cg_read(const char * const file,const int lineno,const struct tst_cg_group * const cg,const char * const file_name,char * const out,const size_t len)1332 ssize_t safe_cg_read(const char *const file, const int lineno,
1333 const struct tst_cg_group *const cg,
1334 const char *const file_name,
1335 char *const out, const size_t len)
1336 {
1337 const struct cgroup_file *const cfile =
1338 cgroup_file_find(file, lineno, file_name);
1339 struct cgroup_dir *const *dir;
1340 const char *alias;
1341 size_t prev_len = 0;
1342 char prev_buf[BUFSIZ];
1343 ssize_t read_ret = 0;
1344
1345 for_each_dir(cg, cfile->ctrl_indx, dir) {
1346 alias = cgroup_file_alias(cfile, *dir);
1347 if (!alias)
1348 continue;
1349
1350 if (prev_len)
1351 memcpy(prev_buf, out, prev_len);
1352
1353 read_ret = safe_file_readat(file, lineno,
1354 (*dir)->dir_fd, alias, out, len);
1355 if (read_ret < 0)
1356 continue;
1357
1358 if (prev_len && memcmp(out, prev_buf, prev_len)) {
1359 tst_brk_(file, lineno, TBROK,
1360 "%s has different value across roots",
1361 file_name);
1362 break;
1363 }
1364
1365 prev_len = MIN(sizeof(prev_buf), (size_t)read_ret);
1366 }
1367
1368 out[MAX(read_ret, (ssize_t)0)] = '\0';
1369
1370 return read_ret;
1371 }
1372
safe_cg_printf(const char * const file,const int lineno,const struct tst_cg_group * cg,const char * const file_name,const char * const fmt,...)1373 void safe_cg_printf(const char *const file, const int lineno,
1374 const struct tst_cg_group *cg,
1375 const char *const file_name,
1376 const char *const fmt, ...)
1377 {
1378 const struct cgroup_file *const cfile =
1379 cgroup_file_find(file, lineno, file_name);
1380 struct cgroup_dir *const *dir;
1381 const char *alias;
1382 va_list va;
1383
1384 for_each_dir(cg, cfile->ctrl_indx, dir) {
1385 alias = cgroup_file_alias(cfile, *dir);
1386 if (!alias)
1387 continue;
1388
1389 va_start(va, fmt);
1390 safe_file_vprintfat(file, lineno,
1391 (*dir)->dir_fd, alias, fmt, va);
1392 va_end(va);
1393 }
1394 }
1395
safe_cg_open(const char * const file,const int lineno,const struct tst_cg_group * cg,const char * const file_name,int flags,int * fds)1396 int safe_cg_open(const char *const file, const int lineno,
1397 const struct tst_cg_group *cg,
1398 const char *const file_name, int flags, int *fds)
1399 {
1400 const struct cgroup_file *const cfile =
1401 cgroup_file_find(file, lineno, file_name);
1402 struct cgroup_dir *const *dir;
1403 const char *alias;
1404 int i = 0;
1405
1406 for_each_dir(cg, cfile->ctrl_indx, dir) {
1407 alias = cgroup_file_alias(cfile, *dir);
1408 if (!alias)
1409 continue;
1410
1411 fds[i++] = safe_openat(file, lineno, (*dir)->dir_fd, alias, flags);
1412 }
1413
1414 return i;
1415 }
1416
safe_cg_fchown(const char * const file,const int lineno,const struct tst_cg_group * cg,const char * const file_name,uid_t owner,gid_t group)1417 void safe_cg_fchown(const char *const file, const int lineno,
1418 const struct tst_cg_group *cg,
1419 const char *const file_name,
1420 uid_t owner, gid_t group)
1421 {
1422 const struct cgroup_file *const cfile =
1423 cgroup_file_find(file, lineno, file_name);
1424 struct cgroup_dir *const *dir;
1425 const char *alias;
1426
1427 for_each_dir(cg, cfile->ctrl_indx, dir) {
1428 alias = cgroup_file_alias(cfile, *dir);
1429 if (!alias)
1430 continue;
1431
1432 safe_fchownat(file, lineno, (*dir)->dir_fd, alias, owner, group, 0);
1433 }
1434 }
1435
1436
safe_cg_scanf(const char * const file,const int lineno,const struct tst_cg_group * const cg,const char * const file_name,const char * const fmt,...)1437 void safe_cg_scanf(const char *const file, const int lineno,
1438 const struct tst_cg_group *const cg,
1439 const char *const file_name,
1440 const char *const fmt, ...)
1441 {
1442 va_list va;
1443 char buf[BUFSIZ];
1444 ssize_t len = safe_cg_read(file, lineno,
1445 cg, file_name, buf, sizeof(buf));
1446 const int conv_cnt = tst_count_scanf_conversions(fmt);
1447 int ret;
1448
1449 if (len < 1)
1450 return;
1451
1452 va_start(va, fmt);
1453 ret = vsscanf(buf, fmt, va);
1454 if (ret < 1) {
1455 tst_brk_(file, lineno, TBROK | TERRNO,
1456 "'%s': vsscanf('%s', '%s', ...)", file_name, buf, fmt);
1457 }
1458 va_end(va);
1459
1460 if (conv_cnt == ret)
1461 return;
1462
1463 tst_brk_(file, lineno, TBROK,
1464 "'%s': vsscanf('%s', '%s', ..): Less conversions than expected: %d != %d",
1465 file_name, buf, fmt, ret, conv_cnt);
1466 }
1467
safe_cg_lines_scanf(const char * const file,const int lineno,const struct tst_cg_group * const cg,const char * const file_name,const char * const fmt,...)1468 void safe_cg_lines_scanf(const char *const file, const int lineno,
1469 const struct tst_cg_group *const cg,
1470 const char *const file_name,
1471 const char *const fmt, ...)
1472 {
1473 va_list va;
1474 char buf[BUFSIZ];
1475 ssize_t len = safe_cg_read(file, lineno,
1476 cg, file_name, buf, sizeof(buf));
1477 const int conv_cnt = tst_count_scanf_conversions(fmt);
1478 int ret = 0;
1479 char *line, *buf_ptr;
1480
1481 if (len < 1)
1482 return;
1483
1484 line = strtok_r(buf, "\n", &buf_ptr);
1485 while (line && ret != conv_cnt) {
1486 va_start(va, fmt);
1487 ret = vsscanf(line, fmt, va);
1488 va_end(va);
1489
1490 line = strtok_r(NULL, "\n", &buf_ptr);
1491 }
1492
1493 if (conv_cnt == ret)
1494 return;
1495
1496 tst_brk_(file, lineno, TBROK,
1497 "'%s': vsscanf('%s', '%s', ..): Less conversions than expected: %d != %d",
1498 file_name, buf, fmt, ret, conv_cnt);
1499 }
1500
safe_cg_occursin(const char * const file,const int lineno,const struct tst_cg_group * const cg,const char * const file_name,const char * const needle)1501 int safe_cg_occursin(const char *const file, const int lineno,
1502 const struct tst_cg_group *const cg,
1503 const char *const file_name,
1504 const char *const needle)
1505 {
1506 char buf[BUFSIZ];
1507
1508 safe_cg_read(file, lineno, cg, file_name, buf, sizeof(buf));
1509
1510 return !!strstr(buf, needle);
1511 }
1512