Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 #include <stdbool.h>
0003 #include <linux/limits.h>
0004 #include <sys/ptrace.h>
0005 #include <sys/types.h>
0006 #include <sys/mman.h>
0007 #include <unistd.h>
0008 #include <stdio.h>
0009 #include <errno.h>
0010 #include <stdlib.h>
0011 #include <string.h>
0012 #include <sys/wait.h>
0013 
0014 #include "../kselftest.h"
0015 #include "cgroup_util.h"
0016 
0017 #define DEBUG
0018 #ifdef DEBUG
0019 #define debug(args...) fprintf(stderr, args)
0020 #else
0021 #define debug(args...)
0022 #endif
0023 
0024 /*
0025  * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
0026  */
0027 static int cg_check_frozen(const char *cgroup, bool frozen)
0028 {
0029     if (frozen) {
0030         if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
0031             debug("Cgroup %s isn't frozen\n", cgroup);
0032             return -1;
0033         }
0034     } else {
0035         /*
0036          * Check the cgroup.events::frozen value.
0037          */
0038         if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
0039             debug("Cgroup %s is frozen\n", cgroup);
0040             return -1;
0041         }
0042     }
0043 
0044     return 0;
0045 }
0046 
0047 /*
0048  * Freeze the given cgroup.
0049  */
0050 static int cg_freeze_nowait(const char *cgroup, bool freeze)
0051 {
0052     return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
0053 }
0054 
0055 /*
0056  * Attach a task to the given cgroup and wait for a cgroup frozen event.
0057  * All transient events (e.g. populated) are ignored.
0058  */
0059 static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
0060                     bool frozen)
0061 {
0062     int fd, ret = -1;
0063     int attempts;
0064 
0065     fd = cg_prepare_for_wait(cgroup);
0066     if (fd < 0)
0067         return fd;
0068 
0069     ret = cg_enter(cgroup, pid);
0070     if (ret)
0071         goto out;
0072 
0073     for (attempts = 0; attempts < 10; attempts++) {
0074         ret = cg_wait_for(fd);
0075         if (ret)
0076             break;
0077 
0078         ret = cg_check_frozen(cgroup, frozen);
0079         if (ret)
0080             continue;
0081     }
0082 
0083 out:
0084     close(fd);
0085     return ret;
0086 }
0087 
0088 /*
0089  * Freeze the given cgroup and wait for the inotify signal.
0090  * If there are no events in 10 seconds, treat this as an error.
0091  * Then check that the cgroup is in the desired state.
0092  */
0093 static int cg_freeze_wait(const char *cgroup, bool freeze)
0094 {
0095     int fd, ret = -1;
0096 
0097     fd = cg_prepare_for_wait(cgroup);
0098     if (fd < 0)
0099         return fd;
0100 
0101     ret = cg_freeze_nowait(cgroup, freeze);
0102     if (ret) {
0103         debug("Error: cg_freeze_nowait() failed\n");
0104         goto out;
0105     }
0106 
0107     ret = cg_wait_for(fd);
0108     if (ret)
0109         goto out;
0110 
0111     ret = cg_check_frozen(cgroup, freeze);
0112 out:
0113     close(fd);
0114     return ret;
0115 }
0116 
0117 /*
0118  * A simple process running in a sleep loop until being
0119  * re-parented.
0120  */
0121 static int child_fn(const char *cgroup, void *arg)
0122 {
0123     int ppid = getppid();
0124 
0125     while (getppid() == ppid)
0126         usleep(1000);
0127 
0128     return getppid() == ppid;
0129 }
0130 
0131 /*
0132  * A simple test for the cgroup freezer: populated the cgroup with 100
0133  * running processes and freeze it. Then unfreeze it. Then it kills all
0134  * processes and destroys the cgroup.
0135  */
0136 static int test_cgfreezer_simple(const char *root)
0137 {
0138     int ret = KSFT_FAIL;
0139     char *cgroup = NULL;
0140     int i;
0141 
0142     cgroup = cg_name(root, "cg_test_simple");
0143     if (!cgroup)
0144         goto cleanup;
0145 
0146     if (cg_create(cgroup))
0147         goto cleanup;
0148 
0149     for (i = 0; i < 100; i++)
0150         cg_run_nowait(cgroup, child_fn, NULL);
0151 
0152     if (cg_wait_for_proc_count(cgroup, 100))
0153         goto cleanup;
0154 
0155     if (cg_check_frozen(cgroup, false))
0156         goto cleanup;
0157 
0158     if (cg_freeze_wait(cgroup, true))
0159         goto cleanup;
0160 
0161     if (cg_freeze_wait(cgroup, false))
0162         goto cleanup;
0163 
0164     ret = KSFT_PASS;
0165 
0166 cleanup:
0167     if (cgroup)
0168         cg_destroy(cgroup);
0169     free(cgroup);
0170     return ret;
0171 }
0172 
0173 /*
0174  * The test creates the following hierarchy:
0175  *       A
0176  *    / / \ \
0177  *   B  E  I K
0178  *  /\  |
0179  * C  D F
0180  *      |
0181  *      G
0182  *      |
0183  *      H
0184  *
0185  * with a process in C, H and 3 processes in K.
0186  * Then it tries to freeze and unfreeze the whole tree.
0187  */
0188 static int test_cgfreezer_tree(const char *root)
0189 {
0190     char *cgroup[10] = {0};
0191     int ret = KSFT_FAIL;
0192     int i;
0193 
0194     cgroup[0] = cg_name(root, "cg_test_tree_A");
0195     if (!cgroup[0])
0196         goto cleanup;
0197 
0198     cgroup[1] = cg_name(cgroup[0], "B");
0199     if (!cgroup[1])
0200         goto cleanup;
0201 
0202     cgroup[2] = cg_name(cgroup[1], "C");
0203     if (!cgroup[2])
0204         goto cleanup;
0205 
0206     cgroup[3] = cg_name(cgroup[1], "D");
0207     if (!cgroup[3])
0208         goto cleanup;
0209 
0210     cgroup[4] = cg_name(cgroup[0], "E");
0211     if (!cgroup[4])
0212         goto cleanup;
0213 
0214     cgroup[5] = cg_name(cgroup[4], "F");
0215     if (!cgroup[5])
0216         goto cleanup;
0217 
0218     cgroup[6] = cg_name(cgroup[5], "G");
0219     if (!cgroup[6])
0220         goto cleanup;
0221 
0222     cgroup[7] = cg_name(cgroup[6], "H");
0223     if (!cgroup[7])
0224         goto cleanup;
0225 
0226     cgroup[8] = cg_name(cgroup[0], "I");
0227     if (!cgroup[8])
0228         goto cleanup;
0229 
0230     cgroup[9] = cg_name(cgroup[0], "K");
0231     if (!cgroup[9])
0232         goto cleanup;
0233 
0234     for (i = 0; i < 10; i++)
0235         if (cg_create(cgroup[i]))
0236             goto cleanup;
0237 
0238     cg_run_nowait(cgroup[2], child_fn, NULL);
0239     cg_run_nowait(cgroup[7], child_fn, NULL);
0240     cg_run_nowait(cgroup[9], child_fn, NULL);
0241     cg_run_nowait(cgroup[9], child_fn, NULL);
0242     cg_run_nowait(cgroup[9], child_fn, NULL);
0243 
0244     /*
0245      * Wait until all child processes will enter
0246      * corresponding cgroups.
0247      */
0248 
0249     if (cg_wait_for_proc_count(cgroup[2], 1) ||
0250         cg_wait_for_proc_count(cgroup[7], 1) ||
0251         cg_wait_for_proc_count(cgroup[9], 3))
0252         goto cleanup;
0253 
0254     /*
0255      * Freeze B.
0256      */
0257     if (cg_freeze_wait(cgroup[1], true))
0258         goto cleanup;
0259 
0260     /*
0261      * Freeze F.
0262      */
0263     if (cg_freeze_wait(cgroup[5], true))
0264         goto cleanup;
0265 
0266     /*
0267      * Freeze G.
0268      */
0269     if (cg_freeze_wait(cgroup[6], true))
0270         goto cleanup;
0271 
0272     /*
0273      * Check that A and E are not frozen.
0274      */
0275     if (cg_check_frozen(cgroup[0], false))
0276         goto cleanup;
0277 
0278     if (cg_check_frozen(cgroup[4], false))
0279         goto cleanup;
0280 
0281     /*
0282      * Freeze A. Check that A, B and E are frozen.
0283      */
0284     if (cg_freeze_wait(cgroup[0], true))
0285         goto cleanup;
0286 
0287     if (cg_check_frozen(cgroup[1], true))
0288         goto cleanup;
0289 
0290     if (cg_check_frozen(cgroup[4], true))
0291         goto cleanup;
0292 
0293     /*
0294      * Unfreeze B, F and G
0295      */
0296     if (cg_freeze_nowait(cgroup[1], false))
0297         goto cleanup;
0298 
0299     if (cg_freeze_nowait(cgroup[5], false))
0300         goto cleanup;
0301 
0302     if (cg_freeze_nowait(cgroup[6], false))
0303         goto cleanup;
0304 
0305     /*
0306      * Check that C and H are still frozen.
0307      */
0308     if (cg_check_frozen(cgroup[2], true))
0309         goto cleanup;
0310 
0311     if (cg_check_frozen(cgroup[7], true))
0312         goto cleanup;
0313 
0314     /*
0315      * Unfreeze A. Check that A, C and K are not frozen.
0316      */
0317     if (cg_freeze_wait(cgroup[0], false))
0318         goto cleanup;
0319 
0320     if (cg_check_frozen(cgroup[2], false))
0321         goto cleanup;
0322 
0323     if (cg_check_frozen(cgroup[9], false))
0324         goto cleanup;
0325 
0326     ret = KSFT_PASS;
0327 
0328 cleanup:
0329     for (i = 9; i >= 0 && cgroup[i]; i--) {
0330         cg_destroy(cgroup[i]);
0331         free(cgroup[i]);
0332     }
0333 
0334     return ret;
0335 }
0336 
0337 /*
0338  * A fork bomb emulator.
0339  */
0340 static int forkbomb_fn(const char *cgroup, void *arg)
0341 {
0342     int ppid;
0343 
0344     fork();
0345     fork();
0346 
0347     ppid = getppid();
0348 
0349     while (getppid() == ppid)
0350         usleep(1000);
0351 
0352     return getppid() == ppid;
0353 }
0354 
0355 /*
0356  * The test runs a fork bomb in a cgroup and tries to freeze it.
0357  * Then it kills all processes and checks that cgroup isn't populated
0358  * anymore.
0359  */
0360 static int test_cgfreezer_forkbomb(const char *root)
0361 {
0362     int ret = KSFT_FAIL;
0363     char *cgroup = NULL;
0364 
0365     cgroup = cg_name(root, "cg_forkbomb_test");
0366     if (!cgroup)
0367         goto cleanup;
0368 
0369     if (cg_create(cgroup))
0370         goto cleanup;
0371 
0372     cg_run_nowait(cgroup, forkbomb_fn, NULL);
0373 
0374     usleep(100000);
0375 
0376     if (cg_freeze_wait(cgroup, true))
0377         goto cleanup;
0378 
0379     if (cg_killall(cgroup))
0380         goto cleanup;
0381 
0382     if (cg_wait_for_proc_count(cgroup, 0))
0383         goto cleanup;
0384 
0385     ret = KSFT_PASS;
0386 
0387 cleanup:
0388     if (cgroup)
0389         cg_destroy(cgroup);
0390     free(cgroup);
0391     return ret;
0392 }
0393 
0394 /*
0395  * The test creates a cgroups and freezes it. Then it creates a child cgroup
0396  * and populates it with a task. After that it checks that the child cgroup
0397  * is frozen and the parent cgroup remains frozen too.
0398  */
0399 static int test_cgfreezer_mkdir(const char *root)
0400 {
0401     int ret = KSFT_FAIL;
0402     char *parent, *child = NULL;
0403     int pid;
0404 
0405     parent = cg_name(root, "cg_test_mkdir_A");
0406     if (!parent)
0407         goto cleanup;
0408 
0409     child = cg_name(parent, "cg_test_mkdir_B");
0410     if (!child)
0411         goto cleanup;
0412 
0413     if (cg_create(parent))
0414         goto cleanup;
0415 
0416     if (cg_freeze_wait(parent, true))
0417         goto cleanup;
0418 
0419     if (cg_create(child))
0420         goto cleanup;
0421 
0422     pid = cg_run_nowait(child, child_fn, NULL);
0423     if (pid < 0)
0424         goto cleanup;
0425 
0426     if (cg_wait_for_proc_count(child, 1))
0427         goto cleanup;
0428 
0429     if (cg_check_frozen(child, true))
0430         goto cleanup;
0431 
0432     if (cg_check_frozen(parent, true))
0433         goto cleanup;
0434 
0435     ret = KSFT_PASS;
0436 
0437 cleanup:
0438     if (child)
0439         cg_destroy(child);
0440     free(child);
0441     if (parent)
0442         cg_destroy(parent);
0443     free(parent);
0444     return ret;
0445 }
0446 
0447 /*
0448  * The test creates two nested cgroups, freezes the parent
0449  * and removes the child. Then it checks that the parent cgroup
0450  * remains frozen and it's possible to create a new child
0451  * without unfreezing. The new child is frozen too.
0452  */
0453 static int test_cgfreezer_rmdir(const char *root)
0454 {
0455     int ret = KSFT_FAIL;
0456     char *parent, *child = NULL;
0457 
0458     parent = cg_name(root, "cg_test_rmdir_A");
0459     if (!parent)
0460         goto cleanup;
0461 
0462     child = cg_name(parent, "cg_test_rmdir_B");
0463     if (!child)
0464         goto cleanup;
0465 
0466     if (cg_create(parent))
0467         goto cleanup;
0468 
0469     if (cg_create(child))
0470         goto cleanup;
0471 
0472     if (cg_freeze_wait(parent, true))
0473         goto cleanup;
0474 
0475     if (cg_destroy(child))
0476         goto cleanup;
0477 
0478     if (cg_check_frozen(parent, true))
0479         goto cleanup;
0480 
0481     if (cg_create(child))
0482         goto cleanup;
0483 
0484     if (cg_check_frozen(child, true))
0485         goto cleanup;
0486 
0487     ret = KSFT_PASS;
0488 
0489 cleanup:
0490     if (child)
0491         cg_destroy(child);
0492     free(child);
0493     if (parent)
0494         cg_destroy(parent);
0495     free(parent);
0496     return ret;
0497 }
0498 
0499 /*
0500  * The test creates two cgroups: A and B, runs a process in A
0501  * and performs several migrations:
0502  * 1) A (running) -> B (frozen)
0503  * 2) B (frozen) -> A (running)
0504  * 3) A (frozen) -> B (frozen)
0505  *
0506  * On each step it checks the actual state of both cgroups.
0507  */
0508 static int test_cgfreezer_migrate(const char *root)
0509 {
0510     int ret = KSFT_FAIL;
0511     char *cgroup[2] = {0};
0512     int pid;
0513 
0514     cgroup[0] = cg_name(root, "cg_test_migrate_A");
0515     if (!cgroup[0])
0516         goto cleanup;
0517 
0518     cgroup[1] = cg_name(root, "cg_test_migrate_B");
0519     if (!cgroup[1])
0520         goto cleanup;
0521 
0522     if (cg_create(cgroup[0]))
0523         goto cleanup;
0524 
0525     if (cg_create(cgroup[1]))
0526         goto cleanup;
0527 
0528     pid = cg_run_nowait(cgroup[0], child_fn, NULL);
0529     if (pid < 0)
0530         goto cleanup;
0531 
0532     if (cg_wait_for_proc_count(cgroup[0], 1))
0533         goto cleanup;
0534 
0535     /*
0536      * Migrate from A (running) to B (frozen)
0537      */
0538     if (cg_freeze_wait(cgroup[1], true))
0539         goto cleanup;
0540 
0541     if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
0542         goto cleanup;
0543 
0544     if (cg_check_frozen(cgroup[0], false))
0545         goto cleanup;
0546 
0547     /*
0548      * Migrate from B (frozen) to A (running)
0549      */
0550     if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
0551         goto cleanup;
0552 
0553     if (cg_check_frozen(cgroup[1], true))
0554         goto cleanup;
0555 
0556     /*
0557      * Migrate from A (frozen) to B (frozen)
0558      */
0559     if (cg_freeze_wait(cgroup[0], true))
0560         goto cleanup;
0561 
0562     if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
0563         goto cleanup;
0564 
0565     if (cg_check_frozen(cgroup[0], true))
0566         goto cleanup;
0567 
0568     ret = KSFT_PASS;
0569 
0570 cleanup:
0571     if (cgroup[0])
0572         cg_destroy(cgroup[0]);
0573     free(cgroup[0]);
0574     if (cgroup[1])
0575         cg_destroy(cgroup[1]);
0576     free(cgroup[1]);
0577     return ret;
0578 }
0579 
0580 /*
0581  * The test checks that ptrace works with a tracing process in a frozen cgroup.
0582  */
0583 static int test_cgfreezer_ptrace(const char *root)
0584 {
0585     int ret = KSFT_FAIL;
0586     char *cgroup = NULL;
0587     siginfo_t siginfo;
0588     int pid;
0589 
0590     cgroup = cg_name(root, "cg_test_ptrace");
0591     if (!cgroup)
0592         goto cleanup;
0593 
0594     if (cg_create(cgroup))
0595         goto cleanup;
0596 
0597     pid = cg_run_nowait(cgroup, child_fn, NULL);
0598     if (pid < 0)
0599         goto cleanup;
0600 
0601     if (cg_wait_for_proc_count(cgroup, 1))
0602         goto cleanup;
0603 
0604     if (cg_freeze_wait(cgroup, true))
0605         goto cleanup;
0606 
0607     if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
0608         goto cleanup;
0609 
0610     if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
0611         goto cleanup;
0612 
0613     waitpid(pid, NULL, 0);
0614 
0615     /*
0616      * Cgroup has to remain frozen, however the test task
0617      * is in traced state.
0618      */
0619     if (cg_check_frozen(cgroup, true))
0620         goto cleanup;
0621 
0622     if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
0623         goto cleanup;
0624 
0625     if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
0626         goto cleanup;
0627 
0628     if (cg_check_frozen(cgroup, true))
0629         goto cleanup;
0630 
0631     ret = KSFT_PASS;
0632 
0633 cleanup:
0634     if (cgroup)
0635         cg_destroy(cgroup);
0636     free(cgroup);
0637     return ret;
0638 }
0639 
0640 /*
0641  * Check if the process is stopped.
0642  */
0643 static int proc_check_stopped(int pid)
0644 {
0645     char buf[PAGE_SIZE];
0646     int len;
0647 
0648     len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
0649     if (len == -1) {
0650         debug("Can't get %d stat\n", pid);
0651         return -1;
0652     }
0653 
0654     if (strstr(buf, "(test_freezer) T ") == NULL) {
0655         debug("Process %d in the unexpected state: %s\n", pid, buf);
0656         return -1;
0657     }
0658 
0659     return 0;
0660 }
0661 
0662 /*
0663  * Test that it's possible to freeze a cgroup with a stopped process.
0664  */
0665 static int test_cgfreezer_stopped(const char *root)
0666 {
0667     int pid, ret = KSFT_FAIL;
0668     char *cgroup = NULL;
0669 
0670     cgroup = cg_name(root, "cg_test_stopped");
0671     if (!cgroup)
0672         goto cleanup;
0673 
0674     if (cg_create(cgroup))
0675         goto cleanup;
0676 
0677     pid = cg_run_nowait(cgroup, child_fn, NULL);
0678 
0679     if (cg_wait_for_proc_count(cgroup, 1))
0680         goto cleanup;
0681 
0682     if (kill(pid, SIGSTOP))
0683         goto cleanup;
0684 
0685     if (cg_check_frozen(cgroup, false))
0686         goto cleanup;
0687 
0688     if (cg_freeze_wait(cgroup, true))
0689         goto cleanup;
0690 
0691     if (cg_freeze_wait(cgroup, false))
0692         goto cleanup;
0693 
0694     if (proc_check_stopped(pid))
0695         goto cleanup;
0696 
0697     ret = KSFT_PASS;
0698 
0699 cleanup:
0700     if (cgroup)
0701         cg_destroy(cgroup);
0702     free(cgroup);
0703     return ret;
0704 }
0705 
0706 /*
0707  * Test that it's possible to freeze a cgroup with a ptraced process.
0708  */
0709 static int test_cgfreezer_ptraced(const char *root)
0710 {
0711     int pid, ret = KSFT_FAIL;
0712     char *cgroup = NULL;
0713     siginfo_t siginfo;
0714 
0715     cgroup = cg_name(root, "cg_test_ptraced");
0716     if (!cgroup)
0717         goto cleanup;
0718 
0719     if (cg_create(cgroup))
0720         goto cleanup;
0721 
0722     pid = cg_run_nowait(cgroup, child_fn, NULL);
0723 
0724     if (cg_wait_for_proc_count(cgroup, 1))
0725         goto cleanup;
0726 
0727     if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
0728         goto cleanup;
0729 
0730     if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
0731         goto cleanup;
0732 
0733     waitpid(pid, NULL, 0);
0734 
0735     if (cg_check_frozen(cgroup, false))
0736         goto cleanup;
0737 
0738     if (cg_freeze_wait(cgroup, true))
0739         goto cleanup;
0740 
0741     /*
0742      * cg_check_frozen(cgroup, true) will fail here,
0743      * because the task in in the TRACEd state.
0744      */
0745     if (cg_freeze_wait(cgroup, false))
0746         goto cleanup;
0747 
0748     if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
0749         goto cleanup;
0750 
0751     if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
0752         goto cleanup;
0753 
0754     ret = KSFT_PASS;
0755 
0756 cleanup:
0757     if (cgroup)
0758         cg_destroy(cgroup);
0759     free(cgroup);
0760     return ret;
0761 }
0762 
0763 static int vfork_fn(const char *cgroup, void *arg)
0764 {
0765     int pid = vfork();
0766 
0767     if (pid == 0)
0768         while (true)
0769             sleep(1);
0770 
0771     return pid;
0772 }
0773 
0774 /*
0775  * Test that it's possible to freeze a cgroup with a process,
0776  * which called vfork() and is waiting for a child.
0777  */
0778 static int test_cgfreezer_vfork(const char *root)
0779 {
0780     int ret = KSFT_FAIL;
0781     char *cgroup = NULL;
0782 
0783     cgroup = cg_name(root, "cg_test_vfork");
0784     if (!cgroup)
0785         goto cleanup;
0786 
0787     if (cg_create(cgroup))
0788         goto cleanup;
0789 
0790     cg_run_nowait(cgroup, vfork_fn, NULL);
0791 
0792     if (cg_wait_for_proc_count(cgroup, 2))
0793         goto cleanup;
0794 
0795     if (cg_freeze_wait(cgroup, true))
0796         goto cleanup;
0797 
0798     ret = KSFT_PASS;
0799 
0800 cleanup:
0801     if (cgroup)
0802         cg_destroy(cgroup);
0803     free(cgroup);
0804     return ret;
0805 }
0806 
0807 #define T(x) { x, #x }
0808 struct cgfreezer_test {
0809     int (*fn)(const char *root);
0810     const char *name;
0811 } tests[] = {
0812     T(test_cgfreezer_simple),
0813     T(test_cgfreezer_tree),
0814     T(test_cgfreezer_forkbomb),
0815     T(test_cgfreezer_mkdir),
0816     T(test_cgfreezer_rmdir),
0817     T(test_cgfreezer_migrate),
0818     T(test_cgfreezer_ptrace),
0819     T(test_cgfreezer_stopped),
0820     T(test_cgfreezer_ptraced),
0821     T(test_cgfreezer_vfork),
0822 };
0823 #undef T
0824 
0825 int main(int argc, char *argv[])
0826 {
0827     char root[PATH_MAX];
0828     int i, ret = EXIT_SUCCESS;
0829 
0830     if (cg_find_unified_root(root, sizeof(root)))
0831         ksft_exit_skip("cgroup v2 isn't mounted\n");
0832     for (i = 0; i < ARRAY_SIZE(tests); i++) {
0833         switch (tests[i].fn(root)) {
0834         case KSFT_PASS:
0835             ksft_test_result_pass("%s\n", tests[i].name);
0836             break;
0837         case KSFT_SKIP:
0838             ksft_test_result_skip("%s\n", tests[i].name);
0839             break;
0840         default:
0841             ret = EXIT_FAILURE;
0842             ksft_test_result_fail("%s\n", tests[i].name);
0843             break;
0844         }
0845     }
0846 
0847     return ret;
0848 }