xref: /aosp_15_r20/external/ltp/lib/tst_cgroup.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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 		&ltp_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