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