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