15313bfe4SRoman Gushchin /* SPDX-License-Identifier: GPL-2.0 */
25313bfe4SRoman Gushchin #include <stdbool.h>
35313bfe4SRoman Gushchin #include <linux/limits.h>
45313bfe4SRoman Gushchin #include <sys/ptrace.h>
55313bfe4SRoman Gushchin #include <sys/types.h>
65313bfe4SRoman Gushchin #include <sys/mman.h>
75313bfe4SRoman Gushchin #include <unistd.h>
85313bfe4SRoman Gushchin #include <stdio.h>
95313bfe4SRoman Gushchin #include <errno.h>
105313bfe4SRoman Gushchin #include <stdlib.h>
115313bfe4SRoman Gushchin #include <string.h>
125313bfe4SRoman Gushchin #include <sys/wait.h>
135313bfe4SRoman Gushchin 
145313bfe4SRoman Gushchin #include "../kselftest.h"
155313bfe4SRoman Gushchin #include "cgroup_util.h"
165313bfe4SRoman Gushchin 
175313bfe4SRoman Gushchin #define DEBUG
185313bfe4SRoman Gushchin #ifdef DEBUG
195313bfe4SRoman Gushchin #define debug(args...) fprintf(stderr, args)
205313bfe4SRoman Gushchin #else
215313bfe4SRoman Gushchin #define debug(args...)
225313bfe4SRoman Gushchin #endif
235313bfe4SRoman Gushchin 
245313bfe4SRoman Gushchin /*
255313bfe4SRoman Gushchin  * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
265313bfe4SRoman Gushchin  */
cg_check_frozen(const char * cgroup,bool frozen)275313bfe4SRoman Gushchin static int cg_check_frozen(const char *cgroup, bool frozen)
285313bfe4SRoman Gushchin {
295313bfe4SRoman Gushchin 	if (frozen) {
305313bfe4SRoman Gushchin 		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
315313bfe4SRoman Gushchin 			debug("Cgroup %s isn't frozen\n", cgroup);
325313bfe4SRoman Gushchin 			return -1;
335313bfe4SRoman Gushchin 		}
345313bfe4SRoman Gushchin 	} else {
355313bfe4SRoman Gushchin 		/*
365313bfe4SRoman Gushchin 		 * Check the cgroup.events::frozen value.
375313bfe4SRoman Gushchin 		 */
385313bfe4SRoman Gushchin 		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
395313bfe4SRoman Gushchin 			debug("Cgroup %s is frozen\n", cgroup);
405313bfe4SRoman Gushchin 			return -1;
415313bfe4SRoman Gushchin 		}
425313bfe4SRoman Gushchin 	}
435313bfe4SRoman Gushchin 
445313bfe4SRoman Gushchin 	return 0;
455313bfe4SRoman Gushchin }
465313bfe4SRoman Gushchin 
475313bfe4SRoman Gushchin /*
485313bfe4SRoman Gushchin  * Freeze the given cgroup.
495313bfe4SRoman Gushchin  */
cg_freeze_nowait(const char * cgroup,bool freeze)505313bfe4SRoman Gushchin static int cg_freeze_nowait(const char *cgroup, bool freeze)
515313bfe4SRoman Gushchin {
525313bfe4SRoman Gushchin 	return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
535313bfe4SRoman Gushchin }
545313bfe4SRoman Gushchin 
555313bfe4SRoman Gushchin /*
565313bfe4SRoman Gushchin  * Attach a task to the given cgroup and wait for a cgroup frozen event.
575313bfe4SRoman Gushchin  * All transient events (e.g. populated) are ignored.
585313bfe4SRoman Gushchin  */
cg_enter_and_wait_for_frozen(const char * cgroup,int pid,bool frozen)595313bfe4SRoman Gushchin static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
605313bfe4SRoman Gushchin 					bool frozen)
615313bfe4SRoman Gushchin {
625313bfe4SRoman Gushchin 	int fd, ret = -1;
635313bfe4SRoman Gushchin 	int attempts;
645313bfe4SRoman Gushchin 
655313bfe4SRoman Gushchin 	fd = cg_prepare_for_wait(cgroup);
665313bfe4SRoman Gushchin 	if (fd < 0)
675313bfe4SRoman Gushchin 		return fd;
685313bfe4SRoman Gushchin 
695313bfe4SRoman Gushchin 	ret = cg_enter(cgroup, pid);
705313bfe4SRoman Gushchin 	if (ret)
715313bfe4SRoman Gushchin 		goto out;
725313bfe4SRoman Gushchin 
735313bfe4SRoman Gushchin 	for (attempts = 0; attempts < 10; attempts++) {
745313bfe4SRoman Gushchin 		ret = cg_wait_for(fd);
755313bfe4SRoman Gushchin 		if (ret)
765313bfe4SRoman Gushchin 			break;
775313bfe4SRoman Gushchin 
785313bfe4SRoman Gushchin 		ret = cg_check_frozen(cgroup, frozen);
795313bfe4SRoman Gushchin 		if (ret)
805313bfe4SRoman Gushchin 			continue;
815313bfe4SRoman Gushchin 	}
825313bfe4SRoman Gushchin 
835313bfe4SRoman Gushchin out:
845313bfe4SRoman Gushchin 	close(fd);
855313bfe4SRoman Gushchin 	return ret;
865313bfe4SRoman Gushchin }
875313bfe4SRoman Gushchin 
885313bfe4SRoman Gushchin /*
895313bfe4SRoman Gushchin  * Freeze the given cgroup and wait for the inotify signal.
905313bfe4SRoman Gushchin  * If there are no events in 10 seconds, treat this as an error.
915313bfe4SRoman Gushchin  * Then check that the cgroup is in the desired state.
925313bfe4SRoman Gushchin  */
cg_freeze_wait(const char * cgroup,bool freeze)935313bfe4SRoman Gushchin static int cg_freeze_wait(const char *cgroup, bool freeze)
945313bfe4SRoman Gushchin {
955313bfe4SRoman Gushchin 	int fd, ret = -1;
965313bfe4SRoman Gushchin 
975313bfe4SRoman Gushchin 	fd = cg_prepare_for_wait(cgroup);
985313bfe4SRoman Gushchin 	if (fd < 0)
995313bfe4SRoman Gushchin 		return fd;
1005313bfe4SRoman Gushchin 
1015313bfe4SRoman Gushchin 	ret = cg_freeze_nowait(cgroup, freeze);
1025313bfe4SRoman Gushchin 	if (ret) {
1035313bfe4SRoman Gushchin 		debug("Error: cg_freeze_nowait() failed\n");
1045313bfe4SRoman Gushchin 		goto out;
1055313bfe4SRoman Gushchin 	}
1065313bfe4SRoman Gushchin 
1075313bfe4SRoman Gushchin 	ret = cg_wait_for(fd);
1085313bfe4SRoman Gushchin 	if (ret)
1095313bfe4SRoman Gushchin 		goto out;
1105313bfe4SRoman Gushchin 
1115313bfe4SRoman Gushchin 	ret = cg_check_frozen(cgroup, freeze);
1125313bfe4SRoman Gushchin out:
1135313bfe4SRoman Gushchin 	close(fd);
1145313bfe4SRoman Gushchin 	return ret;
1155313bfe4SRoman Gushchin }
1165313bfe4SRoman Gushchin 
1175313bfe4SRoman Gushchin /*
1185313bfe4SRoman Gushchin  * A simple process running in a sleep loop until being
1195313bfe4SRoman Gushchin  * re-parented.
1205313bfe4SRoman Gushchin  */
child_fn(const char * cgroup,void * arg)1215313bfe4SRoman Gushchin static int child_fn(const char *cgroup, void *arg)
1225313bfe4SRoman Gushchin {
1235313bfe4SRoman Gushchin 	int ppid = getppid();
1245313bfe4SRoman Gushchin 
1255313bfe4SRoman Gushchin 	while (getppid() == ppid)
1265313bfe4SRoman Gushchin 		usleep(1000);
1275313bfe4SRoman Gushchin 
1285313bfe4SRoman Gushchin 	return getppid() == ppid;
1295313bfe4SRoman Gushchin }
1305313bfe4SRoman Gushchin 
1315313bfe4SRoman Gushchin /*
1325313bfe4SRoman Gushchin  * A simple test for the cgroup freezer: populated the cgroup with 100
1335313bfe4SRoman Gushchin  * running processes and freeze it. Then unfreeze it. Then it kills all
1345313bfe4SRoman Gushchin  * processes and destroys the cgroup.
1355313bfe4SRoman Gushchin  */
test_cgfreezer_simple(const char * root)1365313bfe4SRoman Gushchin static int test_cgfreezer_simple(const char *root)
1375313bfe4SRoman Gushchin {
1385313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
1395313bfe4SRoman Gushchin 	char *cgroup = NULL;
1405313bfe4SRoman Gushchin 	int i;
1415313bfe4SRoman Gushchin 
1425313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_simple");
1435313bfe4SRoman Gushchin 	if (!cgroup)
1445313bfe4SRoman Gushchin 		goto cleanup;
1455313bfe4SRoman Gushchin 
1465313bfe4SRoman Gushchin 	if (cg_create(cgroup))
1475313bfe4SRoman Gushchin 		goto cleanup;
1485313bfe4SRoman Gushchin 
1495313bfe4SRoman Gushchin 	for (i = 0; i < 100; i++)
1505313bfe4SRoman Gushchin 		cg_run_nowait(cgroup, child_fn, NULL);
1515313bfe4SRoman Gushchin 
1525313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 100))
1535313bfe4SRoman Gushchin 		goto cleanup;
1545313bfe4SRoman Gushchin 
1555313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, false))
1565313bfe4SRoman Gushchin 		goto cleanup;
1575313bfe4SRoman Gushchin 
1585313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
1595313bfe4SRoman Gushchin 		goto cleanup;
1605313bfe4SRoman Gushchin 
1615313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, false))
1625313bfe4SRoman Gushchin 		goto cleanup;
1635313bfe4SRoman Gushchin 
1645313bfe4SRoman Gushchin 	ret = KSFT_PASS;
1655313bfe4SRoman Gushchin 
1665313bfe4SRoman Gushchin cleanup:
1675313bfe4SRoman Gushchin 	if (cgroup)
1685313bfe4SRoman Gushchin 		cg_destroy(cgroup);
1695313bfe4SRoman Gushchin 	free(cgroup);
1705313bfe4SRoman Gushchin 	return ret;
1715313bfe4SRoman Gushchin }
1725313bfe4SRoman Gushchin 
1735313bfe4SRoman Gushchin /*
1745313bfe4SRoman Gushchin  * The test creates the following hierarchy:
1755313bfe4SRoman Gushchin  *       A
1765313bfe4SRoman Gushchin  *    / / \ \
1775313bfe4SRoman Gushchin  *   B  E  I K
1785313bfe4SRoman Gushchin  *  /\  |
1795313bfe4SRoman Gushchin  * C  D F
1805313bfe4SRoman Gushchin  *      |
1815313bfe4SRoman Gushchin  *      G
1825313bfe4SRoman Gushchin  *      |
1835313bfe4SRoman Gushchin  *      H
1845313bfe4SRoman Gushchin  *
1855313bfe4SRoman Gushchin  * with a process in C, H and 3 processes in K.
1865313bfe4SRoman Gushchin  * Then it tries to freeze and unfreeze the whole tree.
1875313bfe4SRoman Gushchin  */
test_cgfreezer_tree(const char * root)1885313bfe4SRoman Gushchin static int test_cgfreezer_tree(const char *root)
1895313bfe4SRoman Gushchin {
1905313bfe4SRoman Gushchin 	char *cgroup[10] = {0};
1915313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
1925313bfe4SRoman Gushchin 	int i;
1935313bfe4SRoman Gushchin 
1945313bfe4SRoman Gushchin 	cgroup[0] = cg_name(root, "cg_test_tree_A");
1955313bfe4SRoman Gushchin 	if (!cgroup[0])
1965313bfe4SRoman Gushchin 		goto cleanup;
1975313bfe4SRoman Gushchin 
1985313bfe4SRoman Gushchin 	cgroup[1] = cg_name(cgroup[0], "B");
1995313bfe4SRoman Gushchin 	if (!cgroup[1])
2005313bfe4SRoman Gushchin 		goto cleanup;
2015313bfe4SRoman Gushchin 
2025313bfe4SRoman Gushchin 	cgroup[2] = cg_name(cgroup[1], "C");
2035313bfe4SRoman Gushchin 	if (!cgroup[2])
2045313bfe4SRoman Gushchin 		goto cleanup;
2055313bfe4SRoman Gushchin 
2065313bfe4SRoman Gushchin 	cgroup[3] = cg_name(cgroup[1], "D");
2075313bfe4SRoman Gushchin 	if (!cgroup[3])
2085313bfe4SRoman Gushchin 		goto cleanup;
2095313bfe4SRoman Gushchin 
2105313bfe4SRoman Gushchin 	cgroup[4] = cg_name(cgroup[0], "E");
2115313bfe4SRoman Gushchin 	if (!cgroup[4])
2125313bfe4SRoman Gushchin 		goto cleanup;
2135313bfe4SRoman Gushchin 
2145313bfe4SRoman Gushchin 	cgroup[5] = cg_name(cgroup[4], "F");
2155313bfe4SRoman Gushchin 	if (!cgroup[5])
2165313bfe4SRoman Gushchin 		goto cleanup;
2175313bfe4SRoman Gushchin 
2185313bfe4SRoman Gushchin 	cgroup[6] = cg_name(cgroup[5], "G");
2195313bfe4SRoman Gushchin 	if (!cgroup[6])
2205313bfe4SRoman Gushchin 		goto cleanup;
2215313bfe4SRoman Gushchin 
2225313bfe4SRoman Gushchin 	cgroup[7] = cg_name(cgroup[6], "H");
2235313bfe4SRoman Gushchin 	if (!cgroup[7])
2245313bfe4SRoman Gushchin 		goto cleanup;
2255313bfe4SRoman Gushchin 
2265313bfe4SRoman Gushchin 	cgroup[8] = cg_name(cgroup[0], "I");
2275313bfe4SRoman Gushchin 	if (!cgroup[8])
2285313bfe4SRoman Gushchin 		goto cleanup;
2295313bfe4SRoman Gushchin 
2305313bfe4SRoman Gushchin 	cgroup[9] = cg_name(cgroup[0], "K");
2315313bfe4SRoman Gushchin 	if (!cgroup[9])
2325313bfe4SRoman Gushchin 		goto cleanup;
2335313bfe4SRoman Gushchin 
2345313bfe4SRoman Gushchin 	for (i = 0; i < 10; i++)
2355313bfe4SRoman Gushchin 		if (cg_create(cgroup[i]))
2365313bfe4SRoman Gushchin 			goto cleanup;
2375313bfe4SRoman Gushchin 
2385313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[2], child_fn, NULL);
2395313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[7], child_fn, NULL);
2405313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[9], child_fn, NULL);
2415313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[9], child_fn, NULL);
2425313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[9], child_fn, NULL);
2435313bfe4SRoman Gushchin 
2445313bfe4SRoman Gushchin 	/*
2455313bfe4SRoman Gushchin 	 * Wait until all child processes will enter
2465313bfe4SRoman Gushchin 	 * corresponding cgroups.
2475313bfe4SRoman Gushchin 	 */
2485313bfe4SRoman Gushchin 
2495313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup[2], 1) ||
2505313bfe4SRoman Gushchin 	    cg_wait_for_proc_count(cgroup[7], 1) ||
2515313bfe4SRoman Gushchin 	    cg_wait_for_proc_count(cgroup[9], 3))
2525313bfe4SRoman Gushchin 		goto cleanup;
2535313bfe4SRoman Gushchin 
2545313bfe4SRoman Gushchin 	/*
2555313bfe4SRoman Gushchin 	 * Freeze B.
2565313bfe4SRoman Gushchin 	 */
2575313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[1], true))
2585313bfe4SRoman Gushchin 		goto cleanup;
2595313bfe4SRoman Gushchin 
2605313bfe4SRoman Gushchin 	/*
2615313bfe4SRoman Gushchin 	 * Freeze F.
2625313bfe4SRoman Gushchin 	 */
2635313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[5], true))
2645313bfe4SRoman Gushchin 		goto cleanup;
2655313bfe4SRoman Gushchin 
2665313bfe4SRoman Gushchin 	/*
2675313bfe4SRoman Gushchin 	 * Freeze G.
2685313bfe4SRoman Gushchin 	 */
2695313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[6], true))
2705313bfe4SRoman Gushchin 		goto cleanup;
2715313bfe4SRoman Gushchin 
2725313bfe4SRoman Gushchin 	/*
2735313bfe4SRoman Gushchin 	 * Check that A and E are not frozen.
2745313bfe4SRoman Gushchin 	 */
2755313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[0], false))
2765313bfe4SRoman Gushchin 		goto cleanup;
2775313bfe4SRoman Gushchin 
2785313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[4], false))
2795313bfe4SRoman Gushchin 		goto cleanup;
2805313bfe4SRoman Gushchin 
2815313bfe4SRoman Gushchin 	/*
2825313bfe4SRoman Gushchin 	 * Freeze A. Check that A, B and E are frozen.
2835313bfe4SRoman Gushchin 	 */
2845313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[0], true))
2855313bfe4SRoman Gushchin 		goto cleanup;
2865313bfe4SRoman Gushchin 
2875313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[1], true))
2885313bfe4SRoman Gushchin 		goto cleanup;
2895313bfe4SRoman Gushchin 
2905313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[4], true))
2915313bfe4SRoman Gushchin 		goto cleanup;
2925313bfe4SRoman Gushchin 
2935313bfe4SRoman Gushchin 	/*
2945313bfe4SRoman Gushchin 	 * Unfreeze B, F and G
2955313bfe4SRoman Gushchin 	 */
2965313bfe4SRoman Gushchin 	if (cg_freeze_nowait(cgroup[1], false))
2975313bfe4SRoman Gushchin 		goto cleanup;
2985313bfe4SRoman Gushchin 
2995313bfe4SRoman Gushchin 	if (cg_freeze_nowait(cgroup[5], false))
3005313bfe4SRoman Gushchin 		goto cleanup;
3015313bfe4SRoman Gushchin 
3025313bfe4SRoman Gushchin 	if (cg_freeze_nowait(cgroup[6], false))
3035313bfe4SRoman Gushchin 		goto cleanup;
3045313bfe4SRoman Gushchin 
3055313bfe4SRoman Gushchin 	/*
3065313bfe4SRoman Gushchin 	 * Check that C and H are still frozen.
3075313bfe4SRoman Gushchin 	 */
3085313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[2], true))
3095313bfe4SRoman Gushchin 		goto cleanup;
3105313bfe4SRoman Gushchin 
3115313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[7], true))
3125313bfe4SRoman Gushchin 		goto cleanup;
3135313bfe4SRoman Gushchin 
3145313bfe4SRoman Gushchin 	/*
3155313bfe4SRoman Gushchin 	 * Unfreeze A. Check that A, C and K are not frozen.
3165313bfe4SRoman Gushchin 	 */
3175313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[0], false))
3185313bfe4SRoman Gushchin 		goto cleanup;
3195313bfe4SRoman Gushchin 
3205313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[2], false))
3215313bfe4SRoman Gushchin 		goto cleanup;
3225313bfe4SRoman Gushchin 
3235313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[9], false))
3245313bfe4SRoman Gushchin 		goto cleanup;
3255313bfe4SRoman Gushchin 
3265313bfe4SRoman Gushchin 	ret = KSFT_PASS;
3275313bfe4SRoman Gushchin 
3285313bfe4SRoman Gushchin cleanup:
3295313bfe4SRoman Gushchin 	for (i = 9; i >= 0 && cgroup[i]; i--) {
3305313bfe4SRoman Gushchin 		cg_destroy(cgroup[i]);
3315313bfe4SRoman Gushchin 		free(cgroup[i]);
3325313bfe4SRoman Gushchin 	}
3335313bfe4SRoman Gushchin 
3345313bfe4SRoman Gushchin 	return ret;
3355313bfe4SRoman Gushchin }
3365313bfe4SRoman Gushchin 
3375313bfe4SRoman Gushchin /*
3385313bfe4SRoman Gushchin  * A fork bomb emulator.
3395313bfe4SRoman Gushchin  */
forkbomb_fn(const char * cgroup,void * arg)3405313bfe4SRoman Gushchin static int forkbomb_fn(const char *cgroup, void *arg)
3415313bfe4SRoman Gushchin {
3425313bfe4SRoman Gushchin 	int ppid;
3435313bfe4SRoman Gushchin 
3445313bfe4SRoman Gushchin 	fork();
3455313bfe4SRoman Gushchin 	fork();
3465313bfe4SRoman Gushchin 
3475313bfe4SRoman Gushchin 	ppid = getppid();
3485313bfe4SRoman Gushchin 
3495313bfe4SRoman Gushchin 	while (getppid() == ppid)
3505313bfe4SRoman Gushchin 		usleep(1000);
3515313bfe4SRoman Gushchin 
3525313bfe4SRoman Gushchin 	return getppid() == ppid;
3535313bfe4SRoman Gushchin }
3545313bfe4SRoman Gushchin 
3555313bfe4SRoman Gushchin /*
3565313bfe4SRoman Gushchin  * The test runs a fork bomb in a cgroup and tries to freeze it.
3575313bfe4SRoman Gushchin  * Then it kills all processes and checks that cgroup isn't populated
3585313bfe4SRoman Gushchin  * anymore.
3595313bfe4SRoman Gushchin  */
test_cgfreezer_forkbomb(const char * root)3605313bfe4SRoman Gushchin static int test_cgfreezer_forkbomb(const char *root)
3615313bfe4SRoman Gushchin {
3625313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
3635313bfe4SRoman Gushchin 	char *cgroup = NULL;
3645313bfe4SRoman Gushchin 
3655313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_forkbomb_test");
3665313bfe4SRoman Gushchin 	if (!cgroup)
3675313bfe4SRoman Gushchin 		goto cleanup;
3685313bfe4SRoman Gushchin 
3695313bfe4SRoman Gushchin 	if (cg_create(cgroup))
3705313bfe4SRoman Gushchin 		goto cleanup;
3715313bfe4SRoman Gushchin 
3725313bfe4SRoman Gushchin 	cg_run_nowait(cgroup, forkbomb_fn, NULL);
3735313bfe4SRoman Gushchin 
3745313bfe4SRoman Gushchin 	usleep(100000);
3755313bfe4SRoman Gushchin 
3765313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
3775313bfe4SRoman Gushchin 		goto cleanup;
3785313bfe4SRoman Gushchin 
3795313bfe4SRoman Gushchin 	if (cg_killall(cgroup))
3805313bfe4SRoman Gushchin 		goto cleanup;
3815313bfe4SRoman Gushchin 
3825313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 0))
3835313bfe4SRoman Gushchin 		goto cleanup;
3845313bfe4SRoman Gushchin 
3855313bfe4SRoman Gushchin 	ret = KSFT_PASS;
3865313bfe4SRoman Gushchin 
3875313bfe4SRoman Gushchin cleanup:
3885313bfe4SRoman Gushchin 	if (cgroup)
3895313bfe4SRoman Gushchin 		cg_destroy(cgroup);
3905313bfe4SRoman Gushchin 	free(cgroup);
3915313bfe4SRoman Gushchin 	return ret;
3925313bfe4SRoman Gushchin }
3935313bfe4SRoman Gushchin 
3945313bfe4SRoman Gushchin /*
39544e9d308SRoman Gushchin  * The test creates a cgroups and freezes it. Then it creates a child cgroup
39644e9d308SRoman Gushchin  * and populates it with a task. After that it checks that the child cgroup
39744e9d308SRoman Gushchin  * is frozen and the parent cgroup remains frozen too.
39844e9d308SRoman Gushchin  */
test_cgfreezer_mkdir(const char * root)39944e9d308SRoman Gushchin static int test_cgfreezer_mkdir(const char *root)
40044e9d308SRoman Gushchin {
40144e9d308SRoman Gushchin 	int ret = KSFT_FAIL;
40244e9d308SRoman Gushchin 	char *parent, *child = NULL;
40344e9d308SRoman Gushchin 	int pid;
40444e9d308SRoman Gushchin 
40544e9d308SRoman Gushchin 	parent = cg_name(root, "cg_test_mkdir_A");
40644e9d308SRoman Gushchin 	if (!parent)
40744e9d308SRoman Gushchin 		goto cleanup;
40844e9d308SRoman Gushchin 
40944e9d308SRoman Gushchin 	child = cg_name(parent, "cg_test_mkdir_B");
41044e9d308SRoman Gushchin 	if (!child)
41144e9d308SRoman Gushchin 		goto cleanup;
41244e9d308SRoman Gushchin 
41344e9d308SRoman Gushchin 	if (cg_create(parent))
41444e9d308SRoman Gushchin 		goto cleanup;
41544e9d308SRoman Gushchin 
41644e9d308SRoman Gushchin 	if (cg_freeze_wait(parent, true))
41744e9d308SRoman Gushchin 		goto cleanup;
41844e9d308SRoman Gushchin 
41944e9d308SRoman Gushchin 	if (cg_create(child))
42044e9d308SRoman Gushchin 		goto cleanup;
42144e9d308SRoman Gushchin 
42244e9d308SRoman Gushchin 	pid = cg_run_nowait(child, child_fn, NULL);
42344e9d308SRoman Gushchin 	if (pid < 0)
42444e9d308SRoman Gushchin 		goto cleanup;
42544e9d308SRoman Gushchin 
42644e9d308SRoman Gushchin 	if (cg_wait_for_proc_count(child, 1))
42744e9d308SRoman Gushchin 		goto cleanup;
42844e9d308SRoman Gushchin 
42944e9d308SRoman Gushchin 	if (cg_check_frozen(child, true))
43044e9d308SRoman Gushchin 		goto cleanup;
43144e9d308SRoman Gushchin 
43244e9d308SRoman Gushchin 	if (cg_check_frozen(parent, true))
43344e9d308SRoman Gushchin 		goto cleanup;
43444e9d308SRoman Gushchin 
43544e9d308SRoman Gushchin 	ret = KSFT_PASS;
43644e9d308SRoman Gushchin 
43744e9d308SRoman Gushchin cleanup:
43844e9d308SRoman Gushchin 	if (child)
43944e9d308SRoman Gushchin 		cg_destroy(child);
44044e9d308SRoman Gushchin 	free(child);
44144e9d308SRoman Gushchin 	if (parent)
44244e9d308SRoman Gushchin 		cg_destroy(parent);
44344e9d308SRoman Gushchin 	free(parent);
44444e9d308SRoman Gushchin 	return ret;
44544e9d308SRoman Gushchin }
44644e9d308SRoman Gushchin 
44744e9d308SRoman Gushchin /*
4485313bfe4SRoman Gushchin  * The test creates two nested cgroups, freezes the parent
4495313bfe4SRoman Gushchin  * and removes the child. Then it checks that the parent cgroup
4505313bfe4SRoman Gushchin  * remains frozen and it's possible to create a new child
4515313bfe4SRoman Gushchin  * without unfreezing. The new child is frozen too.
4525313bfe4SRoman Gushchin  */
test_cgfreezer_rmdir(const char * root)4535313bfe4SRoman Gushchin static int test_cgfreezer_rmdir(const char *root)
4545313bfe4SRoman Gushchin {
4555313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
4565313bfe4SRoman Gushchin 	char *parent, *child = NULL;
4575313bfe4SRoman Gushchin 
4585313bfe4SRoman Gushchin 	parent = cg_name(root, "cg_test_rmdir_A");
4595313bfe4SRoman Gushchin 	if (!parent)
4605313bfe4SRoman Gushchin 		goto cleanup;
4615313bfe4SRoman Gushchin 
4625313bfe4SRoman Gushchin 	child = cg_name(parent, "cg_test_rmdir_B");
4635313bfe4SRoman Gushchin 	if (!child)
4645313bfe4SRoman Gushchin 		goto cleanup;
4655313bfe4SRoman Gushchin 
4665313bfe4SRoman Gushchin 	if (cg_create(parent))
4675313bfe4SRoman Gushchin 		goto cleanup;
4685313bfe4SRoman Gushchin 
4695313bfe4SRoman Gushchin 	if (cg_create(child))
4705313bfe4SRoman Gushchin 		goto cleanup;
4715313bfe4SRoman Gushchin 
4725313bfe4SRoman Gushchin 	if (cg_freeze_wait(parent, true))
4735313bfe4SRoman Gushchin 		goto cleanup;
4745313bfe4SRoman Gushchin 
4755313bfe4SRoman Gushchin 	if (cg_destroy(child))
4765313bfe4SRoman Gushchin 		goto cleanup;
4775313bfe4SRoman Gushchin 
4785313bfe4SRoman Gushchin 	if (cg_check_frozen(parent, true))
4795313bfe4SRoman Gushchin 		goto cleanup;
4805313bfe4SRoman Gushchin 
4815313bfe4SRoman Gushchin 	if (cg_create(child))
4825313bfe4SRoman Gushchin 		goto cleanup;
4835313bfe4SRoman Gushchin 
4845313bfe4SRoman Gushchin 	if (cg_check_frozen(child, true))
4855313bfe4SRoman Gushchin 		goto cleanup;
4865313bfe4SRoman Gushchin 
4875313bfe4SRoman Gushchin 	ret = KSFT_PASS;
4885313bfe4SRoman Gushchin 
4895313bfe4SRoman Gushchin cleanup:
4905313bfe4SRoman Gushchin 	if (child)
4915313bfe4SRoman Gushchin 		cg_destroy(child);
4925313bfe4SRoman Gushchin 	free(child);
4935313bfe4SRoman Gushchin 	if (parent)
4945313bfe4SRoman Gushchin 		cg_destroy(parent);
4955313bfe4SRoman Gushchin 	free(parent);
4965313bfe4SRoman Gushchin 	return ret;
4975313bfe4SRoman Gushchin }
4985313bfe4SRoman Gushchin 
4995313bfe4SRoman Gushchin /*
5005313bfe4SRoman Gushchin  * The test creates two cgroups: A and B, runs a process in A
5015313bfe4SRoman Gushchin  * and performs several migrations:
5025313bfe4SRoman Gushchin  * 1) A (running) -> B (frozen)
5035313bfe4SRoman Gushchin  * 2) B (frozen) -> A (running)
5045313bfe4SRoman Gushchin  * 3) A (frozen) -> B (frozen)
5055313bfe4SRoman Gushchin  *
5065313bfe4SRoman Gushchin  * On each step it checks the actual state of both cgroups.
5075313bfe4SRoman Gushchin  */
test_cgfreezer_migrate(const char * root)5085313bfe4SRoman Gushchin static int test_cgfreezer_migrate(const char *root)
5095313bfe4SRoman Gushchin {
5105313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
5115313bfe4SRoman Gushchin 	char *cgroup[2] = {0};
5125313bfe4SRoman Gushchin 	int pid;
5135313bfe4SRoman Gushchin 
5145313bfe4SRoman Gushchin 	cgroup[0] = cg_name(root, "cg_test_migrate_A");
5155313bfe4SRoman Gushchin 	if (!cgroup[0])
5165313bfe4SRoman Gushchin 		goto cleanup;
5175313bfe4SRoman Gushchin 
5185313bfe4SRoman Gushchin 	cgroup[1] = cg_name(root, "cg_test_migrate_B");
5195313bfe4SRoman Gushchin 	if (!cgroup[1])
5205313bfe4SRoman Gushchin 		goto cleanup;
5215313bfe4SRoman Gushchin 
5225313bfe4SRoman Gushchin 	if (cg_create(cgroup[0]))
5235313bfe4SRoman Gushchin 		goto cleanup;
5245313bfe4SRoman Gushchin 
5255313bfe4SRoman Gushchin 	if (cg_create(cgroup[1]))
5265313bfe4SRoman Gushchin 		goto cleanup;
5275313bfe4SRoman Gushchin 
5285313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup[0], child_fn, NULL);
5295313bfe4SRoman Gushchin 	if (pid < 0)
5305313bfe4SRoman Gushchin 		goto cleanup;
5315313bfe4SRoman Gushchin 
5325313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup[0], 1))
5335313bfe4SRoman Gushchin 		goto cleanup;
5345313bfe4SRoman Gushchin 
5355313bfe4SRoman Gushchin 	/*
5365313bfe4SRoman Gushchin 	 * Migrate from A (running) to B (frozen)
5375313bfe4SRoman Gushchin 	 */
5385313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[1], true))
5395313bfe4SRoman Gushchin 		goto cleanup;
5405313bfe4SRoman Gushchin 
5415313bfe4SRoman Gushchin 	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
5425313bfe4SRoman Gushchin 		goto cleanup;
5435313bfe4SRoman Gushchin 
5445313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[0], false))
5455313bfe4SRoman Gushchin 		goto cleanup;
5465313bfe4SRoman Gushchin 
5475313bfe4SRoman Gushchin 	/*
5485313bfe4SRoman Gushchin 	 * Migrate from B (frozen) to A (running)
5495313bfe4SRoman Gushchin 	 */
5505313bfe4SRoman Gushchin 	if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
5515313bfe4SRoman Gushchin 		goto cleanup;
5525313bfe4SRoman Gushchin 
5535313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[1], true))
5545313bfe4SRoman Gushchin 		goto cleanup;
5555313bfe4SRoman Gushchin 
5565313bfe4SRoman Gushchin 	/*
5575313bfe4SRoman Gushchin 	 * Migrate from A (frozen) to B (frozen)
5585313bfe4SRoman Gushchin 	 */
5595313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[0], true))
5605313bfe4SRoman Gushchin 		goto cleanup;
5615313bfe4SRoman Gushchin 
5625313bfe4SRoman Gushchin 	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
5635313bfe4SRoman Gushchin 		goto cleanup;
5645313bfe4SRoman Gushchin 
5655313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[0], true))
5665313bfe4SRoman Gushchin 		goto cleanup;
5675313bfe4SRoman Gushchin 
5685313bfe4SRoman Gushchin 	ret = KSFT_PASS;
5695313bfe4SRoman Gushchin 
5705313bfe4SRoman Gushchin cleanup:
5715313bfe4SRoman Gushchin 	if (cgroup[0])
5725313bfe4SRoman Gushchin 		cg_destroy(cgroup[0]);
5735313bfe4SRoman Gushchin 	free(cgroup[0]);
5745313bfe4SRoman Gushchin 	if (cgroup[1])
5755313bfe4SRoman Gushchin 		cg_destroy(cgroup[1]);
5765313bfe4SRoman Gushchin 	free(cgroup[1]);
5775313bfe4SRoman Gushchin 	return ret;
5785313bfe4SRoman Gushchin }
5795313bfe4SRoman Gushchin 
5805313bfe4SRoman Gushchin /*
5815313bfe4SRoman Gushchin  * The test checks that ptrace works with a tracing process in a frozen cgroup.
5825313bfe4SRoman Gushchin  */
test_cgfreezer_ptrace(const char * root)5835313bfe4SRoman Gushchin static int test_cgfreezer_ptrace(const char *root)
5845313bfe4SRoman Gushchin {
5855313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
5865313bfe4SRoman Gushchin 	char *cgroup = NULL;
5875313bfe4SRoman Gushchin 	siginfo_t siginfo;
5885313bfe4SRoman Gushchin 	int pid;
5895313bfe4SRoman Gushchin 
5905313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_ptrace");
5915313bfe4SRoman Gushchin 	if (!cgroup)
5925313bfe4SRoman Gushchin 		goto cleanup;
5935313bfe4SRoman Gushchin 
5945313bfe4SRoman Gushchin 	if (cg_create(cgroup))
5955313bfe4SRoman Gushchin 		goto cleanup;
5965313bfe4SRoman Gushchin 
5975313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup, child_fn, NULL);
5985313bfe4SRoman Gushchin 	if (pid < 0)
5995313bfe4SRoman Gushchin 		goto cleanup;
6005313bfe4SRoman Gushchin 
6015313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 1))
6025313bfe4SRoman Gushchin 		goto cleanup;
6035313bfe4SRoman Gushchin 
6045313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
6055313bfe4SRoman Gushchin 		goto cleanup;
6065313bfe4SRoman Gushchin 
6075313bfe4SRoman Gushchin 	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
6085313bfe4SRoman Gushchin 		goto cleanup;
6095313bfe4SRoman Gushchin 
6105313bfe4SRoman Gushchin 	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
6115313bfe4SRoman Gushchin 		goto cleanup;
6125313bfe4SRoman Gushchin 
6135313bfe4SRoman Gushchin 	waitpid(pid, NULL, 0);
6145313bfe4SRoman Gushchin 
6155313bfe4SRoman Gushchin 	/*
6165313bfe4SRoman Gushchin 	 * Cgroup has to remain frozen, however the test task
6175313bfe4SRoman Gushchin 	 * is in traced state.
6185313bfe4SRoman Gushchin 	 */
6195313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, true))
6205313bfe4SRoman Gushchin 		goto cleanup;
6215313bfe4SRoman Gushchin 
6225313bfe4SRoman Gushchin 	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
6235313bfe4SRoman Gushchin 		goto cleanup;
6245313bfe4SRoman Gushchin 
6255313bfe4SRoman Gushchin 	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
6265313bfe4SRoman Gushchin 		goto cleanup;
6275313bfe4SRoman Gushchin 
6285313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, true))
6295313bfe4SRoman Gushchin 		goto cleanup;
6305313bfe4SRoman Gushchin 
6315313bfe4SRoman Gushchin 	ret = KSFT_PASS;
6325313bfe4SRoman Gushchin 
6335313bfe4SRoman Gushchin cleanup:
6345313bfe4SRoman Gushchin 	if (cgroup)
6355313bfe4SRoman Gushchin 		cg_destroy(cgroup);
6365313bfe4SRoman Gushchin 	free(cgroup);
6375313bfe4SRoman Gushchin 	return ret;
6385313bfe4SRoman Gushchin }
6395313bfe4SRoman Gushchin 
6405313bfe4SRoman Gushchin /*
6415313bfe4SRoman Gushchin  * Check if the process is stopped.
6425313bfe4SRoman Gushchin  */
proc_check_stopped(int pid)6435313bfe4SRoman Gushchin static int proc_check_stopped(int pid)
6445313bfe4SRoman Gushchin {
6455313bfe4SRoman Gushchin 	char buf[PAGE_SIZE];
6465313bfe4SRoman Gushchin 	int len;
6475313bfe4SRoman Gushchin 
64858c9f75bSMichal Koutný 	len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
6495313bfe4SRoman Gushchin 	if (len == -1) {
6505313bfe4SRoman Gushchin 		debug("Can't get %d stat\n", pid);
6515313bfe4SRoman Gushchin 		return -1;
6525313bfe4SRoman Gushchin 	}
6535313bfe4SRoman Gushchin 
6545313bfe4SRoman Gushchin 	if (strstr(buf, "(test_freezer) T ") == NULL) {
6555313bfe4SRoman Gushchin 		debug("Process %d in the unexpected state: %s\n", pid, buf);
6565313bfe4SRoman Gushchin 		return -1;
6575313bfe4SRoman Gushchin 	}
6585313bfe4SRoman Gushchin 
6595313bfe4SRoman Gushchin 	return 0;
6605313bfe4SRoman Gushchin }
6615313bfe4SRoman Gushchin 
6625313bfe4SRoman Gushchin /*
6635313bfe4SRoman Gushchin  * Test that it's possible to freeze a cgroup with a stopped process.
6645313bfe4SRoman Gushchin  */
test_cgfreezer_stopped(const char * root)6655313bfe4SRoman Gushchin static int test_cgfreezer_stopped(const char *root)
6665313bfe4SRoman Gushchin {
6675313bfe4SRoman Gushchin 	int pid, ret = KSFT_FAIL;
6685313bfe4SRoman Gushchin 	char *cgroup = NULL;
6695313bfe4SRoman Gushchin 
6705313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_stopped");
6715313bfe4SRoman Gushchin 	if (!cgroup)
6725313bfe4SRoman Gushchin 		goto cleanup;
6735313bfe4SRoman Gushchin 
6745313bfe4SRoman Gushchin 	if (cg_create(cgroup))
6755313bfe4SRoman Gushchin 		goto cleanup;
6765313bfe4SRoman Gushchin 
6775313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup, child_fn, NULL);
6785313bfe4SRoman Gushchin 
6795313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 1))
6805313bfe4SRoman Gushchin 		goto cleanup;
6815313bfe4SRoman Gushchin 
6825313bfe4SRoman Gushchin 	if (kill(pid, SIGSTOP))
6835313bfe4SRoman Gushchin 		goto cleanup;
6845313bfe4SRoman Gushchin 
6855313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, false))
6865313bfe4SRoman Gushchin 		goto cleanup;
6875313bfe4SRoman Gushchin 
6885313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
6895313bfe4SRoman Gushchin 		goto cleanup;
6905313bfe4SRoman Gushchin 
6915313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, false))
6925313bfe4SRoman Gushchin 		goto cleanup;
6935313bfe4SRoman Gushchin 
6945313bfe4SRoman Gushchin 	if (proc_check_stopped(pid))
6955313bfe4SRoman Gushchin 		goto cleanup;
6965313bfe4SRoman Gushchin 
6975313bfe4SRoman Gushchin 	ret = KSFT_PASS;
6985313bfe4SRoman Gushchin 
6995313bfe4SRoman Gushchin cleanup:
7005313bfe4SRoman Gushchin 	if (cgroup)
7015313bfe4SRoman Gushchin 		cg_destroy(cgroup);
7025313bfe4SRoman Gushchin 	free(cgroup);
7035313bfe4SRoman Gushchin 	return ret;
7045313bfe4SRoman Gushchin }
7055313bfe4SRoman Gushchin 
7065313bfe4SRoman Gushchin /*
7075313bfe4SRoman Gushchin  * Test that it's possible to freeze a cgroup with a ptraced process.
7085313bfe4SRoman Gushchin  */
test_cgfreezer_ptraced(const char * root)7095313bfe4SRoman Gushchin static int test_cgfreezer_ptraced(const char *root)
7105313bfe4SRoman Gushchin {
7115313bfe4SRoman Gushchin 	int pid, ret = KSFT_FAIL;
7125313bfe4SRoman Gushchin 	char *cgroup = NULL;
7135313bfe4SRoman Gushchin 	siginfo_t siginfo;
7145313bfe4SRoman Gushchin 
7155313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_ptraced");
7165313bfe4SRoman Gushchin 	if (!cgroup)
7175313bfe4SRoman Gushchin 		goto cleanup;
7185313bfe4SRoman Gushchin 
7195313bfe4SRoman Gushchin 	if (cg_create(cgroup))
7205313bfe4SRoman Gushchin 		goto cleanup;
7215313bfe4SRoman Gushchin 
7225313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup, child_fn, NULL);
7235313bfe4SRoman Gushchin 
7245313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 1))
7255313bfe4SRoman Gushchin 		goto cleanup;
7265313bfe4SRoman Gushchin 
7275313bfe4SRoman Gushchin 	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
7285313bfe4SRoman Gushchin 		goto cleanup;
7295313bfe4SRoman Gushchin 
7305313bfe4SRoman Gushchin 	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
7315313bfe4SRoman Gushchin 		goto cleanup;
7325313bfe4SRoman Gushchin 
7335313bfe4SRoman Gushchin 	waitpid(pid, NULL, 0);
7345313bfe4SRoman Gushchin 
7355313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, false))
7365313bfe4SRoman Gushchin 		goto cleanup;
7375313bfe4SRoman Gushchin 
7385313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
7395313bfe4SRoman Gushchin 		goto cleanup;
7405313bfe4SRoman Gushchin 
7415313bfe4SRoman Gushchin 	/*
7425313bfe4SRoman Gushchin 	 * cg_check_frozen(cgroup, true) will fail here,
7435313bfe4SRoman Gushchin 	 * because the task in in the TRACEd state.
7445313bfe4SRoman Gushchin 	 */
7455313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, false))
7465313bfe4SRoman Gushchin 		goto cleanup;
7475313bfe4SRoman Gushchin 
7485313bfe4SRoman Gushchin 	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
7495313bfe4SRoman Gushchin 		goto cleanup;
7505313bfe4SRoman Gushchin 
7515313bfe4SRoman Gushchin 	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
7525313bfe4SRoman Gushchin 		goto cleanup;
7535313bfe4SRoman Gushchin 
7545313bfe4SRoman Gushchin 	ret = KSFT_PASS;
7555313bfe4SRoman Gushchin 
7565313bfe4SRoman Gushchin cleanup:
7575313bfe4SRoman Gushchin 	if (cgroup)
7585313bfe4SRoman Gushchin 		cg_destroy(cgroup);
7595313bfe4SRoman Gushchin 	free(cgroup);
7605313bfe4SRoman Gushchin 	return ret;
7615313bfe4SRoman Gushchin }
7625313bfe4SRoman Gushchin 
vfork_fn(const char * cgroup,void * arg)7635313bfe4SRoman Gushchin static int vfork_fn(const char *cgroup, void *arg)
7645313bfe4SRoman Gushchin {
7655313bfe4SRoman Gushchin 	int pid = vfork();
7665313bfe4SRoman Gushchin 
7675313bfe4SRoman Gushchin 	if (pid == 0)
7685313bfe4SRoman Gushchin 		while (true)
7695313bfe4SRoman Gushchin 			sleep(1);
7705313bfe4SRoman Gushchin 
7715313bfe4SRoman Gushchin 	return pid;
7725313bfe4SRoman Gushchin }
7735313bfe4SRoman Gushchin 
7745313bfe4SRoman Gushchin /*
7755313bfe4SRoman Gushchin  * Test that it's possible to freeze a cgroup with a process,
7765313bfe4SRoman Gushchin  * which called vfork() and is waiting for a child.
7775313bfe4SRoman Gushchin  */
test_cgfreezer_vfork(const char * root)7785313bfe4SRoman Gushchin static int test_cgfreezer_vfork(const char *root)
7795313bfe4SRoman Gushchin {
7805313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
7815313bfe4SRoman Gushchin 	char *cgroup = NULL;
7825313bfe4SRoman Gushchin 
7835313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_vfork");
7845313bfe4SRoman Gushchin 	if (!cgroup)
7855313bfe4SRoman Gushchin 		goto cleanup;
7865313bfe4SRoman Gushchin 
7875313bfe4SRoman Gushchin 	if (cg_create(cgroup))
7885313bfe4SRoman Gushchin 		goto cleanup;
7895313bfe4SRoman Gushchin 
7905313bfe4SRoman Gushchin 	cg_run_nowait(cgroup, vfork_fn, NULL);
7915313bfe4SRoman Gushchin 
7925313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 2))
7935313bfe4SRoman Gushchin 		goto cleanup;
7945313bfe4SRoman Gushchin 
7955313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
7965313bfe4SRoman Gushchin 		goto cleanup;
7975313bfe4SRoman Gushchin 
7985313bfe4SRoman Gushchin 	ret = KSFT_PASS;
7995313bfe4SRoman Gushchin 
8005313bfe4SRoman Gushchin cleanup:
8015313bfe4SRoman Gushchin 	if (cgroup)
8025313bfe4SRoman Gushchin 		cg_destroy(cgroup);
8035313bfe4SRoman Gushchin 	free(cgroup);
8045313bfe4SRoman Gushchin 	return ret;
8055313bfe4SRoman Gushchin }
8065313bfe4SRoman Gushchin 
8075313bfe4SRoman Gushchin #define T(x) { x, #x }
8085313bfe4SRoman Gushchin struct cgfreezer_test {
8095313bfe4SRoman Gushchin 	int (*fn)(const char *root);
8105313bfe4SRoman Gushchin 	const char *name;
8115313bfe4SRoman Gushchin } tests[] = {
8125313bfe4SRoman Gushchin 	T(test_cgfreezer_simple),
8135313bfe4SRoman Gushchin 	T(test_cgfreezer_tree),
8145313bfe4SRoman Gushchin 	T(test_cgfreezer_forkbomb),
81544e9d308SRoman Gushchin 	T(test_cgfreezer_mkdir),
8165313bfe4SRoman Gushchin 	T(test_cgfreezer_rmdir),
8175313bfe4SRoman Gushchin 	T(test_cgfreezer_migrate),
8185313bfe4SRoman Gushchin 	T(test_cgfreezer_ptrace),
8195313bfe4SRoman Gushchin 	T(test_cgfreezer_stopped),
8205313bfe4SRoman Gushchin 	T(test_cgfreezer_ptraced),
8215313bfe4SRoman Gushchin 	T(test_cgfreezer_vfork),
8225313bfe4SRoman Gushchin };
8235313bfe4SRoman Gushchin #undef T
8245313bfe4SRoman Gushchin 
main(int argc,char * argv[])8255313bfe4SRoman Gushchin int main(int argc, char *argv[])
8265313bfe4SRoman Gushchin {
8275313bfe4SRoman Gushchin 	char root[PATH_MAX];
8285313bfe4SRoman Gushchin 	int i, ret = EXIT_SUCCESS;
8295313bfe4SRoman Gushchin 
8305313bfe4SRoman Gushchin 	if (cg_find_unified_root(root, sizeof(root)))
8315313bfe4SRoman Gushchin 		ksft_exit_skip("cgroup v2 isn't mounted\n");
8325313bfe4SRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
8335313bfe4SRoman Gushchin 		switch (tests[i].fn(root)) {
8345313bfe4SRoman Gushchin 		case KSFT_PASS:
8355313bfe4SRoman Gushchin 			ksft_test_result_pass("%s\n", tests[i].name);
8365313bfe4SRoman Gushchin 			break;
8375313bfe4SRoman Gushchin 		case KSFT_SKIP:
8385313bfe4SRoman Gushchin 			ksft_test_result_skip("%s\n", tests[i].name);
8395313bfe4SRoman Gushchin 			break;
8405313bfe4SRoman Gushchin 		default:
8415313bfe4SRoman Gushchin 			ret = EXIT_FAILURE;
8425313bfe4SRoman Gushchin 			ksft_test_result_fail("%s\n", tests[i].name);
8435313bfe4SRoman Gushchin 			break;
8445313bfe4SRoman Gushchin 		}
8455313bfe4SRoman Gushchin 	}
8465313bfe4SRoman Gushchin 
8475313bfe4SRoman Gushchin 	return ret;
8485313bfe4SRoman Gushchin }
849