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 <poll.h>
115313bfe4SRoman Gushchin #include <stdlib.h>
125313bfe4SRoman Gushchin #include <sys/inotify.h>
135313bfe4SRoman Gushchin #include <string.h>
145313bfe4SRoman Gushchin #include <sys/wait.h>
155313bfe4SRoman Gushchin 
165313bfe4SRoman Gushchin #include "../kselftest.h"
175313bfe4SRoman Gushchin #include "cgroup_util.h"
185313bfe4SRoman Gushchin 
195313bfe4SRoman Gushchin #define DEBUG
205313bfe4SRoman Gushchin #ifdef DEBUG
215313bfe4SRoman Gushchin #define debug(args...) fprintf(stderr, args)
225313bfe4SRoman Gushchin #else
235313bfe4SRoman Gushchin #define debug(args...)
245313bfe4SRoman Gushchin #endif
255313bfe4SRoman Gushchin 
265313bfe4SRoman Gushchin /*
275313bfe4SRoman Gushchin  * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
285313bfe4SRoman Gushchin  */
295313bfe4SRoman Gushchin static int cg_check_frozen(const char *cgroup, bool frozen)
305313bfe4SRoman Gushchin {
315313bfe4SRoman Gushchin 	if (frozen) {
325313bfe4SRoman Gushchin 		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
335313bfe4SRoman Gushchin 			debug("Cgroup %s isn't frozen\n", cgroup);
345313bfe4SRoman Gushchin 			return -1;
355313bfe4SRoman Gushchin 		}
365313bfe4SRoman Gushchin 	} else {
375313bfe4SRoman Gushchin 		/*
385313bfe4SRoman Gushchin 		 * Check the cgroup.events::frozen value.
395313bfe4SRoman Gushchin 		 */
405313bfe4SRoman Gushchin 		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
415313bfe4SRoman Gushchin 			debug("Cgroup %s is frozen\n", cgroup);
425313bfe4SRoman Gushchin 			return -1;
435313bfe4SRoman Gushchin 		}
445313bfe4SRoman Gushchin 	}
455313bfe4SRoman Gushchin 
465313bfe4SRoman Gushchin 	return 0;
475313bfe4SRoman Gushchin }
485313bfe4SRoman Gushchin 
495313bfe4SRoman Gushchin /*
505313bfe4SRoman Gushchin  * Freeze the given cgroup.
515313bfe4SRoman Gushchin  */
525313bfe4SRoman Gushchin static int cg_freeze_nowait(const char *cgroup, bool freeze)
535313bfe4SRoman Gushchin {
545313bfe4SRoman Gushchin 	return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
555313bfe4SRoman Gushchin }
565313bfe4SRoman Gushchin 
575313bfe4SRoman Gushchin /*
585313bfe4SRoman Gushchin  * Prepare for waiting on cgroup.events file.
595313bfe4SRoman Gushchin  */
605313bfe4SRoman Gushchin static int cg_prepare_for_wait(const char *cgroup)
615313bfe4SRoman Gushchin {
625313bfe4SRoman Gushchin 	int fd, ret = -1;
635313bfe4SRoman Gushchin 
645313bfe4SRoman Gushchin 	fd = inotify_init1(0);
655313bfe4SRoman Gushchin 	if (fd == -1) {
665313bfe4SRoman Gushchin 		debug("Error: inotify_init1() failed\n");
675313bfe4SRoman Gushchin 		return fd;
685313bfe4SRoman Gushchin 	}
695313bfe4SRoman Gushchin 
705313bfe4SRoman Gushchin 	ret = inotify_add_watch(fd, cg_control(cgroup, "cgroup.events"),
715313bfe4SRoman Gushchin 				IN_MODIFY);
725313bfe4SRoman Gushchin 	if (ret == -1) {
735313bfe4SRoman Gushchin 		debug("Error: inotify_add_watch() failed\n");
745313bfe4SRoman Gushchin 		close(fd);
755313bfe4SRoman Gushchin 	}
765313bfe4SRoman Gushchin 
775313bfe4SRoman Gushchin 	return fd;
785313bfe4SRoman Gushchin }
795313bfe4SRoman Gushchin 
805313bfe4SRoman Gushchin /*
815313bfe4SRoman Gushchin  * Wait for an event. If there are no events for 10 seconds,
825313bfe4SRoman Gushchin  * treat this an error.
835313bfe4SRoman Gushchin  */
845313bfe4SRoman Gushchin static int cg_wait_for(int fd)
855313bfe4SRoman Gushchin {
865313bfe4SRoman Gushchin 	int ret = -1;
875313bfe4SRoman Gushchin 	struct pollfd fds = {
885313bfe4SRoman Gushchin 		.fd = fd,
895313bfe4SRoman Gushchin 		.events = POLLIN,
905313bfe4SRoman Gushchin 	};
915313bfe4SRoman Gushchin 
925313bfe4SRoman Gushchin 	while (true) {
935313bfe4SRoman Gushchin 		ret = poll(&fds, 1, 10000);
945313bfe4SRoman Gushchin 
955313bfe4SRoman Gushchin 		if (ret == -1) {
965313bfe4SRoman Gushchin 			if (errno == EINTR)
975313bfe4SRoman Gushchin 				continue;
985313bfe4SRoman Gushchin 			debug("Error: poll() failed\n");
995313bfe4SRoman Gushchin 			break;
1005313bfe4SRoman Gushchin 		}
1015313bfe4SRoman Gushchin 
1025313bfe4SRoman Gushchin 		if (ret > 0 && fds.revents & POLLIN) {
1035313bfe4SRoman Gushchin 			ret = 0;
1045313bfe4SRoman Gushchin 			break;
1055313bfe4SRoman Gushchin 		}
1065313bfe4SRoman Gushchin 	}
1075313bfe4SRoman Gushchin 
1085313bfe4SRoman Gushchin 	return ret;
1095313bfe4SRoman Gushchin }
1105313bfe4SRoman Gushchin 
1115313bfe4SRoman Gushchin /*
1125313bfe4SRoman Gushchin  * Attach a task to the given cgroup and wait for a cgroup frozen event.
1135313bfe4SRoman Gushchin  * All transient events (e.g. populated) are ignored.
1145313bfe4SRoman Gushchin  */
1155313bfe4SRoman Gushchin static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
1165313bfe4SRoman Gushchin 					bool frozen)
1175313bfe4SRoman Gushchin {
1185313bfe4SRoman Gushchin 	int fd, ret = -1;
1195313bfe4SRoman Gushchin 	int attempts;
1205313bfe4SRoman Gushchin 
1215313bfe4SRoman Gushchin 	fd = cg_prepare_for_wait(cgroup);
1225313bfe4SRoman Gushchin 	if (fd < 0)
1235313bfe4SRoman Gushchin 		return fd;
1245313bfe4SRoman Gushchin 
1255313bfe4SRoman Gushchin 	ret = cg_enter(cgroup, pid);
1265313bfe4SRoman Gushchin 	if (ret)
1275313bfe4SRoman Gushchin 		goto out;
1285313bfe4SRoman Gushchin 
1295313bfe4SRoman Gushchin 	for (attempts = 0; attempts < 10; attempts++) {
1305313bfe4SRoman Gushchin 		ret = cg_wait_for(fd);
1315313bfe4SRoman Gushchin 		if (ret)
1325313bfe4SRoman Gushchin 			break;
1335313bfe4SRoman Gushchin 
1345313bfe4SRoman Gushchin 		ret = cg_check_frozen(cgroup, frozen);
1355313bfe4SRoman Gushchin 		if (ret)
1365313bfe4SRoman Gushchin 			continue;
1375313bfe4SRoman Gushchin 	}
1385313bfe4SRoman Gushchin 
1395313bfe4SRoman Gushchin out:
1405313bfe4SRoman Gushchin 	close(fd);
1415313bfe4SRoman Gushchin 	return ret;
1425313bfe4SRoman Gushchin }
1435313bfe4SRoman Gushchin 
1445313bfe4SRoman Gushchin /*
1455313bfe4SRoman Gushchin  * Freeze the given cgroup and wait for the inotify signal.
1465313bfe4SRoman Gushchin  * If there are no events in 10 seconds, treat this as an error.
1475313bfe4SRoman Gushchin  * Then check that the cgroup is in the desired state.
1485313bfe4SRoman Gushchin  */
1495313bfe4SRoman Gushchin static int cg_freeze_wait(const char *cgroup, bool freeze)
1505313bfe4SRoman Gushchin {
1515313bfe4SRoman Gushchin 	int fd, ret = -1;
1525313bfe4SRoman Gushchin 
1535313bfe4SRoman Gushchin 	fd = cg_prepare_for_wait(cgroup);
1545313bfe4SRoman Gushchin 	if (fd < 0)
1555313bfe4SRoman Gushchin 		return fd;
1565313bfe4SRoman Gushchin 
1575313bfe4SRoman Gushchin 	ret = cg_freeze_nowait(cgroup, freeze);
1585313bfe4SRoman Gushchin 	if (ret) {
1595313bfe4SRoman Gushchin 		debug("Error: cg_freeze_nowait() failed\n");
1605313bfe4SRoman Gushchin 		goto out;
1615313bfe4SRoman Gushchin 	}
1625313bfe4SRoman Gushchin 
1635313bfe4SRoman Gushchin 	ret = cg_wait_for(fd);
1645313bfe4SRoman Gushchin 	if (ret)
1655313bfe4SRoman Gushchin 		goto out;
1665313bfe4SRoman Gushchin 
1675313bfe4SRoman Gushchin 	ret = cg_check_frozen(cgroup, freeze);
1685313bfe4SRoman Gushchin out:
1695313bfe4SRoman Gushchin 	close(fd);
1705313bfe4SRoman Gushchin 	return ret;
1715313bfe4SRoman Gushchin }
1725313bfe4SRoman Gushchin 
1735313bfe4SRoman Gushchin /*
1745313bfe4SRoman Gushchin  * A simple process running in a sleep loop until being
1755313bfe4SRoman Gushchin  * re-parented.
1765313bfe4SRoman Gushchin  */
1775313bfe4SRoman Gushchin static int child_fn(const char *cgroup, void *arg)
1785313bfe4SRoman Gushchin {
1795313bfe4SRoman Gushchin 	int ppid = getppid();
1805313bfe4SRoman Gushchin 
1815313bfe4SRoman Gushchin 	while (getppid() == ppid)
1825313bfe4SRoman Gushchin 		usleep(1000);
1835313bfe4SRoman Gushchin 
1845313bfe4SRoman Gushchin 	return getppid() == ppid;
1855313bfe4SRoman Gushchin }
1865313bfe4SRoman Gushchin 
1875313bfe4SRoman Gushchin /*
1885313bfe4SRoman Gushchin  * A simple test for the cgroup freezer: populated the cgroup with 100
1895313bfe4SRoman Gushchin  * running processes and freeze it. Then unfreeze it. Then it kills all
1905313bfe4SRoman Gushchin  * processes and destroys the cgroup.
1915313bfe4SRoman Gushchin  */
1925313bfe4SRoman Gushchin static int test_cgfreezer_simple(const char *root)
1935313bfe4SRoman Gushchin {
1945313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
1955313bfe4SRoman Gushchin 	char *cgroup = NULL;
1965313bfe4SRoman Gushchin 	int i;
1975313bfe4SRoman Gushchin 
1985313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_simple");
1995313bfe4SRoman Gushchin 	if (!cgroup)
2005313bfe4SRoman Gushchin 		goto cleanup;
2015313bfe4SRoman Gushchin 
2025313bfe4SRoman Gushchin 	if (cg_create(cgroup))
2035313bfe4SRoman Gushchin 		goto cleanup;
2045313bfe4SRoman Gushchin 
2055313bfe4SRoman Gushchin 	for (i = 0; i < 100; i++)
2065313bfe4SRoman Gushchin 		cg_run_nowait(cgroup, child_fn, NULL);
2075313bfe4SRoman Gushchin 
2085313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 100))
2095313bfe4SRoman Gushchin 		goto cleanup;
2105313bfe4SRoman Gushchin 
2115313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, false))
2125313bfe4SRoman Gushchin 		goto cleanup;
2135313bfe4SRoman Gushchin 
2145313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
2155313bfe4SRoman Gushchin 		goto cleanup;
2165313bfe4SRoman Gushchin 
2175313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, false))
2185313bfe4SRoman Gushchin 		goto cleanup;
2195313bfe4SRoman Gushchin 
2205313bfe4SRoman Gushchin 	ret = KSFT_PASS;
2215313bfe4SRoman Gushchin 
2225313bfe4SRoman Gushchin cleanup:
2235313bfe4SRoman Gushchin 	if (cgroup)
2245313bfe4SRoman Gushchin 		cg_destroy(cgroup);
2255313bfe4SRoman Gushchin 	free(cgroup);
2265313bfe4SRoman Gushchin 	return ret;
2275313bfe4SRoman Gushchin }
2285313bfe4SRoman Gushchin 
2295313bfe4SRoman Gushchin /*
2305313bfe4SRoman Gushchin  * The test creates the following hierarchy:
2315313bfe4SRoman Gushchin  *       A
2325313bfe4SRoman Gushchin  *    / / \ \
2335313bfe4SRoman Gushchin  *   B  E  I K
2345313bfe4SRoman Gushchin  *  /\  |
2355313bfe4SRoman Gushchin  * C  D F
2365313bfe4SRoman Gushchin  *      |
2375313bfe4SRoman Gushchin  *      G
2385313bfe4SRoman Gushchin  *      |
2395313bfe4SRoman Gushchin  *      H
2405313bfe4SRoman Gushchin  *
2415313bfe4SRoman Gushchin  * with a process in C, H and 3 processes in K.
2425313bfe4SRoman Gushchin  * Then it tries to freeze and unfreeze the whole tree.
2435313bfe4SRoman Gushchin  */
2445313bfe4SRoman Gushchin static int test_cgfreezer_tree(const char *root)
2455313bfe4SRoman Gushchin {
2465313bfe4SRoman Gushchin 	char *cgroup[10] = {0};
2475313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
2485313bfe4SRoman Gushchin 	int i;
2495313bfe4SRoman Gushchin 
2505313bfe4SRoman Gushchin 	cgroup[0] = cg_name(root, "cg_test_tree_A");
2515313bfe4SRoman Gushchin 	if (!cgroup[0])
2525313bfe4SRoman Gushchin 		goto cleanup;
2535313bfe4SRoman Gushchin 
2545313bfe4SRoman Gushchin 	cgroup[1] = cg_name(cgroup[0], "B");
2555313bfe4SRoman Gushchin 	if (!cgroup[1])
2565313bfe4SRoman Gushchin 		goto cleanup;
2575313bfe4SRoman Gushchin 
2585313bfe4SRoman Gushchin 	cgroup[2] = cg_name(cgroup[1], "C");
2595313bfe4SRoman Gushchin 	if (!cgroup[2])
2605313bfe4SRoman Gushchin 		goto cleanup;
2615313bfe4SRoman Gushchin 
2625313bfe4SRoman Gushchin 	cgroup[3] = cg_name(cgroup[1], "D");
2635313bfe4SRoman Gushchin 	if (!cgroup[3])
2645313bfe4SRoman Gushchin 		goto cleanup;
2655313bfe4SRoman Gushchin 
2665313bfe4SRoman Gushchin 	cgroup[4] = cg_name(cgroup[0], "E");
2675313bfe4SRoman Gushchin 	if (!cgroup[4])
2685313bfe4SRoman Gushchin 		goto cleanup;
2695313bfe4SRoman Gushchin 
2705313bfe4SRoman Gushchin 	cgroup[5] = cg_name(cgroup[4], "F");
2715313bfe4SRoman Gushchin 	if (!cgroup[5])
2725313bfe4SRoman Gushchin 		goto cleanup;
2735313bfe4SRoman Gushchin 
2745313bfe4SRoman Gushchin 	cgroup[6] = cg_name(cgroup[5], "G");
2755313bfe4SRoman Gushchin 	if (!cgroup[6])
2765313bfe4SRoman Gushchin 		goto cleanup;
2775313bfe4SRoman Gushchin 
2785313bfe4SRoman Gushchin 	cgroup[7] = cg_name(cgroup[6], "H");
2795313bfe4SRoman Gushchin 	if (!cgroup[7])
2805313bfe4SRoman Gushchin 		goto cleanup;
2815313bfe4SRoman Gushchin 
2825313bfe4SRoman Gushchin 	cgroup[8] = cg_name(cgroup[0], "I");
2835313bfe4SRoman Gushchin 	if (!cgroup[8])
2845313bfe4SRoman Gushchin 		goto cleanup;
2855313bfe4SRoman Gushchin 
2865313bfe4SRoman Gushchin 	cgroup[9] = cg_name(cgroup[0], "K");
2875313bfe4SRoman Gushchin 	if (!cgroup[9])
2885313bfe4SRoman Gushchin 		goto cleanup;
2895313bfe4SRoman Gushchin 
2905313bfe4SRoman Gushchin 	for (i = 0; i < 10; i++)
2915313bfe4SRoman Gushchin 		if (cg_create(cgroup[i]))
2925313bfe4SRoman Gushchin 			goto cleanup;
2935313bfe4SRoman Gushchin 
2945313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[2], child_fn, NULL);
2955313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[7], child_fn, NULL);
2965313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[9], child_fn, NULL);
2975313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[9], child_fn, NULL);
2985313bfe4SRoman Gushchin 	cg_run_nowait(cgroup[9], child_fn, NULL);
2995313bfe4SRoman Gushchin 
3005313bfe4SRoman Gushchin 	/*
3015313bfe4SRoman Gushchin 	 * Wait until all child processes will enter
3025313bfe4SRoman Gushchin 	 * corresponding cgroups.
3035313bfe4SRoman Gushchin 	 */
3045313bfe4SRoman Gushchin 
3055313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup[2], 1) ||
3065313bfe4SRoman Gushchin 	    cg_wait_for_proc_count(cgroup[7], 1) ||
3075313bfe4SRoman Gushchin 	    cg_wait_for_proc_count(cgroup[9], 3))
3085313bfe4SRoman Gushchin 		goto cleanup;
3095313bfe4SRoman Gushchin 
3105313bfe4SRoman Gushchin 	/*
3115313bfe4SRoman Gushchin 	 * Freeze B.
3125313bfe4SRoman Gushchin 	 */
3135313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[1], true))
3145313bfe4SRoman Gushchin 		goto cleanup;
3155313bfe4SRoman Gushchin 
3165313bfe4SRoman Gushchin 	/*
3175313bfe4SRoman Gushchin 	 * Freeze F.
3185313bfe4SRoman Gushchin 	 */
3195313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[5], true))
3205313bfe4SRoman Gushchin 		goto cleanup;
3215313bfe4SRoman Gushchin 
3225313bfe4SRoman Gushchin 	/*
3235313bfe4SRoman Gushchin 	 * Freeze G.
3245313bfe4SRoman Gushchin 	 */
3255313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[6], true))
3265313bfe4SRoman Gushchin 		goto cleanup;
3275313bfe4SRoman Gushchin 
3285313bfe4SRoman Gushchin 	/*
3295313bfe4SRoman Gushchin 	 * Check that A and E are not frozen.
3305313bfe4SRoman Gushchin 	 */
3315313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[0], false))
3325313bfe4SRoman Gushchin 		goto cleanup;
3335313bfe4SRoman Gushchin 
3345313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[4], false))
3355313bfe4SRoman Gushchin 		goto cleanup;
3365313bfe4SRoman Gushchin 
3375313bfe4SRoman Gushchin 	/*
3385313bfe4SRoman Gushchin 	 * Freeze A. Check that A, B and E are frozen.
3395313bfe4SRoman Gushchin 	 */
3405313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[0], true))
3415313bfe4SRoman Gushchin 		goto cleanup;
3425313bfe4SRoman Gushchin 
3435313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[1], true))
3445313bfe4SRoman Gushchin 		goto cleanup;
3455313bfe4SRoman Gushchin 
3465313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[4], true))
3475313bfe4SRoman Gushchin 		goto cleanup;
3485313bfe4SRoman Gushchin 
3495313bfe4SRoman Gushchin 	/*
3505313bfe4SRoman Gushchin 	 * Unfreeze B, F and G
3515313bfe4SRoman Gushchin 	 */
3525313bfe4SRoman Gushchin 	if (cg_freeze_nowait(cgroup[1], false))
3535313bfe4SRoman Gushchin 		goto cleanup;
3545313bfe4SRoman Gushchin 
3555313bfe4SRoman Gushchin 	if (cg_freeze_nowait(cgroup[5], false))
3565313bfe4SRoman Gushchin 		goto cleanup;
3575313bfe4SRoman Gushchin 
3585313bfe4SRoman Gushchin 	if (cg_freeze_nowait(cgroup[6], false))
3595313bfe4SRoman Gushchin 		goto cleanup;
3605313bfe4SRoman Gushchin 
3615313bfe4SRoman Gushchin 	/*
3625313bfe4SRoman Gushchin 	 * Check that C and H are still frozen.
3635313bfe4SRoman Gushchin 	 */
3645313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[2], true))
3655313bfe4SRoman Gushchin 		goto cleanup;
3665313bfe4SRoman Gushchin 
3675313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[7], true))
3685313bfe4SRoman Gushchin 		goto cleanup;
3695313bfe4SRoman Gushchin 
3705313bfe4SRoman Gushchin 	/*
3715313bfe4SRoman Gushchin 	 * Unfreeze A. Check that A, C and K are not frozen.
3725313bfe4SRoman Gushchin 	 */
3735313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[0], false))
3745313bfe4SRoman Gushchin 		goto cleanup;
3755313bfe4SRoman Gushchin 
3765313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[2], false))
3775313bfe4SRoman Gushchin 		goto cleanup;
3785313bfe4SRoman Gushchin 
3795313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[9], false))
3805313bfe4SRoman Gushchin 		goto cleanup;
3815313bfe4SRoman Gushchin 
3825313bfe4SRoman Gushchin 	ret = KSFT_PASS;
3835313bfe4SRoman Gushchin 
3845313bfe4SRoman Gushchin cleanup:
3855313bfe4SRoman Gushchin 	for (i = 9; i >= 0 && cgroup[i]; i--) {
3865313bfe4SRoman Gushchin 		cg_destroy(cgroup[i]);
3875313bfe4SRoman Gushchin 		free(cgroup[i]);
3885313bfe4SRoman Gushchin 	}
3895313bfe4SRoman Gushchin 
3905313bfe4SRoman Gushchin 	return ret;
3915313bfe4SRoman Gushchin }
3925313bfe4SRoman Gushchin 
3935313bfe4SRoman Gushchin /*
3945313bfe4SRoman Gushchin  * A fork bomb emulator.
3955313bfe4SRoman Gushchin  */
3965313bfe4SRoman Gushchin static int forkbomb_fn(const char *cgroup, void *arg)
3975313bfe4SRoman Gushchin {
3985313bfe4SRoman Gushchin 	int ppid;
3995313bfe4SRoman Gushchin 
4005313bfe4SRoman Gushchin 	fork();
4015313bfe4SRoman Gushchin 	fork();
4025313bfe4SRoman Gushchin 
4035313bfe4SRoman Gushchin 	ppid = getppid();
4045313bfe4SRoman Gushchin 
4055313bfe4SRoman Gushchin 	while (getppid() == ppid)
4065313bfe4SRoman Gushchin 		usleep(1000);
4075313bfe4SRoman Gushchin 
4085313bfe4SRoman Gushchin 	return getppid() == ppid;
4095313bfe4SRoman Gushchin }
4105313bfe4SRoman Gushchin 
4115313bfe4SRoman Gushchin /*
4125313bfe4SRoman Gushchin  * The test runs a fork bomb in a cgroup and tries to freeze it.
4135313bfe4SRoman Gushchin  * Then it kills all processes and checks that cgroup isn't populated
4145313bfe4SRoman Gushchin  * anymore.
4155313bfe4SRoman Gushchin  */
4165313bfe4SRoman Gushchin static int test_cgfreezer_forkbomb(const char *root)
4175313bfe4SRoman Gushchin {
4185313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
4195313bfe4SRoman Gushchin 	char *cgroup = NULL;
4205313bfe4SRoman Gushchin 
4215313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_forkbomb_test");
4225313bfe4SRoman Gushchin 	if (!cgroup)
4235313bfe4SRoman Gushchin 		goto cleanup;
4245313bfe4SRoman Gushchin 
4255313bfe4SRoman Gushchin 	if (cg_create(cgroup))
4265313bfe4SRoman Gushchin 		goto cleanup;
4275313bfe4SRoman Gushchin 
4285313bfe4SRoman Gushchin 	cg_run_nowait(cgroup, forkbomb_fn, NULL);
4295313bfe4SRoman Gushchin 
4305313bfe4SRoman Gushchin 	usleep(100000);
4315313bfe4SRoman Gushchin 
4325313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
4335313bfe4SRoman Gushchin 		goto cleanup;
4345313bfe4SRoman Gushchin 
4355313bfe4SRoman Gushchin 	if (cg_killall(cgroup))
4365313bfe4SRoman Gushchin 		goto cleanup;
4375313bfe4SRoman Gushchin 
4385313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 0))
4395313bfe4SRoman Gushchin 		goto cleanup;
4405313bfe4SRoman Gushchin 
4415313bfe4SRoman Gushchin 	ret = KSFT_PASS;
4425313bfe4SRoman Gushchin 
4435313bfe4SRoman Gushchin cleanup:
4445313bfe4SRoman Gushchin 	if (cgroup)
4455313bfe4SRoman Gushchin 		cg_destroy(cgroup);
4465313bfe4SRoman Gushchin 	free(cgroup);
4475313bfe4SRoman Gushchin 	return ret;
4485313bfe4SRoman Gushchin }
4495313bfe4SRoman Gushchin 
4505313bfe4SRoman Gushchin /*
45144e9d308SRoman Gushchin  * The test creates a cgroups and freezes it. Then it creates a child cgroup
45244e9d308SRoman Gushchin  * and populates it with a task. After that it checks that the child cgroup
45344e9d308SRoman Gushchin  * is frozen and the parent cgroup remains frozen too.
45444e9d308SRoman Gushchin  */
45544e9d308SRoman Gushchin static int test_cgfreezer_mkdir(const char *root)
45644e9d308SRoman Gushchin {
45744e9d308SRoman Gushchin 	int ret = KSFT_FAIL;
45844e9d308SRoman Gushchin 	char *parent, *child = NULL;
45944e9d308SRoman Gushchin 	int pid;
46044e9d308SRoman Gushchin 
46144e9d308SRoman Gushchin 	parent = cg_name(root, "cg_test_mkdir_A");
46244e9d308SRoman Gushchin 	if (!parent)
46344e9d308SRoman Gushchin 		goto cleanup;
46444e9d308SRoman Gushchin 
46544e9d308SRoman Gushchin 	child = cg_name(parent, "cg_test_mkdir_B");
46644e9d308SRoman Gushchin 	if (!child)
46744e9d308SRoman Gushchin 		goto cleanup;
46844e9d308SRoman Gushchin 
46944e9d308SRoman Gushchin 	if (cg_create(parent))
47044e9d308SRoman Gushchin 		goto cleanup;
47144e9d308SRoman Gushchin 
47244e9d308SRoman Gushchin 	if (cg_freeze_wait(parent, true))
47344e9d308SRoman Gushchin 		goto cleanup;
47444e9d308SRoman Gushchin 
47544e9d308SRoman Gushchin 	if (cg_create(child))
47644e9d308SRoman Gushchin 		goto cleanup;
47744e9d308SRoman Gushchin 
47844e9d308SRoman Gushchin 	pid = cg_run_nowait(child, child_fn, NULL);
47944e9d308SRoman Gushchin 	if (pid < 0)
48044e9d308SRoman Gushchin 		goto cleanup;
48144e9d308SRoman Gushchin 
48244e9d308SRoman Gushchin 	if (cg_wait_for_proc_count(child, 1))
48344e9d308SRoman Gushchin 		goto cleanup;
48444e9d308SRoman Gushchin 
48544e9d308SRoman Gushchin 	if (cg_check_frozen(child, true))
48644e9d308SRoman Gushchin 		goto cleanup;
48744e9d308SRoman Gushchin 
48844e9d308SRoman Gushchin 	if (cg_check_frozen(parent, true))
48944e9d308SRoman Gushchin 		goto cleanup;
49044e9d308SRoman Gushchin 
49144e9d308SRoman Gushchin 	ret = KSFT_PASS;
49244e9d308SRoman Gushchin 
49344e9d308SRoman Gushchin cleanup:
49444e9d308SRoman Gushchin 	if (child)
49544e9d308SRoman Gushchin 		cg_destroy(child);
49644e9d308SRoman Gushchin 	free(child);
49744e9d308SRoman Gushchin 	if (parent)
49844e9d308SRoman Gushchin 		cg_destroy(parent);
49944e9d308SRoman Gushchin 	free(parent);
50044e9d308SRoman Gushchin 	return ret;
50144e9d308SRoman Gushchin }
50244e9d308SRoman Gushchin 
50344e9d308SRoman Gushchin /*
5045313bfe4SRoman Gushchin  * The test creates two nested cgroups, freezes the parent
5055313bfe4SRoman Gushchin  * and removes the child. Then it checks that the parent cgroup
5065313bfe4SRoman Gushchin  * remains frozen and it's possible to create a new child
5075313bfe4SRoman Gushchin  * without unfreezing. The new child is frozen too.
5085313bfe4SRoman Gushchin  */
5095313bfe4SRoman Gushchin static int test_cgfreezer_rmdir(const char *root)
5105313bfe4SRoman Gushchin {
5115313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
5125313bfe4SRoman Gushchin 	char *parent, *child = NULL;
5135313bfe4SRoman Gushchin 
5145313bfe4SRoman Gushchin 	parent = cg_name(root, "cg_test_rmdir_A");
5155313bfe4SRoman Gushchin 	if (!parent)
5165313bfe4SRoman Gushchin 		goto cleanup;
5175313bfe4SRoman Gushchin 
5185313bfe4SRoman Gushchin 	child = cg_name(parent, "cg_test_rmdir_B");
5195313bfe4SRoman Gushchin 	if (!child)
5205313bfe4SRoman Gushchin 		goto cleanup;
5215313bfe4SRoman Gushchin 
5225313bfe4SRoman Gushchin 	if (cg_create(parent))
5235313bfe4SRoman Gushchin 		goto cleanup;
5245313bfe4SRoman Gushchin 
5255313bfe4SRoman Gushchin 	if (cg_create(child))
5265313bfe4SRoman Gushchin 		goto cleanup;
5275313bfe4SRoman Gushchin 
5285313bfe4SRoman Gushchin 	if (cg_freeze_wait(parent, true))
5295313bfe4SRoman Gushchin 		goto cleanup;
5305313bfe4SRoman Gushchin 
5315313bfe4SRoman Gushchin 	if (cg_destroy(child))
5325313bfe4SRoman Gushchin 		goto cleanup;
5335313bfe4SRoman Gushchin 
5345313bfe4SRoman Gushchin 	if (cg_check_frozen(parent, true))
5355313bfe4SRoman Gushchin 		goto cleanup;
5365313bfe4SRoman Gushchin 
5375313bfe4SRoman Gushchin 	if (cg_create(child))
5385313bfe4SRoman Gushchin 		goto cleanup;
5395313bfe4SRoman Gushchin 
5405313bfe4SRoman Gushchin 	if (cg_check_frozen(child, true))
5415313bfe4SRoman Gushchin 		goto cleanup;
5425313bfe4SRoman Gushchin 
5435313bfe4SRoman Gushchin 	ret = KSFT_PASS;
5445313bfe4SRoman Gushchin 
5455313bfe4SRoman Gushchin cleanup:
5465313bfe4SRoman Gushchin 	if (child)
5475313bfe4SRoman Gushchin 		cg_destroy(child);
5485313bfe4SRoman Gushchin 	free(child);
5495313bfe4SRoman Gushchin 	if (parent)
5505313bfe4SRoman Gushchin 		cg_destroy(parent);
5515313bfe4SRoman Gushchin 	free(parent);
5525313bfe4SRoman Gushchin 	return ret;
5535313bfe4SRoman Gushchin }
5545313bfe4SRoman Gushchin 
5555313bfe4SRoman Gushchin /*
5565313bfe4SRoman Gushchin  * The test creates two cgroups: A and B, runs a process in A
5575313bfe4SRoman Gushchin  * and performs several migrations:
5585313bfe4SRoman Gushchin  * 1) A (running) -> B (frozen)
5595313bfe4SRoman Gushchin  * 2) B (frozen) -> A (running)
5605313bfe4SRoman Gushchin  * 3) A (frozen) -> B (frozen)
5615313bfe4SRoman Gushchin  *
5625313bfe4SRoman Gushchin  * On each step it checks the actual state of both cgroups.
5635313bfe4SRoman Gushchin  */
5645313bfe4SRoman Gushchin static int test_cgfreezer_migrate(const char *root)
5655313bfe4SRoman Gushchin {
5665313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
5675313bfe4SRoman Gushchin 	char *cgroup[2] = {0};
5685313bfe4SRoman Gushchin 	int pid;
5695313bfe4SRoman Gushchin 
5705313bfe4SRoman Gushchin 	cgroup[0] = cg_name(root, "cg_test_migrate_A");
5715313bfe4SRoman Gushchin 	if (!cgroup[0])
5725313bfe4SRoman Gushchin 		goto cleanup;
5735313bfe4SRoman Gushchin 
5745313bfe4SRoman Gushchin 	cgroup[1] = cg_name(root, "cg_test_migrate_B");
5755313bfe4SRoman Gushchin 	if (!cgroup[1])
5765313bfe4SRoman Gushchin 		goto cleanup;
5775313bfe4SRoman Gushchin 
5785313bfe4SRoman Gushchin 	if (cg_create(cgroup[0]))
5795313bfe4SRoman Gushchin 		goto cleanup;
5805313bfe4SRoman Gushchin 
5815313bfe4SRoman Gushchin 	if (cg_create(cgroup[1]))
5825313bfe4SRoman Gushchin 		goto cleanup;
5835313bfe4SRoman Gushchin 
5845313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup[0], child_fn, NULL);
5855313bfe4SRoman Gushchin 	if (pid < 0)
5865313bfe4SRoman Gushchin 		goto cleanup;
5875313bfe4SRoman Gushchin 
5885313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup[0], 1))
5895313bfe4SRoman Gushchin 		goto cleanup;
5905313bfe4SRoman Gushchin 
5915313bfe4SRoman Gushchin 	/*
5925313bfe4SRoman Gushchin 	 * Migrate from A (running) to B (frozen)
5935313bfe4SRoman Gushchin 	 */
5945313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[1], true))
5955313bfe4SRoman Gushchin 		goto cleanup;
5965313bfe4SRoman Gushchin 
5975313bfe4SRoman Gushchin 	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
5985313bfe4SRoman Gushchin 		goto cleanup;
5995313bfe4SRoman Gushchin 
6005313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[0], false))
6015313bfe4SRoman Gushchin 		goto cleanup;
6025313bfe4SRoman Gushchin 
6035313bfe4SRoman Gushchin 	/*
6045313bfe4SRoman Gushchin 	 * Migrate from B (frozen) to A (running)
6055313bfe4SRoman Gushchin 	 */
6065313bfe4SRoman Gushchin 	if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
6075313bfe4SRoman Gushchin 		goto cleanup;
6085313bfe4SRoman Gushchin 
6095313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[1], true))
6105313bfe4SRoman Gushchin 		goto cleanup;
6115313bfe4SRoman Gushchin 
6125313bfe4SRoman Gushchin 	/*
6135313bfe4SRoman Gushchin 	 * Migrate from A (frozen) to B (frozen)
6145313bfe4SRoman Gushchin 	 */
6155313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup[0], true))
6165313bfe4SRoman Gushchin 		goto cleanup;
6175313bfe4SRoman Gushchin 
6185313bfe4SRoman Gushchin 	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
6195313bfe4SRoman Gushchin 		goto cleanup;
6205313bfe4SRoman Gushchin 
6215313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup[0], true))
6225313bfe4SRoman Gushchin 		goto cleanup;
6235313bfe4SRoman Gushchin 
6245313bfe4SRoman Gushchin 	ret = KSFT_PASS;
6255313bfe4SRoman Gushchin 
6265313bfe4SRoman Gushchin cleanup:
6275313bfe4SRoman Gushchin 	if (cgroup[0])
6285313bfe4SRoman Gushchin 		cg_destroy(cgroup[0]);
6295313bfe4SRoman Gushchin 	free(cgroup[0]);
6305313bfe4SRoman Gushchin 	if (cgroup[1])
6315313bfe4SRoman Gushchin 		cg_destroy(cgroup[1]);
6325313bfe4SRoman Gushchin 	free(cgroup[1]);
6335313bfe4SRoman Gushchin 	return ret;
6345313bfe4SRoman Gushchin }
6355313bfe4SRoman Gushchin 
6365313bfe4SRoman Gushchin /*
6375313bfe4SRoman Gushchin  * The test checks that ptrace works with a tracing process in a frozen cgroup.
6385313bfe4SRoman Gushchin  */
6395313bfe4SRoman Gushchin static int test_cgfreezer_ptrace(const char *root)
6405313bfe4SRoman Gushchin {
6415313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
6425313bfe4SRoman Gushchin 	char *cgroup = NULL;
6435313bfe4SRoman Gushchin 	siginfo_t siginfo;
6445313bfe4SRoman Gushchin 	int pid;
6455313bfe4SRoman Gushchin 
6465313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_ptrace");
6475313bfe4SRoman Gushchin 	if (!cgroup)
6485313bfe4SRoman Gushchin 		goto cleanup;
6495313bfe4SRoman Gushchin 
6505313bfe4SRoman Gushchin 	if (cg_create(cgroup))
6515313bfe4SRoman Gushchin 		goto cleanup;
6525313bfe4SRoman Gushchin 
6535313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup, child_fn, NULL);
6545313bfe4SRoman Gushchin 	if (pid < 0)
6555313bfe4SRoman Gushchin 		goto cleanup;
6565313bfe4SRoman Gushchin 
6575313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 1))
6585313bfe4SRoman Gushchin 		goto cleanup;
6595313bfe4SRoman Gushchin 
6605313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
6615313bfe4SRoman Gushchin 		goto cleanup;
6625313bfe4SRoman Gushchin 
6635313bfe4SRoman Gushchin 	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
6645313bfe4SRoman Gushchin 		goto cleanup;
6655313bfe4SRoman Gushchin 
6665313bfe4SRoman Gushchin 	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
6675313bfe4SRoman Gushchin 		goto cleanup;
6685313bfe4SRoman Gushchin 
6695313bfe4SRoman Gushchin 	waitpid(pid, NULL, 0);
6705313bfe4SRoman Gushchin 
6715313bfe4SRoman Gushchin 	/*
6725313bfe4SRoman Gushchin 	 * Cgroup has to remain frozen, however the test task
6735313bfe4SRoman Gushchin 	 * is in traced state.
6745313bfe4SRoman Gushchin 	 */
6755313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, true))
6765313bfe4SRoman Gushchin 		goto cleanup;
6775313bfe4SRoman Gushchin 
6785313bfe4SRoman Gushchin 	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
6795313bfe4SRoman Gushchin 		goto cleanup;
6805313bfe4SRoman Gushchin 
6815313bfe4SRoman Gushchin 	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
6825313bfe4SRoman Gushchin 		goto cleanup;
6835313bfe4SRoman Gushchin 
6845313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, true))
6855313bfe4SRoman Gushchin 		goto cleanup;
6865313bfe4SRoman Gushchin 
6875313bfe4SRoman Gushchin 	ret = KSFT_PASS;
6885313bfe4SRoman Gushchin 
6895313bfe4SRoman Gushchin cleanup:
6905313bfe4SRoman Gushchin 	if (cgroup)
6915313bfe4SRoman Gushchin 		cg_destroy(cgroup);
6925313bfe4SRoman Gushchin 	free(cgroup);
6935313bfe4SRoman Gushchin 	return ret;
6945313bfe4SRoman Gushchin }
6955313bfe4SRoman Gushchin 
6965313bfe4SRoman Gushchin /*
6975313bfe4SRoman Gushchin  * Check if the process is stopped.
6985313bfe4SRoman Gushchin  */
6995313bfe4SRoman Gushchin static int proc_check_stopped(int pid)
7005313bfe4SRoman Gushchin {
7015313bfe4SRoman Gushchin 	char buf[PAGE_SIZE];
7025313bfe4SRoman Gushchin 	int len;
7035313bfe4SRoman Gushchin 
70458c9f75bSMichal Koutný 	len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
7055313bfe4SRoman Gushchin 	if (len == -1) {
7065313bfe4SRoman Gushchin 		debug("Can't get %d stat\n", pid);
7075313bfe4SRoman Gushchin 		return -1;
7085313bfe4SRoman Gushchin 	}
7095313bfe4SRoman Gushchin 
7105313bfe4SRoman Gushchin 	if (strstr(buf, "(test_freezer) T ") == NULL) {
7115313bfe4SRoman Gushchin 		debug("Process %d in the unexpected state: %s\n", pid, buf);
7125313bfe4SRoman Gushchin 		return -1;
7135313bfe4SRoman Gushchin 	}
7145313bfe4SRoman Gushchin 
7155313bfe4SRoman Gushchin 	return 0;
7165313bfe4SRoman Gushchin }
7175313bfe4SRoman Gushchin 
7185313bfe4SRoman Gushchin /*
7195313bfe4SRoman Gushchin  * Test that it's possible to freeze a cgroup with a stopped process.
7205313bfe4SRoman Gushchin  */
7215313bfe4SRoman Gushchin static int test_cgfreezer_stopped(const char *root)
7225313bfe4SRoman Gushchin {
7235313bfe4SRoman Gushchin 	int pid, ret = KSFT_FAIL;
7245313bfe4SRoman Gushchin 	char *cgroup = NULL;
7255313bfe4SRoman Gushchin 
7265313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_stopped");
7275313bfe4SRoman Gushchin 	if (!cgroup)
7285313bfe4SRoman Gushchin 		goto cleanup;
7295313bfe4SRoman Gushchin 
7305313bfe4SRoman Gushchin 	if (cg_create(cgroup))
7315313bfe4SRoman Gushchin 		goto cleanup;
7325313bfe4SRoman Gushchin 
7335313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup, child_fn, NULL);
7345313bfe4SRoman Gushchin 
7355313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 1))
7365313bfe4SRoman Gushchin 		goto cleanup;
7375313bfe4SRoman Gushchin 
7385313bfe4SRoman Gushchin 	if (kill(pid, SIGSTOP))
7395313bfe4SRoman Gushchin 		goto cleanup;
7405313bfe4SRoman Gushchin 
7415313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, false))
7425313bfe4SRoman Gushchin 		goto cleanup;
7435313bfe4SRoman Gushchin 
7445313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
7455313bfe4SRoman Gushchin 		goto cleanup;
7465313bfe4SRoman Gushchin 
7475313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, false))
7485313bfe4SRoman Gushchin 		goto cleanup;
7495313bfe4SRoman Gushchin 
7505313bfe4SRoman Gushchin 	if (proc_check_stopped(pid))
7515313bfe4SRoman Gushchin 		goto cleanup;
7525313bfe4SRoman Gushchin 
7535313bfe4SRoman Gushchin 	ret = KSFT_PASS;
7545313bfe4SRoman Gushchin 
7555313bfe4SRoman Gushchin cleanup:
7565313bfe4SRoman Gushchin 	if (cgroup)
7575313bfe4SRoman Gushchin 		cg_destroy(cgroup);
7585313bfe4SRoman Gushchin 	free(cgroup);
7595313bfe4SRoman Gushchin 	return ret;
7605313bfe4SRoman Gushchin }
7615313bfe4SRoman Gushchin 
7625313bfe4SRoman Gushchin /*
7635313bfe4SRoman Gushchin  * Test that it's possible to freeze a cgroup with a ptraced process.
7645313bfe4SRoman Gushchin  */
7655313bfe4SRoman Gushchin static int test_cgfreezer_ptraced(const char *root)
7665313bfe4SRoman Gushchin {
7675313bfe4SRoman Gushchin 	int pid, ret = KSFT_FAIL;
7685313bfe4SRoman Gushchin 	char *cgroup = NULL;
7695313bfe4SRoman Gushchin 	siginfo_t siginfo;
7705313bfe4SRoman Gushchin 
7715313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_ptraced");
7725313bfe4SRoman Gushchin 	if (!cgroup)
7735313bfe4SRoman Gushchin 		goto cleanup;
7745313bfe4SRoman Gushchin 
7755313bfe4SRoman Gushchin 	if (cg_create(cgroup))
7765313bfe4SRoman Gushchin 		goto cleanup;
7775313bfe4SRoman Gushchin 
7785313bfe4SRoman Gushchin 	pid = cg_run_nowait(cgroup, child_fn, NULL);
7795313bfe4SRoman Gushchin 
7805313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 1))
7815313bfe4SRoman Gushchin 		goto cleanup;
7825313bfe4SRoman Gushchin 
7835313bfe4SRoman Gushchin 	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
7845313bfe4SRoman Gushchin 		goto cleanup;
7855313bfe4SRoman Gushchin 
7865313bfe4SRoman Gushchin 	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
7875313bfe4SRoman Gushchin 		goto cleanup;
7885313bfe4SRoman Gushchin 
7895313bfe4SRoman Gushchin 	waitpid(pid, NULL, 0);
7905313bfe4SRoman Gushchin 
7915313bfe4SRoman Gushchin 	if (cg_check_frozen(cgroup, false))
7925313bfe4SRoman Gushchin 		goto cleanup;
7935313bfe4SRoman Gushchin 
7945313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
7955313bfe4SRoman Gushchin 		goto cleanup;
7965313bfe4SRoman Gushchin 
7975313bfe4SRoman Gushchin 	/*
7985313bfe4SRoman Gushchin 	 * cg_check_frozen(cgroup, true) will fail here,
7995313bfe4SRoman Gushchin 	 * because the task in in the TRACEd state.
8005313bfe4SRoman Gushchin 	 */
8015313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, false))
8025313bfe4SRoman Gushchin 		goto cleanup;
8035313bfe4SRoman Gushchin 
8045313bfe4SRoman Gushchin 	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
8055313bfe4SRoman Gushchin 		goto cleanup;
8065313bfe4SRoman Gushchin 
8075313bfe4SRoman Gushchin 	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
8085313bfe4SRoman Gushchin 		goto cleanup;
8095313bfe4SRoman Gushchin 
8105313bfe4SRoman Gushchin 	ret = KSFT_PASS;
8115313bfe4SRoman Gushchin 
8125313bfe4SRoman Gushchin cleanup:
8135313bfe4SRoman Gushchin 	if (cgroup)
8145313bfe4SRoman Gushchin 		cg_destroy(cgroup);
8155313bfe4SRoman Gushchin 	free(cgroup);
8165313bfe4SRoman Gushchin 	return ret;
8175313bfe4SRoman Gushchin }
8185313bfe4SRoman Gushchin 
8195313bfe4SRoman Gushchin static int vfork_fn(const char *cgroup, void *arg)
8205313bfe4SRoman Gushchin {
8215313bfe4SRoman Gushchin 	int pid = vfork();
8225313bfe4SRoman Gushchin 
8235313bfe4SRoman Gushchin 	if (pid == 0)
8245313bfe4SRoman Gushchin 		while (true)
8255313bfe4SRoman Gushchin 			sleep(1);
8265313bfe4SRoman Gushchin 
8275313bfe4SRoman Gushchin 	return pid;
8285313bfe4SRoman Gushchin }
8295313bfe4SRoman Gushchin 
8305313bfe4SRoman Gushchin /*
8315313bfe4SRoman Gushchin  * Test that it's possible to freeze a cgroup with a process,
8325313bfe4SRoman Gushchin  * which called vfork() and is waiting for a child.
8335313bfe4SRoman Gushchin  */
8345313bfe4SRoman Gushchin static int test_cgfreezer_vfork(const char *root)
8355313bfe4SRoman Gushchin {
8365313bfe4SRoman Gushchin 	int ret = KSFT_FAIL;
8375313bfe4SRoman Gushchin 	char *cgroup = NULL;
8385313bfe4SRoman Gushchin 
8395313bfe4SRoman Gushchin 	cgroup = cg_name(root, "cg_test_vfork");
8405313bfe4SRoman Gushchin 	if (!cgroup)
8415313bfe4SRoman Gushchin 		goto cleanup;
8425313bfe4SRoman Gushchin 
8435313bfe4SRoman Gushchin 	if (cg_create(cgroup))
8445313bfe4SRoman Gushchin 		goto cleanup;
8455313bfe4SRoman Gushchin 
8465313bfe4SRoman Gushchin 	cg_run_nowait(cgroup, vfork_fn, NULL);
8475313bfe4SRoman Gushchin 
8485313bfe4SRoman Gushchin 	if (cg_wait_for_proc_count(cgroup, 2))
8495313bfe4SRoman Gushchin 		goto cleanup;
8505313bfe4SRoman Gushchin 
8515313bfe4SRoman Gushchin 	if (cg_freeze_wait(cgroup, true))
8525313bfe4SRoman Gushchin 		goto cleanup;
8535313bfe4SRoman Gushchin 
8545313bfe4SRoman Gushchin 	ret = KSFT_PASS;
8555313bfe4SRoman Gushchin 
8565313bfe4SRoman Gushchin cleanup:
8575313bfe4SRoman Gushchin 	if (cgroup)
8585313bfe4SRoman Gushchin 		cg_destroy(cgroup);
8595313bfe4SRoman Gushchin 	free(cgroup);
8605313bfe4SRoman Gushchin 	return ret;
8615313bfe4SRoman Gushchin }
8625313bfe4SRoman Gushchin 
8635313bfe4SRoman Gushchin #define T(x) { x, #x }
8645313bfe4SRoman Gushchin struct cgfreezer_test {
8655313bfe4SRoman Gushchin 	int (*fn)(const char *root);
8665313bfe4SRoman Gushchin 	const char *name;
8675313bfe4SRoman Gushchin } tests[] = {
8685313bfe4SRoman Gushchin 	T(test_cgfreezer_simple),
8695313bfe4SRoman Gushchin 	T(test_cgfreezer_tree),
8705313bfe4SRoman Gushchin 	T(test_cgfreezer_forkbomb),
87144e9d308SRoman Gushchin 	T(test_cgfreezer_mkdir),
8725313bfe4SRoman Gushchin 	T(test_cgfreezer_rmdir),
8735313bfe4SRoman Gushchin 	T(test_cgfreezer_migrate),
8745313bfe4SRoman Gushchin 	T(test_cgfreezer_ptrace),
8755313bfe4SRoman Gushchin 	T(test_cgfreezer_stopped),
8765313bfe4SRoman Gushchin 	T(test_cgfreezer_ptraced),
8775313bfe4SRoman Gushchin 	T(test_cgfreezer_vfork),
8785313bfe4SRoman Gushchin };
8795313bfe4SRoman Gushchin #undef T
8805313bfe4SRoman Gushchin 
8815313bfe4SRoman Gushchin int main(int argc, char *argv[])
8825313bfe4SRoman Gushchin {
8835313bfe4SRoman Gushchin 	char root[PATH_MAX];
8845313bfe4SRoman Gushchin 	int i, ret = EXIT_SUCCESS;
8855313bfe4SRoman Gushchin 
8865313bfe4SRoman Gushchin 	if (cg_find_unified_root(root, sizeof(root)))
8875313bfe4SRoman Gushchin 		ksft_exit_skip("cgroup v2 isn't mounted\n");
8885313bfe4SRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
8895313bfe4SRoman Gushchin 		switch (tests[i].fn(root)) {
8905313bfe4SRoman Gushchin 		case KSFT_PASS:
8915313bfe4SRoman Gushchin 			ksft_test_result_pass("%s\n", tests[i].name);
8925313bfe4SRoman Gushchin 			break;
8935313bfe4SRoman Gushchin 		case KSFT_SKIP:
8945313bfe4SRoman Gushchin 			ksft_test_result_skip("%s\n", tests[i].name);
8955313bfe4SRoman Gushchin 			break;
8965313bfe4SRoman Gushchin 		default:
8975313bfe4SRoman Gushchin 			ret = EXIT_FAILURE;
8985313bfe4SRoman Gushchin 			ksft_test_result_fail("%s\n", tests[i].name);
8995313bfe4SRoman Gushchin 			break;
9005313bfe4SRoman Gushchin 		}
9015313bfe4SRoman Gushchin 	}
9025313bfe4SRoman Gushchin 
9035313bfe4SRoman Gushchin 	return ret;
9045313bfe4SRoman Gushchin }
905