cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

test_freezer.c (15563B)


      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 */
     27static 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 */
     50static 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 */
     59static 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
     83out:
     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 */
     93static 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);
    112out:
    113	close(fd);
    114	return ret;
    115}
    116
    117/*
    118 * A simple process running in a sleep loop until being
    119 * re-parented.
    120 */
    121static 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 */
    136static 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
    166cleanup:
    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 */
    188static 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
    328cleanup:
    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 */
    340static 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 */
    360static 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
    387cleanup:
    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 */
    399static 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
    437cleanup:
    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 */
    453static 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
    489cleanup:
    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 */
    508static 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
    570cleanup:
    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 */
    583static 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
    633cleanup:
    634	if (cgroup)
    635		cg_destroy(cgroup);
    636	free(cgroup);
    637	return ret;
    638}
    639
    640/*
    641 * Check if the process is stopped.
    642 */
    643static 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 */
    665static 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
    699cleanup:
    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 */
    709static 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 in 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
    756cleanup:
    757	if (cgroup)
    758		cg_destroy(cgroup);
    759	free(cgroup);
    760	return ret;
    761}
    762
    763static 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 */
    778static 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
    800cleanup:
    801	if (cgroup)
    802		cg_destroy(cgroup);
    803	free(cgroup);
    804	return ret;
    805}
    806
    807#define T(x) { x, #x }
    808struct 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
    825int 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)))
    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}