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