• 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 #include <sys/mount.h>
15 
16 #include "tst_test.h"
17 #include "lapi/fcntl.h"
18 #include "lapi/mount.h"
19 #include "lapi/mkdirat.h"
20 #include "tst_safe_file_at.h"
21 #include "tst_cgroup.h"
22 
23 struct cgroup_root;
24 
25 /* A node in a single CGroup hierarchy. It exists mainly for
26  * convenience so that we do not have to traverse the CGroup structure
27  * for frequent operations.
28  *
29  * This is actually a single-linked list not a tree. We only need to
30  * traverse from leaf towards root.
31  */
32 struct cgroup_dir {
33 	const char *dir_name;
34 	const struct cgroup_dir *dir_parent;
35 
36 	/* Shortcut to root */
37 	const struct cgroup_root *dir_root;
38 
39 	/* Subsystems (controllers) bit field. Only controllers which
40 	 * were required and configured for this node are added to
41 	 * this field. So it may be different from root->css_field.
42 	 */
43 	uint32_t ctrl_field;
44 
45 	/* In general we avoid having sprintfs everywhere and use
46 	 * openat, linkat, etc.
47 	 */
48 	int dir_fd;
49 
50 	int we_created_it:1;
51 };
52 
53 /* The root of a CGroup hierarchy/tree */
54 struct cgroup_root {
55 	enum tst_cgroup_ver ver;
56 	/* A mount path */
57 	char mnt_path[PATH_MAX];
58 	/* Subsystems (controllers) bit field. Includes all
59 	 * controllers found while scanning this root.
60 	 */
61 	uint32_t ctrl_field;
62 
63 	/* CGroup hierarchy: mnt -> ltp -> {drain, test -> ??? } We
64 	 * keep a flat reference to ltp, drain and test for
65 	 * convenience.
66 	 */
67 
68 	/* Mount directory */
69 	struct cgroup_dir mnt_dir;
70 	/* LTP CGroup directory, contains drain and test dirs */
71 	struct cgroup_dir ltp_dir;
72 	/* Drain CGroup, see cgroup_cleanup */
73 	struct cgroup_dir drain_dir;
74 	/* CGroup for current test. Which may have children. */
75 	struct cgroup_dir test_dir;
76 
77 	int we_mounted_it:1;
78 	/* cpuset is in compatability mode */
79 	int no_cpuset_prefix:1;
80 };
81 
82 /* Controller sub-systems */
83 enum cgroup_ctrl_indx {
84 	CTRL_MEMORY = 1,
85 	CTRL_CPU,
86 	CTRL_CPUSET,
87 };
88 #define CTRLS_MAX CTRL_CPUSET
89 
90 /* At most we can have one cgroup V1 tree for each controller and one
91  * (empty) v2 tree.
92  */
93 #define ROOTS_MAX (CTRLS_MAX + 1)
94 
95 /* Describes a controller file or knob
96  *
97  * The primary purpose of this is to map V2 names to V1
98  * names.
99  */
100 struct cgroup_file {
101 	/* Canonical name. Is the V2 name unless an item is V1 only */
102 	const char *const file_name;
103 	/* V1 name or NULL if item is V2 only */
104 	const char *const file_name_v1;
105 
106 	/* The controller this item belongs to or zero for
107 	 * 'cgroup.<item>'.
108 	 */
109 	const enum cgroup_ctrl_indx ctrl_indx;
110 };
111 
112 /* Describes a Controller or subsystem
113  *
114  * Internally the kernel seems to call controllers subsystems and uses
115  * the abbreviations subsys and css.
116  */
117 struct cgroup_ctrl {
118 	/* Userland name of the controller (e.g. 'memory' not 'memcg') */
119 	const char *const ctrl_name;
120 	/* List of files belonging to this controller */
121 	const struct cgroup_file *const files;
122 	/* Our index for the controller */
123 	const enum cgroup_ctrl_indx ctrl_indx;
124 
125 	/* Runtime; hierarchy the controller is attached to */
126 	struct cgroup_root *ctrl_root;
127 	/* Runtime; whether we required the controller */
128 	int we_require_it:1;
129 };
130 
131 struct tst_cgroup_group {
132 	char group_name[NAME_MAX + 1];
133 	/* Maps controller ID to the tree which contains it. The V2
134 	 * tree is at zero even if it contains no controllers.
135 	 */
136 	struct cgroup_dir *dirs_by_ctrl[ROOTS_MAX];
137 	/* NULL terminated list of trees */
138 	struct cgroup_dir *dirs[ROOTS_MAX + 1];
139 };
140 
141 /* Always use first item for unified hierarchy */
142 static struct cgroup_root roots[ROOTS_MAX + 1];
143 
144 static const struct cgroup_file cgroup_ctrl_files[] = {
145 	/* procs exists on V1, however it was read-only until kernel v3.0. */
146 	{ "cgroup.procs", "tasks", 0 },
147 	{ "cgroup.controllers", NULL, 0 },
148 	{ "cgroup.subtree_control", NULL, 0 },
149 	{ "cgroup.clone_children", "cgroup.clone_children", 0 },
150 	{ }
151 };
152 
153 static const struct cgroup_file memory_ctrl_files[] = {
154 	{ "memory.current", "memory.usage_in_bytes", CTRL_MEMORY },
155 	{ "memory.max", "memory.limit_in_bytes", CTRL_MEMORY },
156 	{ "memory.stat", "memory.stat", CTRL_MEMORY },
157 	{ "memory.swappiness", "memory.swappiness", CTRL_MEMORY },
158 	{ "memory.swap.current", "memory.memsw.usage_in_bytes", CTRL_MEMORY },
159 	{ "memory.swap.max", "memory.memsw.limit_in_bytes", CTRL_MEMORY },
160 	{ "memory.kmem.usage_in_bytes", "memory.kmem.usage_in_bytes", CTRL_MEMORY },
161 	{ "memory.kmem.limit_in_bytes", "memory.kmem.limit_in_bytes", CTRL_MEMORY },
162 	{ }
163 };
164 
165 static const struct cgroup_file cpu_ctrl_files[] = {
166 	/* The V1 quota and period files were combined in the V2 max
167 	 * file. The quota is in the first column and if we just print
168 	 * a single value to the file, it will be treated as the
169 	 * quota. To get or set the period we need to branch on the
170 	 * API version.
171 	 */
172 	{ "cpu.max", "cpu.cfs_quota_us", CTRL_CPU },
173 	{ "cpu.cfs_period_us", "cpu.cfs_period_us", CTRL_CPU },
174 	{ }
175 };
176 
177 static const struct cgroup_file cpuset_ctrl_files[] = {
178 	{ "cpuset.cpus", "cpuset.cpus", CTRL_CPUSET },
179 	{ "cpuset.mems", "cpuset.mems", CTRL_CPUSET },
180 	{ "cpuset.memory_migrate", "cpuset.memory_migrate", CTRL_CPUSET },
181 	{ }
182 };
183 
184 /* Lookup tree for item names. */
185 static struct cgroup_ctrl controllers[] = {
186 	[0] = { "cgroup", cgroup_ctrl_files, 0, NULL, 0 },
187 	[CTRL_MEMORY] = {
188 		"memory", memory_ctrl_files, CTRL_MEMORY, NULL, 0
189 	},
190 	[CTRL_CPU] = {
191 		"cpu", cpu_ctrl_files, CTRL_CPU, NULL, 0
192 	},
193 	[CTRL_CPUSET] = {
194 		"cpuset", cpuset_ctrl_files, CTRL_CPUSET, NULL, 0
195 	},
196 	{ }
197 };
198 
199 static const struct tst_cgroup_opts default_opts = { 0 };
200 
201 /* We should probably allow these to be set in environment
202  * variables
203  */
204 static const char *cgroup_ltp_dir = "ltp";
205 static const char *cgroup_ltp_drain_dir = "drain";
206 static char cgroup_test_dir[NAME_MAX + 1];
207 static const char *cgroup_mount_ltp_prefix = "/tmp/cgroup_";
208 static const char *cgroup_v2_ltp_mount = "unified";
209 
210 #define first_root				\
211 	(roots[0].ver ? roots : roots + 1)
212 #define for_each_root(r)			\
213 	for ((r) = first_root; (r)->ver; (r)++)
214 #define for_each_v1_root(r)			\
215 	for ((r) = roots + 1; (r)->ver; (r)++)
216 #define for_each_ctrl(ctrl)			\
217 	for ((ctrl) = controllers + 1; (ctrl)->ctrl_name; (ctrl)++)
218 
219 /* In all cases except one, this only loops once.
220  *
221  * If (ctrl) == 0 and multiple V1 (and a V2) hierarchies are mounted,
222  * then we need to loop over multiple directories. For example if we
223  * need to write to "tasks"/"cgroup.procs" which exists for each
224  * hierarchy.
225  */
226 #define for_each_dir(cg, ctrl, t)					\
227 	for ((t) = (ctrl) ? (cg)->dirs_by_ctrl + (ctrl) : (cg)->dirs;	\
228 	     *(t);							\
229 	     (t) = (ctrl) ? (cg)->dirs + ROOTS_MAX : (t) + 1)
230 
231 __attribute__ ((nonnull))
has_ctrl(const uint32_t ctrl_field,const struct cgroup_ctrl * const ctrl)232 static int has_ctrl(const uint32_t ctrl_field,
233 		    const struct cgroup_ctrl *const ctrl)
234 {
235 	return !!(ctrl_field & (1 << ctrl->ctrl_indx));
236 }
237 
238 __attribute__ ((nonnull))
add_ctrl(uint32_t * const ctrl_field,const struct cgroup_ctrl * const ctrl)239 static void add_ctrl(uint32_t *const ctrl_field,
240 		     const struct cgroup_ctrl *const ctrl)
241 {
242 	*ctrl_field |= 1 << ctrl->ctrl_indx;
243 }
244 
cgroup_v2_mounted(void)245 static int cgroup_v2_mounted(void)
246 {
247 	return !!roots[0].ver;
248 }
249 
cgroup_v1_mounted(void)250 static int cgroup_v1_mounted(void)
251 {
252 	return !!roots[1].ver;
253 }
254 
cgroup_mounted(void)255 static int cgroup_mounted(void)
256 {
257 	return cgroup_v2_mounted() || cgroup_v1_mounted();
258 }
259 
260 __attribute__ ((nonnull, warn_unused_result))
cgroup_ctrl_on_v2(const struct cgroup_ctrl * const ctrl)261 static int cgroup_ctrl_on_v2(const struct cgroup_ctrl *const ctrl)
262 {
263 	return ctrl->ctrl_root && ctrl->ctrl_root->ver == TST_CGROUP_V2;
264 }
265 
266 __attribute__ ((nonnull))
cgroup_dir_mk(const struct cgroup_dir * const parent,const char * const dir_name,struct cgroup_dir * const new)267 static void cgroup_dir_mk(const struct cgroup_dir *const parent,
268 			  const char *const dir_name,
269 			  struct cgroup_dir *const new)
270 {
271 	const char *dpath;
272 
273 	new->dir_root = parent->dir_root;
274 	new->dir_name = dir_name;
275 	new->dir_parent = parent;
276 	new->ctrl_field = parent->ctrl_field;
277 	new->we_created_it = 0;
278 
279 	if (!mkdirat(parent->dir_fd, dir_name, 0777)) {
280 		new->we_created_it = 1;
281 		goto opendir;
282 	}
283 
284 	if (errno == EEXIST)
285 		goto opendir;
286 
287 	dpath = tst_decode_fd(parent->dir_fd);
288 
289 	if (errno == EACCES) {
290 		tst_brk(TCONF | TERRNO,
291 			"Lack permission to make '%s/%s'; premake it or run as root",
292 			dpath, dir_name);
293 	} else {
294 		tst_brk(TBROK | TERRNO,
295 			"mkdirat(%d<%s>, '%s', 0777)",
296 			parent->dir_fd, dpath, dir_name);
297 	}
298 
299 opendir:
300 	new->dir_fd = SAFE_OPENAT(parent->dir_fd, dir_name,
301 				  O_PATH | O_DIRECTORY);
302 }
303 
tst_cgroup_print_config(void)304 void tst_cgroup_print_config(void)
305 {
306 	struct cgroup_root *root;
307 	const struct cgroup_ctrl *ctrl;
308 
309 	tst_res(TINFO, "Detected Controllers:");
310 
311 	for_each_ctrl(ctrl) {
312 		root = ctrl->ctrl_root;
313 
314 		if (!root)
315 			continue;
316 
317 		tst_res(TINFO, "\t%.10s %s @ %s:%s",
318 			ctrl->ctrl_name,
319 			root->no_cpuset_prefix ? "[noprefix]" : "",
320 			root->ver == TST_CGROUP_V1 ? "V1" : "V2",
321 			root->mnt_path);
322 	}
323 }
324 
325 __attribute__ ((nonnull, warn_unused_result))
cgroup_find_ctrl(const char * const ctrl_name)326 static struct cgroup_ctrl *cgroup_find_ctrl(const char *const ctrl_name)
327 {
328 	struct cgroup_ctrl *ctrl = controllers;
329 
330 	while (ctrl->ctrl_name && strcmp(ctrl_name, ctrl->ctrl_name))
331 		ctrl++;
332 
333 	if (!ctrl->ctrl_name)
334 		ctrl = NULL;
335 
336 	return ctrl;
337 }
338 
339 /* Determine if a mounted cgroup hierarchy is unique and record it if so.
340  *
341  * For CGroups V2 this is very simple as there is only one
342  * hierarchy. We just record which controllers are available and check
343  * if this matches what we read from any previous mount points.
344  *
345  * For V1 the set of controllers S is partitioned into sets {P_1, P_2,
346  * ..., P_n} with one or more controllers in each partion. Each
347  * partition P_n can be mounted multiple times, but the same
348  * controller can not appear in more than one partition. Usually each
349  * partition contains a single controller, but, for example, cpu and
350  * cpuacct are often mounted together in the same partiion.
351  *
352  * Each controller partition has its own hierarchy (root) which we
353  * must track and update independently.
354  */
355 __attribute__ ((nonnull))
cgroup_root_scan(const char * const mnt_type,const char * const mnt_dir,char * const mnt_opts)356 static void cgroup_root_scan(const char *const mnt_type,
357 			     const char *const mnt_dir,
358 			     char *const mnt_opts)
359 {
360 	struct cgroup_root *root = roots;
361 	const struct cgroup_ctrl *const_ctrl;
362 	struct cgroup_ctrl *ctrl;
363 	uint32_t ctrl_field = 0;
364 	int no_prefix = 0;
365 	char buf[BUFSIZ];
366 	char *tok;
367 	const int mnt_dfd = SAFE_OPEN(mnt_dir, O_PATH | O_DIRECTORY);
368 
369 	if (!strcmp(mnt_type, "cgroup"))
370 		goto v1;
371 
372 	SAFE_FILE_READAT(mnt_dfd, "cgroup.controllers", buf, sizeof(buf));
373 
374 	for (tok = strtok(buf, " "); tok; tok = strtok(NULL, " ")) {
375 		const_ctrl = cgroup_find_ctrl(tok);
376 		if (const_ctrl)
377 			add_ctrl(&ctrl_field, const_ctrl);
378 	}
379 
380 	if (root->ver && ctrl_field == root->ctrl_field)
381 		goto discard;
382 
383 	if (root->ctrl_field)
384 		tst_brk(TBROK, "Available V2 controllers are changing between scans?");
385 
386 	root->ver = TST_CGROUP_V2;
387 
388 	goto backref;
389 
390 v1:
391 	for (tok = strtok(mnt_opts, ","); tok; tok = strtok(NULL, ",")) {
392 		const_ctrl = cgroup_find_ctrl(tok);
393 		if (const_ctrl)
394 			add_ctrl(&ctrl_field, const_ctrl);
395 
396 		no_prefix |= !strcmp("noprefix", tok);
397 	}
398 
399 	if (!ctrl_field)
400 		goto discard;
401 
402 	for_each_v1_root(root) {
403 		if (!(ctrl_field & root->ctrl_field))
404 			continue;
405 
406 		if (ctrl_field == root->ctrl_field)
407 			goto discard;
408 
409 		tst_brk(TBROK,
410 			"The intersection of two distinct sets of mounted controllers should be null? "
411 			"Check '%s' and '%s'", root->mnt_path, mnt_dir);
412 	}
413 
414 	if (root >= roots + ROOTS_MAX) {
415 		tst_brk(TBROK,
416 			"Unique controller mounts have exceeded our limit %d?",
417 			ROOTS_MAX);
418 	}
419 
420 	root->ver = TST_CGROUP_V1;
421 
422 backref:
423 	strcpy(root->mnt_path, mnt_dir);
424 	root->mnt_dir.dir_root = root;
425 	root->mnt_dir.dir_name = root->mnt_path;
426 	root->mnt_dir.dir_fd = mnt_dfd;
427 	root->ctrl_field = ctrl_field;
428 	root->no_cpuset_prefix = no_prefix;
429 
430 	for_each_ctrl(ctrl) {
431 		if (has_ctrl(root->ctrl_field, ctrl))
432 			ctrl->ctrl_root = root;
433 	}
434 
435 	return;
436 
437 discard:
438 	close(mnt_dfd);
439 }
440 
tst_cgroup_scan(void)441 void tst_cgroup_scan(void)
442 {
443 	struct mntent *mnt;
444 	FILE *f = setmntent("/proc/self/mounts", "r");
445 
446 	if (!f) {
447 		tst_brk(TBROK | TERRNO, "Can't open /proc/self/mounts");
448 		return;
449 	}
450 
451 	mnt = getmntent(f);
452 	if (!mnt) {
453 		tst_brk(TBROK | TERRNO, "Can't read mounts or no mounts?");
454 		return;
455 	}
456 
457 	do {
458 		if (strncmp(mnt->mnt_type, "cgroup", 6))
459 			continue;
460 
461 		cgroup_root_scan(mnt->mnt_type, mnt->mnt_dir, mnt->mnt_opts);
462 	} while ((mnt = getmntent(f)));
463 }
464 
cgroup_mount_v2(void)465 static void cgroup_mount_v2(void)
466 {
467 	char mnt_path[PATH_MAX];
468 
469 	sprintf(mnt_path, "%s%s", cgroup_mount_ltp_prefix, cgroup_v2_ltp_mount);
470 
471 	if (!mkdir(mnt_path, 0777)) {
472 		roots[0].mnt_dir.we_created_it = 1;
473 		goto mount;
474 	}
475 
476 	if (errno == EEXIST)
477 		goto mount;
478 
479 	if (errno == EACCES) {
480 		tst_res(TINFO | TERRNO,
481 			"Lack permission to make %s, premake it or run as root",
482 			mnt_path);
483 		return;
484 	}
485 
486 	tst_brk(TBROK | TERRNO, "mkdir(%s, 0777)", mnt_path);
487 	return;
488 
489 mount:
490 	if (!mount("cgroup2", mnt_path, "cgroup2", 0, NULL)) {
491 		tst_res(TINFO, "Mounted V2 CGroups on %s", mnt_path);
492 		tst_cgroup_scan();
493 		roots[0].we_mounted_it = 1;
494 		return;
495 	}
496 
497 	tst_res(TINFO | TERRNO, "Could not mount V2 CGroups on %s", mnt_path);
498 
499 	if (roots[0].mnt_dir.we_created_it) {
500 		roots[0].mnt_dir.we_created_it = 0;
501 		SAFE_RMDIR(mnt_path);
502 	}
503 }
504 
505 __attribute__ ((nonnull))
cgroup_mount_v1(struct cgroup_ctrl * const ctrl)506 static void cgroup_mount_v1(struct cgroup_ctrl *const ctrl)
507 {
508 	char mnt_path[PATH_MAX];
509 	int made_dir = 0;
510 
511 	sprintf(mnt_path, "%s%s", cgroup_mount_ltp_prefix, ctrl->ctrl_name);
512 
513 	if (!mkdir(mnt_path, 0777)) {
514 		made_dir = 1;
515 		goto mount;
516 	}
517 
518 	if (errno == EEXIST)
519 		goto mount;
520 
521 	if (errno == EACCES) {
522 		tst_res(TINFO | TERRNO,
523 			"Lack permission to make %s, premake it or run as root",
524 			mnt_path);
525 		return;
526 	}
527 
528 	tst_brk(TBROK | TERRNO, "mkdir(%s, 0777)", mnt_path);
529 	return;
530 
531 mount:
532 	if (mount(ctrl->ctrl_name, mnt_path, "cgroup", 0, ctrl->ctrl_name)) {
533 		tst_res(TINFO | TERRNO,
534 			"Could not mount V1 CGroup on %s", mnt_path);
535 
536 		if (made_dir)
537 			SAFE_RMDIR(mnt_path);
538 		return;
539 	}
540 
541 	tst_res(TINFO, "Mounted V1 %s CGroup on %s", ctrl->ctrl_name, mnt_path);
542 	tst_cgroup_scan();
543 	if (!ctrl->ctrl_root)
544 		return;
545 
546 	ctrl->ctrl_root->we_mounted_it = 1;
547 	ctrl->ctrl_root->mnt_dir.we_created_it = made_dir;
548 
549 	if (ctrl->ctrl_indx == CTRL_MEMORY) {
550 		SAFE_FILE_PRINTFAT(ctrl->ctrl_root->mnt_dir.dir_fd,
551 				   "memory.use_hierarchy", "%d", 1);
552 	}
553 }
554 
555 __attribute__ ((nonnull))
cgroup_copy_cpuset(const struct cgroup_root * const root)556 static void cgroup_copy_cpuset(const struct cgroup_root *const root)
557 {
558 	char knob_val[BUFSIZ];
559 	int i;
560 	const char *const n0[] = {"mems", "cpus"};
561 	const char *const n1[] = {"cpuset.mems", "cpuset.cpus"};
562 	const char *const *const fname = root->no_cpuset_prefix ? n0 : n1;
563 
564 	for (i = 0; i < 2; i++) {
565 		SAFE_FILE_READAT(root->mnt_dir.dir_fd,
566 				 fname[i], knob_val, sizeof(knob_val));
567 		SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
568 				   fname[i], "%s", knob_val);
569 	}
570 }
571 
572 /* Ensure the specified controller is available.
573  *
574  * First we check if the specified controller has a known mount point,
575  * if not then we scan the system. If we find it then we goto ensuring
576  * the LTP group exists in the hierarchy the controller is using.
577  *
578  * If we can't find the controller, then we try to create it. First we
579  * check if the V2 hierarchy/tree is mounted. If it isn't then we try
580  * mounting it and look for the controller. If it is already mounted
581  * then we know the controller is not available on V2 on this system.
582  *
583  * If we can't mount V2 or the controller is not on V2, then we try
584  * mounting it on its own V1 tree.
585  *
586  * Once we have mounted the controller somehow, we create a hierarchy
587  * of cgroups. If we are on V2 we first need to enable the controller
588  * for all children of root. Then we create hierarchy described in
589  * tst_cgroup.h.
590  *
591  * If we are using V1 cpuset then we copy the available mems and cpus
592  * from root to the ltp group and set clone_children on the ltp group
593  * to distribute these settings to the test cgroups. This means the
594  * test author does not have to copy these settings before using the
595  * cpuset.
596  *
597  */
tst_cgroup_require(const char * const ctrl_name,const struct tst_cgroup_opts * options)598 void tst_cgroup_require(const char *const ctrl_name,
599 			const struct tst_cgroup_opts *options)
600 {
601 	const char *const cgsc = "cgroup.subtree_control";
602 	struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name);
603 	struct cgroup_root *root;
604 
605 	if (!ctrl) {
606 		tst_brk(TBROK, "'%s' controller is unknown to LTP", ctrl_name);
607 		tst_brk(TBROK, "Calling %s in cleanup?", __func__);
608 		return;
609 	}
610 
611 	if (!options)
612 		options = &default_opts;
613 
614 	if (ctrl->we_require_it)
615 		tst_res(TWARN, "Duplicate %s(%s, )", __func__, ctrl->ctrl_name);
616 
617 	ctrl->we_require_it = 1;
618 
619 	if (ctrl->ctrl_root)
620 		goto mkdirs;
621 
622 	tst_cgroup_scan();
623 
624 	if (ctrl->ctrl_root)
625 		goto mkdirs;
626 
627 	if (!cgroup_v2_mounted() && !options->only_mount_v1)
628 		cgroup_mount_v2();
629 
630 	if (ctrl->ctrl_root)
631 		goto mkdirs;
632 
633 	cgroup_mount_v1(ctrl);
634 
635 	if (!ctrl->ctrl_root) {
636 		tst_brk(TCONF,
637 			"'%s' controller required, but not available",
638 			ctrl->ctrl_name);
639 		return;
640 	}
641 
642 mkdirs:
643 	root = ctrl->ctrl_root;
644 	add_ctrl(&root->mnt_dir.ctrl_field, ctrl);
645 
646 	if (cgroup_ctrl_on_v2(ctrl)) {
647 		if (root->we_mounted_it) {
648 			SAFE_FILE_PRINTFAT(root->mnt_dir.dir_fd,
649 					   cgsc, "+%s", ctrl->ctrl_name);
650 		} else {
651 			tst_file_printfat(root->mnt_dir.dir_fd,
652 					  cgsc, "+%s", ctrl->ctrl_name);
653 		}
654 	}
655 
656 	if (!root->ltp_dir.dir_fd)
657 		cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
658 	else
659 		root->ltp_dir.ctrl_field |= root->mnt_dir.ctrl_field;
660 
661 	if (cgroup_ctrl_on_v2(ctrl)) {
662 		SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
663 				   cgsc, "+%s", ctrl->ctrl_name);
664 	} else {
665 		SAFE_FILE_PRINTFAT(root->ltp_dir.dir_fd,
666 				   "cgroup.clone_children", "%d", 1);
667 
668 		if (ctrl->ctrl_indx == CTRL_CPUSET)
669 			cgroup_copy_cpuset(root);
670 	}
671 
672 	cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
673 
674 	sprintf(cgroup_test_dir, "test-%d", getpid());
675 	cgroup_dir_mk(&root->ltp_dir, cgroup_test_dir, &root->test_dir);
676 }
677 
cgroup_drain(const enum tst_cgroup_ver ver,const int source_dfd,const int dest_dfd)678 static void cgroup_drain(const enum tst_cgroup_ver ver,
679 			 const int source_dfd, const int dest_dfd)
680 {
681 	char pid_list[BUFSIZ];
682 	char *tok;
683 	const char *const file_name =
684 		ver == TST_CGROUP_V1 ? "tasks" : "cgroup.procs";
685 	int fd;
686 	ssize_t ret;
687 
688 	ret = SAFE_FILE_READAT(source_dfd, file_name,
689 			       pid_list, sizeof(pid_list));
690 	if (ret < 0)
691 		return;
692 
693 	fd = SAFE_OPENAT(dest_dfd, file_name, O_WRONLY);
694 	if (fd < 0)
695 		return;
696 
697 	for (tok = strtok(pid_list, "\n"); tok; tok = strtok(NULL, "\n")) {
698 		ret = dprintf(fd, "%s", tok);
699 
700 		if (ret < (ssize_t)strlen(tok))
701 			tst_brk(TBROK | TERRNO, "Failed to drain %s", tok);
702 	}
703 	SAFE_CLOSE(fd);
704 }
705 
706 __attribute__ ((nonnull))
close_path_fds(struct cgroup_root * const root)707 static void close_path_fds(struct cgroup_root *const root)
708 {
709 	if (root->test_dir.dir_fd > 0)
710 		SAFE_CLOSE(root->test_dir.dir_fd);
711 	if (root->ltp_dir.dir_fd > 0)
712 		SAFE_CLOSE(root->ltp_dir.dir_fd);
713 	if (root->drain_dir.dir_fd > 0)
714 		SAFE_CLOSE(root->drain_dir.dir_fd);
715 	if (root->mnt_dir.dir_fd > 0)
716 		SAFE_CLOSE(root->mnt_dir.dir_fd);
717 }
718 
719 /* Maybe remove CGroups used during testing and clear our data
720  *
721  * This will never remove CGroups we did not create to allow tests to
722  * be run in parallel.
723  *
724  * Each test process is given its own unique CGroup. Unless we want to
725  * stress test the CGroup system. We should at least remove these
726  * unique per test CGroups.
727  *
728  * We probably also want to remove the LTP parent CGroup, although
729  * this may have been created by the system manager or another test
730  * (see notes on parallel testing).
731  *
732  * On systems with no initial CGroup setup we may try to destroy the
733  * CGroup roots we mounted so that they can be recreated by another
734  * test. Note that successfully unmounting a CGroup root does not
735  * necessarily indicate that it was destroyed.
736  *
737  * The ltp/drain CGroup is required for cleaning up test CGroups when
738  * we can not move them to the root CGroup. CGroups can only be
739  * removed when they have no members and only leaf or root CGroups may
740  * have processes within them. As test processes create and destroy
741  * their own CGroups they must move themselves either to root or
742  * another leaf CGroup. So we move them to drain while destroying the
743  * unique test CGroup.
744  *
745  * If we have access to root and created the LTP CGroup we then move
746  * the test process to root and destroy the drain and LTP
747  * CGroups. Otherwise we just leave the test process to die in the
748  * drain, much like many a unwanted terrapin.
749  *
750  * Finally we clear any data we have collected on CGroups. This will
751  * happen regardless of whether anything was removed.
752  */
tst_cgroup_cleanup(void)753 void tst_cgroup_cleanup(void)
754 {
755 	struct cgroup_root *root;
756 	struct cgroup_ctrl *ctrl;
757 
758 	if (!cgroup_mounted())
759 		goto clear_data;
760 
761 	for_each_root(root) {
762 		if (!root->test_dir.dir_name)
763 			continue;
764 
765 		cgroup_drain(root->ver,
766 			     root->test_dir.dir_fd, root->drain_dir.dir_fd);
767 		SAFE_UNLINKAT(root->ltp_dir.dir_fd, root->test_dir.dir_name,
768 			      AT_REMOVEDIR);
769 	}
770 
771 	for_each_root(root) {
772 		if (!root->ltp_dir.we_created_it)
773 			continue;
774 
775 		cgroup_drain(root->ver,
776 			     root->drain_dir.dir_fd, root->mnt_dir.dir_fd);
777 
778 		if (root->drain_dir.dir_name) {
779 			SAFE_UNLINKAT(root->ltp_dir.dir_fd,
780 				      root->drain_dir.dir_name, AT_REMOVEDIR);
781 		}
782 
783 		if (root->ltp_dir.dir_name) {
784 			SAFE_UNLINKAT(root->mnt_dir.dir_fd,
785 				      root->ltp_dir.dir_name, AT_REMOVEDIR);
786 		}
787 	}
788 
789 	for_each_ctrl(ctrl) {
790 		if (!cgroup_ctrl_on_v2(ctrl) || !ctrl->ctrl_root->we_mounted_it)
791 			continue;
792 
793 		SAFE_FILE_PRINTFAT(ctrl->ctrl_root->mnt_dir.dir_fd,
794 				   "cgroup.subtree_control",
795 				   "-%s", ctrl->ctrl_name);
796 	}
797 
798 	for_each_root(root) {
799 		if (!root->we_mounted_it)
800 			continue;
801 
802 		/* This probably does not result in the CGroup root
803 		 * being destroyed
804 		 */
805 		if (umount2(root->mnt_path, MNT_DETACH))
806 			continue;
807 
808 		SAFE_RMDIR(root->mnt_path);
809 	}
810 
811 clear_data:
812 	for_each_ctrl(ctrl) {
813 		ctrl->ctrl_root = NULL;
814 		ctrl->we_require_it = 0;
815 	}
816 
817 	for_each_root(root)
818 		close_path_fds(root);
819 
820 	memset(roots, 0, sizeof(roots));
821 }
822 
823 __attribute__((nonnull(1)))
cgroup_group_init(struct tst_cgroup_group * const cg,const char * const group_name)824 static void cgroup_group_init(struct tst_cgroup_group *const cg,
825 			      const char *const group_name)
826 {
827 	memset(cg, 0, sizeof(*cg));
828 
829 	if (!group_name)
830 		return;
831 
832 	if (strlen(group_name) > NAME_MAX)
833 		tst_brk(TBROK, "Group name is too long");
834 
835 	strcpy(cg->group_name, group_name);
836 }
837 
838 __attribute__((nonnull(2, 3)))
cgroup_group_add_dir(const struct tst_cgroup_group * const parent,struct tst_cgroup_group * const cg,struct cgroup_dir * const dir)839 static void cgroup_group_add_dir(const struct tst_cgroup_group *const parent,
840 				 struct tst_cgroup_group *const cg,
841 				 struct cgroup_dir *const dir)
842 {
843 	const struct cgroup_ctrl *ctrl;
844 	int i;
845 
846 	if (dir->dir_root->ver != TST_CGROUP_V1)
847 		cg->dirs_by_ctrl[0] = dir;
848 
849 	for_each_ctrl(ctrl) {
850 		if (!has_ctrl(dir->ctrl_field, ctrl))
851 			continue;
852 
853 		cg->dirs_by_ctrl[ctrl->ctrl_indx] = dir;
854 
855 		if (!parent || dir->dir_root->ver == TST_CGROUP_V1)
856 			continue;
857 
858 		SAFE_CGROUP_PRINTF(parent, "cgroup.subtree_control",
859 				   "+%s", ctrl->ctrl_name);
860 	}
861 
862 	for (i = 0; cg->dirs[i]; i++)
863 		;
864 	cg->dirs[i] = dir;
865 }
866 
867 struct tst_cgroup_group *
tst_cgroup_group_mk(const struct tst_cgroup_group * const parent,const char * const group_name)868 tst_cgroup_group_mk(const struct tst_cgroup_group *const parent,
869 		    const char *const group_name)
870 {
871 	struct tst_cgroup_group *cg;
872 	struct cgroup_dir *const *dir;
873 	struct cgroup_dir *new_dir;
874 
875 	cg = SAFE_MALLOC(sizeof(*cg));
876 	cgroup_group_init(cg, group_name);
877 
878 	for_each_dir(parent, 0, dir) {
879 		new_dir = SAFE_MALLOC(sizeof(*new_dir));
880 		cgroup_dir_mk(*dir, group_name, new_dir);
881 		cgroup_group_add_dir(parent, cg, new_dir);
882 	}
883 
884 	return cg;
885 }
886 
tst_cgroup_group_name(const struct tst_cgroup_group * const cg)887 const char *tst_cgroup_group_name(const struct tst_cgroup_group *const cg)
888 {
889 	return cg->group_name;
890 }
891 
tst_cgroup_group_rm(struct tst_cgroup_group * const cg)892 struct tst_cgroup_group *tst_cgroup_group_rm(struct tst_cgroup_group *const cg)
893 {
894 	struct cgroup_dir **dir;
895 
896 	for_each_dir(cg, 0, dir) {
897 		close((*dir)->dir_fd);
898 		SAFE_UNLINKAT((*dir)->dir_parent->dir_fd,
899 			      (*dir)->dir_name,
900 			      AT_REMOVEDIR);
901 		free(*dir);
902 	}
903 
904 	free(cg);
905 	return NULL;
906 }
907 
908 __attribute__ ((nonnull, warn_unused_result))
cgroup_file_find(const char * const file,const int lineno,const char * const file_name)909 static const struct cgroup_file *cgroup_file_find(const char *const file,
910 						  const int lineno,
911 						  const char *const file_name)
912 {
913 	const struct cgroup_file *cfile;
914 	const struct cgroup_ctrl *ctrl;
915 	char ctrl_name[32];
916 	const char *const sep = strchr(file_name, '.');
917 	size_t len;
918 
919 	if (!sep) {
920 		tst_brk_(file, lineno, TBROK,
921 			 "Invalid file name '%s'; did not find controller separator '.'",
922 			 file_name);
923 		return NULL;
924 	}
925 
926 	len = sep - file_name;
927 	memcpy(ctrl_name, file_name, len);
928 	ctrl_name[len] = '\0';
929 
930 	ctrl = cgroup_find_ctrl(ctrl_name);
931 
932 	if (!ctrl) {
933 		tst_brk_(file, lineno, TBROK,
934 			 "Did not find controller '%s'\n", ctrl_name);
935 		return NULL;
936 	}
937 
938 	for (cfile = ctrl->files; cfile->file_name; cfile++) {
939 		if (!strcmp(file_name, cfile->file_name))
940 			break;
941 	}
942 
943 	if (!cfile->file_name) {
944 		tst_brk_(file, lineno, TBROK,
945 			 "Did not find '%s' in '%s'\n",
946 			 file_name, ctrl->ctrl_name);
947 		return NULL;
948 	}
949 
950 	return cfile;
951 }
952 
tst_cgroup_ver(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const ctrl_name)953 enum tst_cgroup_ver tst_cgroup_ver(const char *const file, const int lineno,
954 				    const struct tst_cgroup_group *const cg,
955 				    const char *const ctrl_name)
956 {
957 	const struct cgroup_ctrl *const ctrl = cgroup_find_ctrl(ctrl_name);
958 	const struct cgroup_dir *dir;
959 
960 	if (!strcmp(ctrl_name, "cgroup")) {
961 		tst_brk_(file, lineno,
962 			 TBROK,
963 			 "cgroup may be present on both V1 and V2 hierarchies");
964 		return 0;
965 	}
966 
967 	if (!ctrl) {
968 		tst_brk_(file, lineno,
969 			 TBROK, "Unknown controller '%s'", ctrl_name);
970 		return 0;
971 	}
972 
973 	dir = cg->dirs_by_ctrl[ctrl->ctrl_indx];
974 
975 	if (!dir) {
976 		tst_brk_(file, lineno,
977 			 TBROK, "%s controller not attached to CGroup %s",
978 			 ctrl_name, cg->group_name);
979 		return 0;
980 	}
981 
982 	return dir->dir_root->ver;
983 }
984 
985 __attribute__ ((nonnull, warn_unused_result))
cgroup_file_alias(const struct cgroup_file * const cfile,const struct cgroup_dir * const dir)986 static const char *cgroup_file_alias(const struct cgroup_file *const cfile,
987 				     const struct cgroup_dir *const dir)
988 {
989 	if (dir->dir_root->ver != TST_CGROUP_V1)
990 		return cfile->file_name;
991 
992 	if (cfile->ctrl_indx == CTRL_CPUSET &&
993 	    dir->dir_root->no_cpuset_prefix &&
994 	    cfile->file_name_v1) {
995 		return strchr(cfile->file_name_v1, '.') + 1;
996 	}
997 
998 	return cfile->file_name_v1;
999 }
1000 
safe_cgroup_has(const char * const file,const int lineno,const struct tst_cgroup_group * cg,const char * const file_name)1001 int safe_cgroup_has(const char *const file, const int lineno,
1002 		    const struct tst_cgroup_group *cg,
1003 		    const char *const file_name)
1004 {
1005 	const struct cgroup_file *const cfile =
1006 		cgroup_file_find(file, lineno, file_name);
1007 	struct cgroup_dir *const *dir;
1008 	const char *alias;
1009 
1010 	if (!cfile)
1011 		return 0;
1012 
1013 	for_each_dir(cg, cfile->ctrl_indx, dir) {
1014 		alias = cgroup_file_alias(cfile, *dir);
1015 		if (!alias)
1016 			continue;
1017 
1018 		if (!faccessat((*dir)->dir_fd, alias, F_OK, 0))
1019 			return 1;
1020 
1021 		if (errno == ENOENT)
1022 			continue;
1023 
1024 		tst_brk_(file, lineno, TBROK | TERRNO,
1025 			 "faccessat(%d<%s>, %s, F_OK, 0)",
1026 			 (*dir)->dir_fd, tst_decode_fd((*dir)->dir_fd), alias);
1027 	}
1028 
1029 	return 0;
1030 }
1031 
1032 __attribute__ ((warn_unused_result))
cgroup_group_from_roots(const size_t tree_off)1033 static struct tst_cgroup_group *cgroup_group_from_roots(const size_t tree_off)
1034 {
1035 	struct cgroup_root *root;
1036 	struct cgroup_dir *dir;
1037 	struct tst_cgroup_group *cg;
1038 
1039 	cg = tst_alloc(sizeof(*cg));
1040 	cgroup_group_init(cg, NULL);
1041 
1042 	for_each_root(root) {
1043 		dir = (typeof(dir))(((char *)root) + tree_off);
1044 
1045 		if (dir->ctrl_field)
1046 			cgroup_group_add_dir(NULL, cg, dir);
1047 	}
1048 
1049 	if (cg->dirs[0]) {
1050 		strncpy(cg->group_name, cg->dirs[0]->dir_name, NAME_MAX);
1051 		return cg;
1052 	}
1053 
1054 	tst_brk(TBROK,
1055 		"No CGroups found; maybe you forgot to call tst_cgroup_require?");
1056 
1057 	return cg;
1058 }
1059 
tst_cgroup_get_test_group(void)1060 const struct tst_cgroup_group *tst_cgroup_get_test_group(void)
1061 {
1062 	return cgroup_group_from_roots(offsetof(struct cgroup_root, test_dir));
1063 }
1064 
tst_cgroup_get_drain_group(void)1065 const struct tst_cgroup_group *tst_cgroup_get_drain_group(void)
1066 {
1067 	return cgroup_group_from_roots(offsetof(struct cgroup_root, drain_dir));
1068 }
1069 
safe_cgroup_read(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,char * const out,const size_t len)1070 ssize_t safe_cgroup_read(const char *const file, const int lineno,
1071 			 const struct tst_cgroup_group *const cg,
1072 			 const char *const file_name,
1073 			 char *const out, const size_t len)
1074 {
1075 	const struct cgroup_file *const cfile =
1076 		cgroup_file_find(file, lineno, file_name);
1077 	struct cgroup_dir *const *dir;
1078 	const char *alias;
1079 	size_t prev_len = 0;
1080 	char prev_buf[BUFSIZ];
1081 	ssize_t read_ret = 0;
1082 
1083 	for_each_dir(cg, cfile->ctrl_indx, dir) {
1084 		alias = cgroup_file_alias(cfile, *dir);
1085 		if (!alias)
1086 			continue;
1087 
1088 		if (prev_len)
1089 			memcpy(prev_buf, out, prev_len);
1090 
1091 		read_ret = safe_file_readat(file, lineno,
1092 					    (*dir)->dir_fd, alias, out, len);
1093 		if (read_ret < 0)
1094 			continue;
1095 
1096 		if (prev_len && memcmp(out, prev_buf, prev_len)) {
1097 			tst_brk_(file, lineno, TBROK,
1098 				 "%s has different value across roots",
1099 				 file_name);
1100 			break;
1101 		}
1102 
1103 		prev_len = MIN(sizeof(prev_buf), (size_t)read_ret);
1104 	}
1105 
1106 	out[MAX(read_ret, 0)] = '\0';
1107 
1108 	return read_ret;
1109 }
1110 
safe_cgroup_printf(const char * const file,const int lineno,const struct tst_cgroup_group * cg,const char * const file_name,const char * const fmt,...)1111 void safe_cgroup_printf(const char *const file, const int lineno,
1112 			const struct tst_cgroup_group *cg,
1113 			const char *const file_name,
1114 			const char *const fmt, ...)
1115 {
1116 	const struct cgroup_file *const cfile =
1117 		cgroup_file_find(file, lineno, file_name);
1118 	struct cgroup_dir *const *dir;
1119 	const char *alias;
1120 	va_list va;
1121 
1122 	for_each_dir(cg, cfile->ctrl_indx, dir) {
1123 		alias = cgroup_file_alias(cfile, *dir);
1124 		if (!alias)
1125 			continue;
1126 
1127 		va_start(va, fmt);
1128 		safe_file_vprintfat(file, lineno,
1129 				    (*dir)->dir_fd, alias, fmt, va);
1130 		va_end(va);
1131 	}
1132 }
1133 
safe_cgroup_scanf(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,const char * const fmt,...)1134 void safe_cgroup_scanf(const char *const file, const int lineno,
1135 		       const struct tst_cgroup_group *const cg,
1136 		       const char *const file_name,
1137 		       const char *const fmt, ...)
1138 {
1139 	va_list va;
1140 	char buf[BUFSIZ];
1141 	ssize_t len = safe_cgroup_read(file, lineno,
1142 				       cg, file_name, buf, sizeof(buf));
1143 	const int conv_cnt = tst_count_scanf_conversions(fmt);
1144 	int ret;
1145 
1146 	if (len < 1)
1147 		return;
1148 
1149 	va_start(va, fmt);
1150 	ret = vsscanf(buf, fmt, va);
1151 	if (ret < 1) {
1152 		tst_brk_(file, lineno, TBROK | TERRNO,
1153 			 "'%s': vsscanf('%s', '%s', ...)", file_name, buf, fmt);
1154 	}
1155 	va_end(va);
1156 
1157 	if (conv_cnt == ret)
1158 		return;
1159 
1160 	tst_brk_(file, lineno, TBROK,
1161 		 "'%s': vsscanf('%s', '%s', ..): Less conversions than expected: %d != %d",
1162 		 file_name, buf, fmt, ret, conv_cnt);
1163 }
1164 
safe_cgroup_lines_scanf(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,const char * const fmt,...)1165 void safe_cgroup_lines_scanf(const char *const file, const int lineno,
1166 			     const struct tst_cgroup_group *const cg,
1167 			     const char *const file_name,
1168 			     const char *const fmt, ...)
1169 {
1170 	va_list va;
1171 	char buf[BUFSIZ];
1172 	ssize_t len = safe_cgroup_read(file, lineno,
1173 				       cg, file_name, buf, sizeof(buf));
1174 	const int conv_cnt = tst_count_scanf_conversions(fmt);
1175 	int ret = 0;
1176 	char *line, *buf_ptr;
1177 
1178 	if (len < 1)
1179 		return;
1180 
1181 	line = strtok_r(buf, "\n", &buf_ptr);
1182 	while (line && ret != conv_cnt) {
1183 		va_start(va, fmt);
1184 		ret = vsscanf(line, fmt, va);
1185 		va_end(va);
1186 
1187 		line = strtok_r(NULL, "\n", &buf_ptr);
1188 	}
1189 
1190 	if (conv_cnt == ret)
1191 		return;
1192 
1193 	tst_brk_(file, lineno, TBROK,
1194 		 "'%s': vsscanf('%s', '%s', ..): Less conversions than expected: %d != %d",
1195 		 file_name, buf, fmt, ret, conv_cnt);
1196 }
1197 
safe_cgroup_occursin(const char * const file,const int lineno,const struct tst_cgroup_group * const cg,const char * const file_name,const char * const needle)1198 int safe_cgroup_occursin(const char *const file, const int lineno,
1199 			 const struct tst_cgroup_group *const cg,
1200 			 const char *const file_name,
1201 			 const char *const needle)
1202 {
1203 	char buf[BUFSIZ];
1204 
1205 	safe_cgroup_read(file, lineno, cg, file_name, buf, sizeof(buf));
1206 
1207 	return !!strstr(buf, needle);
1208 }
1209