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