xref: /linux/tools/testing/selftests/cgroup/test_freezer.c (revision de6fef50eaf40789b11841474726fd918a3a84a1)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <stdbool.h>
3 #include <linux/limits.h>
4 #include <sys/ptrace.h>
5 #include <sys/types.h>
6 #include <sys/mman.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/wait.h>
13 
14 #include "../kselftest.h"
15 #include "cgroup_util.h"
16 
17 #define DEBUG
18 #ifdef DEBUG
19 #define debug(args...) fprintf(stderr, args)
20 #else
21 #define debug(args...)
22 #endif
23 
24 /*
25  * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
26  */
cg_check_frozen(const char * cgroup,bool frozen)27 static int cg_check_frozen(const char *cgroup, bool frozen)
28 {
29 	if (frozen) {
30 		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
31 			debug("Cgroup %s isn't frozen\n", cgroup);
32 			return -1;
33 		}
34 	} else {
35 		/*
36 		 * Check the cgroup.events::frozen value.
37 		 */
38 		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
39 			debug("Cgroup %s is frozen\n", cgroup);
40 			return -1;
41 		}
42 	}
43 
44 	return 0;
45 }
46 
47 /*
48  * Freeze the given cgroup.
49  */
cg_freeze_nowait(const char * cgroup,bool freeze)50 static int cg_freeze_nowait(const char *cgroup, bool freeze)
51 {
52 	return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
53 }
54 
55 /*
56  * Attach a task to the given cgroup and wait for a cgroup frozen event.
57  * All transient events (e.g. populated) are ignored.
58  */
cg_enter_and_wait_for_frozen(const char * cgroup,int pid,bool frozen)59 static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
60 					bool frozen)
61 {
62 	int fd, ret = -1;
63 	int attempts;
64 
65 	fd = cg_prepare_for_wait(cgroup);
66 	if (fd < 0)
67 		return fd;
68 
69 	ret = cg_enter(cgroup, pid);
70 	if (ret)
71 		goto out;
72 
73 	for (attempts = 0; attempts < 10; attempts++) {
74 		ret = cg_wait_for(fd);
75 		if (ret)
76 			break;
77 
78 		ret = cg_check_frozen(cgroup, frozen);
79 		if (ret)
80 			continue;
81 	}
82 
83 out:
84 	close(fd);
85 	return ret;
86 }
87 
88 /*
89  * Freeze the given cgroup and wait for the inotify signal.
90  * If there are no events in 10 seconds, treat this as an error.
91  * Then check that the cgroup is in the desired state.
92  */
cg_freeze_wait(const char * cgroup,bool freeze)93 static int cg_freeze_wait(const char *cgroup, bool freeze)
94 {
95 	int fd, ret = -1;
96 
97 	fd = cg_prepare_for_wait(cgroup);
98 	if (fd < 0)
99 		return fd;
100 
101 	ret = cg_freeze_nowait(cgroup, freeze);
102 	if (ret) {
103 		debug("Error: cg_freeze_nowait() failed\n");
104 		goto out;
105 	}
106 
107 	ret = cg_wait_for(fd);
108 	if (ret)
109 		goto out;
110 
111 	ret = cg_check_frozen(cgroup, freeze);
112 out:
113 	close(fd);
114 	return ret;
115 }
116 
117 /*
118  * A simple process running in a sleep loop until being
119  * re-parented.
120  */
child_fn(const char * cgroup,void * arg)121 static int child_fn(const char *cgroup, void *arg)
122 {
123 	int ppid = getppid();
124 
125 	while (getppid() == ppid)
126 		usleep(1000);
127 
128 	return getppid() == ppid;
129 }
130 
131 /*
132  * A simple test for the cgroup freezer: populated the cgroup with 100
133  * running processes and freeze it. Then unfreeze it. Then it kills all
134  * processes and destroys the cgroup.
135  */
test_cgfreezer_simple(const char * root)136 static int test_cgfreezer_simple(const char *root)
137 {
138 	int ret = KSFT_FAIL;
139 	char *cgroup = NULL;
140 	int i;
141 
142 	cgroup = cg_name(root, "cg_test_simple");
143 	if (!cgroup)
144 		goto cleanup;
145 
146 	if (cg_create(cgroup))
147 		goto cleanup;
148 
149 	for (i = 0; i < 100; i++)
150 		cg_run_nowait(cgroup, child_fn, NULL);
151 
152 	if (cg_wait_for_proc_count(cgroup, 100))
153 		goto cleanup;
154 
155 	if (cg_check_frozen(cgroup, false))
156 		goto cleanup;
157 
158 	if (cg_freeze_wait(cgroup, true))
159 		goto cleanup;
160 
161 	if (cg_freeze_wait(cgroup, false))
162 		goto cleanup;
163 
164 	ret = KSFT_PASS;
165 
166 cleanup:
167 	if (cgroup)
168 		cg_destroy(cgroup);
169 	free(cgroup);
170 	return ret;
171 }
172 
173 /*
174  * The test creates the following hierarchy:
175  *       A
176  *    / / \ \
177  *   B  E  I K
178  *  /\  |
179  * C  D F
180  *      |
181  *      G
182  *      |
183  *      H
184  *
185  * with a process in C, H and 3 processes in K.
186  * Then it tries to freeze and unfreeze the whole tree.
187  */
test_cgfreezer_tree(const char * root)188 static int test_cgfreezer_tree(const char *root)
189 {
190 	char *cgroup[10] = {0};
191 	int ret = KSFT_FAIL;
192 	int i;
193 
194 	cgroup[0] = cg_name(root, "cg_test_tree_A");
195 	if (!cgroup[0])
196 		goto cleanup;
197 
198 	cgroup[1] = cg_name(cgroup[0], "B");
199 	if (!cgroup[1])
200 		goto cleanup;
201 
202 	cgroup[2] = cg_name(cgroup[1], "C");
203 	if (!cgroup[2])
204 		goto cleanup;
205 
206 	cgroup[3] = cg_name(cgroup[1], "D");
207 	if (!cgroup[3])
208 		goto cleanup;
209 
210 	cgroup[4] = cg_name(cgroup[0], "E");
211 	if (!cgroup[4])
212 		goto cleanup;
213 
214 	cgroup[5] = cg_name(cgroup[4], "F");
215 	if (!cgroup[5])
216 		goto cleanup;
217 
218 	cgroup[6] = cg_name(cgroup[5], "G");
219 	if (!cgroup[6])
220 		goto cleanup;
221 
222 	cgroup[7] = cg_name(cgroup[6], "H");
223 	if (!cgroup[7])
224 		goto cleanup;
225 
226 	cgroup[8] = cg_name(cgroup[0], "I");
227 	if (!cgroup[8])
228 		goto cleanup;
229 
230 	cgroup[9] = cg_name(cgroup[0], "K");
231 	if (!cgroup[9])
232 		goto cleanup;
233 
234 	for (i = 0; i < 10; i++)
235 		if (cg_create(cgroup[i]))
236 			goto cleanup;
237 
238 	cg_run_nowait(cgroup[2], child_fn, NULL);
239 	cg_run_nowait(cgroup[7], child_fn, NULL);
240 	cg_run_nowait(cgroup[9], child_fn, NULL);
241 	cg_run_nowait(cgroup[9], child_fn, NULL);
242 	cg_run_nowait(cgroup[9], child_fn, NULL);
243 
244 	/*
245 	 * Wait until all child processes will enter
246 	 * corresponding cgroups.
247 	 */
248 
249 	if (cg_wait_for_proc_count(cgroup[2], 1) ||
250 	    cg_wait_for_proc_count(cgroup[7], 1) ||
251 	    cg_wait_for_proc_count(cgroup[9], 3))
252 		goto cleanup;
253 
254 	/*
255 	 * Freeze B.
256 	 */
257 	if (cg_freeze_wait(cgroup[1], true))
258 		goto cleanup;
259 
260 	/*
261 	 * Freeze F.
262 	 */
263 	if (cg_freeze_wait(cgroup[5], true))
264 		goto cleanup;
265 
266 	/*
267 	 * Freeze G.
268 	 */
269 	if (cg_freeze_wait(cgroup[6], true))
270 		goto cleanup;
271 
272 	/*
273 	 * Check that A and E are not frozen.
274 	 */
275 	if (cg_check_frozen(cgroup[0], false))
276 		goto cleanup;
277 
278 	if (cg_check_frozen(cgroup[4], false))
279 		goto cleanup;
280 
281 	/*
282 	 * Freeze A. Check that A, B and E are frozen.
283 	 */
284 	if (cg_freeze_wait(cgroup[0], true))
285 		goto cleanup;
286 
287 	if (cg_check_frozen(cgroup[1], true))
288 		goto cleanup;
289 
290 	if (cg_check_frozen(cgroup[4], true))
291 		goto cleanup;
292 
293 	/*
294 	 * Unfreeze B, F and G
295 	 */
296 	if (cg_freeze_nowait(cgroup[1], false))
297 		goto cleanup;
298 
299 	if (cg_freeze_nowait(cgroup[5], false))
300 		goto cleanup;
301 
302 	if (cg_freeze_nowait(cgroup[6], false))
303 		goto cleanup;
304 
305 	/*
306 	 * Check that C and H are still frozen.
307 	 */
308 	if (cg_check_frozen(cgroup[2], true))
309 		goto cleanup;
310 
311 	if (cg_check_frozen(cgroup[7], true))
312 		goto cleanup;
313 
314 	/*
315 	 * Unfreeze A. Check that A, C and K are not frozen.
316 	 */
317 	if (cg_freeze_wait(cgroup[0], false))
318 		goto cleanup;
319 
320 	if (cg_check_frozen(cgroup[2], false))
321 		goto cleanup;
322 
323 	if (cg_check_frozen(cgroup[9], false))
324 		goto cleanup;
325 
326 	ret = KSFT_PASS;
327 
328 cleanup:
329 	for (i = 9; i >= 0 && cgroup[i]; i--) {
330 		cg_destroy(cgroup[i]);
331 		free(cgroup[i]);
332 	}
333 
334 	return ret;
335 }
336 
337 /*
338  * A fork bomb emulator.
339  */
forkbomb_fn(const char * cgroup,void * arg)340 static int forkbomb_fn(const char *cgroup, void *arg)
341 {
342 	int ppid;
343 
344 	fork();
345 	fork();
346 
347 	ppid = getppid();
348 
349 	while (getppid() == ppid)
350 		usleep(1000);
351 
352 	return getppid() == ppid;
353 }
354 
355 /*
356  * The test runs a fork bomb in a cgroup and tries to freeze it.
357  * Then it kills all processes and checks that cgroup isn't populated
358  * anymore.
359  */
test_cgfreezer_forkbomb(const char * root)360 static int test_cgfreezer_forkbomb(const char *root)
361 {
362 	int ret = KSFT_FAIL;
363 	char *cgroup = NULL;
364 
365 	cgroup = cg_name(root, "cg_forkbomb_test");
366 	if (!cgroup)
367 		goto cleanup;
368 
369 	if (cg_create(cgroup))
370 		goto cleanup;
371 
372 	cg_run_nowait(cgroup, forkbomb_fn, NULL);
373 
374 	usleep(100000);
375 
376 	if (cg_freeze_wait(cgroup, true))
377 		goto cleanup;
378 
379 	if (cg_killall(cgroup))
380 		goto cleanup;
381 
382 	if (cg_wait_for_proc_count(cgroup, 0))
383 		goto cleanup;
384 
385 	ret = KSFT_PASS;
386 
387 cleanup:
388 	if (cgroup)
389 		cg_destroy(cgroup);
390 	free(cgroup);
391 	return ret;
392 }
393 
394 /*
395  * The test creates a cgroups and freezes it. Then it creates a child cgroup
396  * and populates it with a task. After that it checks that the child cgroup
397  * is frozen and the parent cgroup remains frozen too.
398  */
test_cgfreezer_mkdir(const char * root)399 static int test_cgfreezer_mkdir(const char *root)
400 {
401 	int ret = KSFT_FAIL;
402 	char *parent, *child = NULL;
403 	int pid;
404 
405 	parent = cg_name(root, "cg_test_mkdir_A");
406 	if (!parent)
407 		goto cleanup;
408 
409 	child = cg_name(parent, "cg_test_mkdir_B");
410 	if (!child)
411 		goto cleanup;
412 
413 	if (cg_create(parent))
414 		goto cleanup;
415 
416 	if (cg_freeze_wait(parent, true))
417 		goto cleanup;
418 
419 	if (cg_create(child))
420 		goto cleanup;
421 
422 	pid = cg_run_nowait(child, child_fn, NULL);
423 	if (pid < 0)
424 		goto cleanup;
425 
426 	if (cg_wait_for_proc_count(child, 1))
427 		goto cleanup;
428 
429 	if (cg_check_frozen(child, true))
430 		goto cleanup;
431 
432 	if (cg_check_frozen(parent, true))
433 		goto cleanup;
434 
435 	ret = KSFT_PASS;
436 
437 cleanup:
438 	if (child)
439 		cg_destroy(child);
440 	free(child);
441 	if (parent)
442 		cg_destroy(parent);
443 	free(parent);
444 	return ret;
445 }
446 
447 /*
448  * The test creates two nested cgroups, freezes the parent
449  * and removes the child. Then it checks that the parent cgroup
450  * remains frozen and it's possible to create a new child
451  * without unfreezing. The new child is frozen too.
452  */
test_cgfreezer_rmdir(const char * root)453 static int test_cgfreezer_rmdir(const char *root)
454 {
455 	int ret = KSFT_FAIL;
456 	char *parent, *child = NULL;
457 
458 	parent = cg_name(root, "cg_test_rmdir_A");
459 	if (!parent)
460 		goto cleanup;
461 
462 	child = cg_name(parent, "cg_test_rmdir_B");
463 	if (!child)
464 		goto cleanup;
465 
466 	if (cg_create(parent))
467 		goto cleanup;
468 
469 	if (cg_create(child))
470 		goto cleanup;
471 
472 	if (cg_freeze_wait(parent, true))
473 		goto cleanup;
474 
475 	if (cg_destroy(child))
476 		goto cleanup;
477 
478 	if (cg_check_frozen(parent, true))
479 		goto cleanup;
480 
481 	if (cg_create(child))
482 		goto cleanup;
483 
484 	if (cg_check_frozen(child, true))
485 		goto cleanup;
486 
487 	ret = KSFT_PASS;
488 
489 cleanup:
490 	if (child)
491 		cg_destroy(child);
492 	free(child);
493 	if (parent)
494 		cg_destroy(parent);
495 	free(parent);
496 	return ret;
497 }
498 
499 /*
500  * The test creates two cgroups: A and B, runs a process in A
501  * and performs several migrations:
502  * 1) A (running) -> B (frozen)
503  * 2) B (frozen) -> A (running)
504  * 3) A (frozen) -> B (frozen)
505  *
506  * On each step it checks the actual state of both cgroups.
507  */
test_cgfreezer_migrate(const char * root)508 static int test_cgfreezer_migrate(const char *root)
509 {
510 	int ret = KSFT_FAIL;
511 	char *cgroup[2] = {0};
512 	int pid;
513 
514 	cgroup[0] = cg_name(root, "cg_test_migrate_A");
515 	if (!cgroup[0])
516 		goto cleanup;
517 
518 	cgroup[1] = cg_name(root, "cg_test_migrate_B");
519 	if (!cgroup[1])
520 		goto cleanup;
521 
522 	if (cg_create(cgroup[0]))
523 		goto cleanup;
524 
525 	if (cg_create(cgroup[1]))
526 		goto cleanup;
527 
528 	pid = cg_run_nowait(cgroup[0], child_fn, NULL);
529 	if (pid < 0)
530 		goto cleanup;
531 
532 	if (cg_wait_for_proc_count(cgroup[0], 1))
533 		goto cleanup;
534 
535 	/*
536 	 * Migrate from A (running) to B (frozen)
537 	 */
538 	if (cg_freeze_wait(cgroup[1], true))
539 		goto cleanup;
540 
541 	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
542 		goto cleanup;
543 
544 	if (cg_check_frozen(cgroup[0], false))
545 		goto cleanup;
546 
547 	/*
548 	 * Migrate from B (frozen) to A (running)
549 	 */
550 	if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
551 		goto cleanup;
552 
553 	if (cg_check_frozen(cgroup[1], true))
554 		goto cleanup;
555 
556 	/*
557 	 * Migrate from A (frozen) to B (frozen)
558 	 */
559 	if (cg_freeze_wait(cgroup[0], true))
560 		goto cleanup;
561 
562 	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
563 		goto cleanup;
564 
565 	if (cg_check_frozen(cgroup[0], true))
566 		goto cleanup;
567 
568 	ret = KSFT_PASS;
569 
570 cleanup:
571 	if (cgroup[0])
572 		cg_destroy(cgroup[0]);
573 	free(cgroup[0]);
574 	if (cgroup[1])
575 		cg_destroy(cgroup[1]);
576 	free(cgroup[1]);
577 	return ret;
578 }
579 
580 /*
581  * The test checks that ptrace works with a tracing process in a frozen cgroup.
582  */
test_cgfreezer_ptrace(const char * root)583 static int test_cgfreezer_ptrace(const char *root)
584 {
585 	int ret = KSFT_FAIL;
586 	char *cgroup = NULL;
587 	siginfo_t siginfo;
588 	int pid;
589 
590 	cgroup = cg_name(root, "cg_test_ptrace");
591 	if (!cgroup)
592 		goto cleanup;
593 
594 	if (cg_create(cgroup))
595 		goto cleanup;
596 
597 	pid = cg_run_nowait(cgroup, child_fn, NULL);
598 	if (pid < 0)
599 		goto cleanup;
600 
601 	if (cg_wait_for_proc_count(cgroup, 1))
602 		goto cleanup;
603 
604 	if (cg_freeze_wait(cgroup, true))
605 		goto cleanup;
606 
607 	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
608 		goto cleanup;
609 
610 	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
611 		goto cleanup;
612 
613 	waitpid(pid, NULL, 0);
614 
615 	/*
616 	 * Cgroup has to remain frozen, however the test task
617 	 * is in traced state.
618 	 */
619 	if (cg_check_frozen(cgroup, true))
620 		goto cleanup;
621 
622 	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
623 		goto cleanup;
624 
625 	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
626 		goto cleanup;
627 
628 	if (cg_check_frozen(cgroup, true))
629 		goto cleanup;
630 
631 	ret = KSFT_PASS;
632 
633 cleanup:
634 	if (cgroup)
635 		cg_destroy(cgroup);
636 	free(cgroup);
637 	return ret;
638 }
639 
640 /*
641  * Check if the process is stopped.
642  */
proc_check_stopped(int pid)643 static int proc_check_stopped(int pid)
644 {
645 	char buf[PAGE_SIZE];
646 	int len;
647 
648 	len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
649 	if (len == -1) {
650 		debug("Can't get %d stat\n", pid);
651 		return -1;
652 	}
653 
654 	if (strstr(buf, "(test_freezer) T ") == NULL) {
655 		debug("Process %d in the unexpected state: %s\n", pid, buf);
656 		return -1;
657 	}
658 
659 	return 0;
660 }
661 
662 /*
663  * Test that it's possible to freeze a cgroup with a stopped process.
664  */
test_cgfreezer_stopped(const char * root)665 static int test_cgfreezer_stopped(const char *root)
666 {
667 	int pid, ret = KSFT_FAIL;
668 	char *cgroup = NULL;
669 
670 	cgroup = cg_name(root, "cg_test_stopped");
671 	if (!cgroup)
672 		goto cleanup;
673 
674 	if (cg_create(cgroup))
675 		goto cleanup;
676 
677 	pid = cg_run_nowait(cgroup, child_fn, NULL);
678 
679 	if (cg_wait_for_proc_count(cgroup, 1))
680 		goto cleanup;
681 
682 	if (kill(pid, SIGSTOP))
683 		goto cleanup;
684 
685 	if (cg_check_frozen(cgroup, false))
686 		goto cleanup;
687 
688 	if (cg_freeze_wait(cgroup, true))
689 		goto cleanup;
690 
691 	if (cg_freeze_wait(cgroup, false))
692 		goto cleanup;
693 
694 	if (proc_check_stopped(pid))
695 		goto cleanup;
696 
697 	ret = KSFT_PASS;
698 
699 cleanup:
700 	if (cgroup)
701 		cg_destroy(cgroup);
702 	free(cgroup);
703 	return ret;
704 }
705 
706 /*
707  * Test that it's possible to freeze a cgroup with a ptraced process.
708  */
test_cgfreezer_ptraced(const char * root)709 static int test_cgfreezer_ptraced(const char *root)
710 {
711 	int pid, ret = KSFT_FAIL;
712 	char *cgroup = NULL;
713 	siginfo_t siginfo;
714 
715 	cgroup = cg_name(root, "cg_test_ptraced");
716 	if (!cgroup)
717 		goto cleanup;
718 
719 	if (cg_create(cgroup))
720 		goto cleanup;
721 
722 	pid = cg_run_nowait(cgroup, child_fn, NULL);
723 
724 	if (cg_wait_for_proc_count(cgroup, 1))
725 		goto cleanup;
726 
727 	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
728 		goto cleanup;
729 
730 	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
731 		goto cleanup;
732 
733 	waitpid(pid, NULL, 0);
734 
735 	if (cg_check_frozen(cgroup, false))
736 		goto cleanup;
737 
738 	if (cg_freeze_wait(cgroup, true))
739 		goto cleanup;
740 
741 	/*
742 	 * cg_check_frozen(cgroup, true) will fail here,
743 	 * because the task is in the TRACEd state.
744 	 */
745 	if (cg_freeze_wait(cgroup, false))
746 		goto cleanup;
747 
748 	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
749 		goto cleanup;
750 
751 	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
752 		goto cleanup;
753 
754 	ret = KSFT_PASS;
755 
756 cleanup:
757 	if (cgroup)
758 		cg_destroy(cgroup);
759 	free(cgroup);
760 	return ret;
761 }
762 
vfork_fn(const char * cgroup,void * arg)763 static int vfork_fn(const char *cgroup, void *arg)
764 {
765 	int pid = vfork();
766 
767 	if (pid == 0)
768 		while (true)
769 			sleep(1);
770 
771 	return pid;
772 }
773 
774 /*
775  * Test that it's possible to freeze a cgroup with a process,
776  * which called vfork() and is waiting for a child.
777  */
test_cgfreezer_vfork(const char * root)778 static int test_cgfreezer_vfork(const char *root)
779 {
780 	int ret = KSFT_FAIL;
781 	char *cgroup = NULL;
782 
783 	cgroup = cg_name(root, "cg_test_vfork");
784 	if (!cgroup)
785 		goto cleanup;
786 
787 	if (cg_create(cgroup))
788 		goto cleanup;
789 
790 	cg_run_nowait(cgroup, vfork_fn, NULL);
791 
792 	if (cg_wait_for_proc_count(cgroup, 2))
793 		goto cleanup;
794 
795 	if (cg_freeze_wait(cgroup, true))
796 		goto cleanup;
797 
798 	ret = KSFT_PASS;
799 
800 cleanup:
801 	if (cgroup)
802 		cg_destroy(cgroup);
803 	free(cgroup);
804 	return ret;
805 }
806 
807 #define T(x) { x, #x }
808 struct cgfreezer_test {
809 	int (*fn)(const char *root);
810 	const char *name;
811 } tests[] = {
812 	T(test_cgfreezer_simple),
813 	T(test_cgfreezer_tree),
814 	T(test_cgfreezer_forkbomb),
815 	T(test_cgfreezer_mkdir),
816 	T(test_cgfreezer_rmdir),
817 	T(test_cgfreezer_migrate),
818 	T(test_cgfreezer_ptrace),
819 	T(test_cgfreezer_stopped),
820 	T(test_cgfreezer_ptraced),
821 	T(test_cgfreezer_vfork),
822 };
823 #undef T
824 
main(int argc,char * argv[])825 int main(int argc, char *argv[])
826 {
827 	char root[PATH_MAX];
828 	int i, ret = EXIT_SUCCESS;
829 
830 	if (cg_find_unified_root(root, sizeof(root), NULL))
831 		ksft_exit_skip("cgroup v2 isn't mounted\n");
832 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
833 		switch (tests[i].fn(root)) {
834 		case KSFT_PASS:
835 			ksft_test_result_pass("%s\n", tests[i].name);
836 			break;
837 		case KSFT_SKIP:
838 			ksft_test_result_skip("%s\n", tests[i].name);
839 			break;
840 		default:
841 			ret = EXIT_FAILURE;
842 			ksft_test_result_fail("%s\n", tests[i].name);
843 			break;
844 		}
845 	}
846 
847 	return ret;
848 }
849