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