1*85e3b86eSChristian Brauner /* SPDX-License-Identifier: GPL-2.0 */
2*85e3b86eSChristian Brauner 
3*85e3b86eSChristian Brauner #include <errno.h>
4*85e3b86eSChristian Brauner #include <linux/limits.h>
5*85e3b86eSChristian Brauner #include <stdbool.h>
6*85e3b86eSChristian Brauner #include <stdio.h>
7*85e3b86eSChristian Brauner #include <stdlib.h>
8*85e3b86eSChristian Brauner #include <string.h>
9*85e3b86eSChristian Brauner #include <sys/types.h>
10*85e3b86eSChristian Brauner #include <unistd.h>
11*85e3b86eSChristian Brauner 
12*85e3b86eSChristian Brauner #include "../kselftest.h"
13*85e3b86eSChristian Brauner #include "../pidfd/pidfd.h"
14*85e3b86eSChristian Brauner #include "cgroup_util.h"
15*85e3b86eSChristian Brauner 
16*85e3b86eSChristian Brauner /*
17*85e3b86eSChristian Brauner  * Kill the given cgroup and wait for the inotify signal.
18*85e3b86eSChristian Brauner  * If there are no events in 10 seconds, treat this as an error.
19*85e3b86eSChristian Brauner  * Then check that the cgroup is in the desired state.
20*85e3b86eSChristian Brauner  */
cg_kill_wait(const char * cgroup)21*85e3b86eSChristian Brauner static int cg_kill_wait(const char *cgroup)
22*85e3b86eSChristian Brauner {
23*85e3b86eSChristian Brauner 	int fd, ret = -1;
24*85e3b86eSChristian Brauner 
25*85e3b86eSChristian Brauner 	fd = cg_prepare_for_wait(cgroup);
26*85e3b86eSChristian Brauner 	if (fd < 0)
27*85e3b86eSChristian Brauner 		return fd;
28*85e3b86eSChristian Brauner 
29*85e3b86eSChristian Brauner 	ret = cg_write(cgroup, "cgroup.kill", "1");
30*85e3b86eSChristian Brauner 	if (ret)
31*85e3b86eSChristian Brauner 		goto out;
32*85e3b86eSChristian Brauner 
33*85e3b86eSChristian Brauner 	ret = cg_wait_for(fd);
34*85e3b86eSChristian Brauner 	if (ret)
35*85e3b86eSChristian Brauner 		goto out;
36*85e3b86eSChristian Brauner 
37*85e3b86eSChristian Brauner out:
38*85e3b86eSChristian Brauner 	close(fd);
39*85e3b86eSChristian Brauner 	return ret;
40*85e3b86eSChristian Brauner }
41*85e3b86eSChristian Brauner 
42*85e3b86eSChristian Brauner /*
43*85e3b86eSChristian Brauner  * A simple process running in a sleep loop until being
44*85e3b86eSChristian Brauner  * re-parented.
45*85e3b86eSChristian Brauner  */
child_fn(const char * cgroup,void * arg)46*85e3b86eSChristian Brauner static int child_fn(const char *cgroup, void *arg)
47*85e3b86eSChristian Brauner {
48*85e3b86eSChristian Brauner 	int ppid = getppid();
49*85e3b86eSChristian Brauner 
50*85e3b86eSChristian Brauner 	while (getppid() == ppid)
51*85e3b86eSChristian Brauner 		usleep(1000);
52*85e3b86eSChristian Brauner 
53*85e3b86eSChristian Brauner 	return getppid() == ppid;
54*85e3b86eSChristian Brauner }
55*85e3b86eSChristian Brauner 
test_cgkill_simple(const char * root)56*85e3b86eSChristian Brauner static int test_cgkill_simple(const char *root)
57*85e3b86eSChristian Brauner {
58*85e3b86eSChristian Brauner 	pid_t pids[100];
59*85e3b86eSChristian Brauner 	int ret = KSFT_FAIL;
60*85e3b86eSChristian Brauner 	char *cgroup = NULL;
61*85e3b86eSChristian Brauner 	int i;
62*85e3b86eSChristian Brauner 
63*85e3b86eSChristian Brauner 	cgroup = cg_name(root, "cg_test_simple");
64*85e3b86eSChristian Brauner 	if (!cgroup)
65*85e3b86eSChristian Brauner 		goto cleanup;
66*85e3b86eSChristian Brauner 
67*85e3b86eSChristian Brauner 	if (cg_create(cgroup))
68*85e3b86eSChristian Brauner 		goto cleanup;
69*85e3b86eSChristian Brauner 
70*85e3b86eSChristian Brauner 	for (i = 0; i < 100; i++)
71*85e3b86eSChristian Brauner 		pids[i] = cg_run_nowait(cgroup, child_fn, NULL);
72*85e3b86eSChristian Brauner 
73*85e3b86eSChristian Brauner 	if (cg_wait_for_proc_count(cgroup, 100))
74*85e3b86eSChristian Brauner 		goto cleanup;
75*85e3b86eSChristian Brauner 
76*85e3b86eSChristian Brauner 	if (cg_read_strcmp(cgroup, "cgroup.events", "populated 1\n"))
77*85e3b86eSChristian Brauner 		goto cleanup;
78*85e3b86eSChristian Brauner 
79*85e3b86eSChristian Brauner 	if (cg_kill_wait(cgroup))
80*85e3b86eSChristian Brauner 		goto cleanup;
81*85e3b86eSChristian Brauner 
82*85e3b86eSChristian Brauner 	ret = KSFT_PASS;
83*85e3b86eSChristian Brauner 
84*85e3b86eSChristian Brauner cleanup:
85*85e3b86eSChristian Brauner 	for (i = 0; i < 100; i++)
86*85e3b86eSChristian Brauner 		wait_for_pid(pids[i]);
87*85e3b86eSChristian Brauner 
88*85e3b86eSChristian Brauner 	if (ret == KSFT_PASS &&
89*85e3b86eSChristian Brauner 	    cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
90*85e3b86eSChristian Brauner 		ret = KSFT_FAIL;
91*85e3b86eSChristian Brauner 
92*85e3b86eSChristian Brauner 	if (cgroup)
93*85e3b86eSChristian Brauner 		cg_destroy(cgroup);
94*85e3b86eSChristian Brauner 	free(cgroup);
95*85e3b86eSChristian Brauner 	return ret;
96*85e3b86eSChristian Brauner }
97*85e3b86eSChristian Brauner 
98*85e3b86eSChristian Brauner /*
99*85e3b86eSChristian Brauner  * The test creates the following hierarchy:
100*85e3b86eSChristian Brauner  *       A
101*85e3b86eSChristian Brauner  *    / / \ \
102*85e3b86eSChristian Brauner  *   B  E  I K
103*85e3b86eSChristian Brauner  *  /\  |
104*85e3b86eSChristian Brauner  * C  D F
105*85e3b86eSChristian Brauner  *      |
106*85e3b86eSChristian Brauner  *      G
107*85e3b86eSChristian Brauner  *      |
108*85e3b86eSChristian Brauner  *      H
109*85e3b86eSChristian Brauner  *
110*85e3b86eSChristian Brauner  * with a process in C, H and 3 processes in K.
111*85e3b86eSChristian Brauner  * Then it tries to kill the whole tree.
112*85e3b86eSChristian Brauner  */
test_cgkill_tree(const char * root)113*85e3b86eSChristian Brauner static int test_cgkill_tree(const char *root)
114*85e3b86eSChristian Brauner {
115*85e3b86eSChristian Brauner 	pid_t pids[5];
116*85e3b86eSChristian Brauner 	char *cgroup[10] = {0};
117*85e3b86eSChristian Brauner 	int ret = KSFT_FAIL;
118*85e3b86eSChristian Brauner 	int i;
119*85e3b86eSChristian Brauner 
120*85e3b86eSChristian Brauner 	cgroup[0] = cg_name(root, "cg_test_tree_A");
121*85e3b86eSChristian Brauner 	if (!cgroup[0])
122*85e3b86eSChristian Brauner 		goto cleanup;
123*85e3b86eSChristian Brauner 
124*85e3b86eSChristian Brauner 	cgroup[1] = cg_name(cgroup[0], "B");
125*85e3b86eSChristian Brauner 	if (!cgroup[1])
126*85e3b86eSChristian Brauner 		goto cleanup;
127*85e3b86eSChristian Brauner 
128*85e3b86eSChristian Brauner 	cgroup[2] = cg_name(cgroup[1], "C");
129*85e3b86eSChristian Brauner 	if (!cgroup[2])
130*85e3b86eSChristian Brauner 		goto cleanup;
131*85e3b86eSChristian Brauner 
132*85e3b86eSChristian Brauner 	cgroup[3] = cg_name(cgroup[1], "D");
133*85e3b86eSChristian Brauner 	if (!cgroup[3])
134*85e3b86eSChristian Brauner 		goto cleanup;
135*85e3b86eSChristian Brauner 
136*85e3b86eSChristian Brauner 	cgroup[4] = cg_name(cgroup[0], "E");
137*85e3b86eSChristian Brauner 	if (!cgroup[4])
138*85e3b86eSChristian Brauner 		goto cleanup;
139*85e3b86eSChristian Brauner 
140*85e3b86eSChristian Brauner 	cgroup[5] = cg_name(cgroup[4], "F");
141*85e3b86eSChristian Brauner 	if (!cgroup[5])
142*85e3b86eSChristian Brauner 		goto cleanup;
143*85e3b86eSChristian Brauner 
144*85e3b86eSChristian Brauner 	cgroup[6] = cg_name(cgroup[5], "G");
145*85e3b86eSChristian Brauner 	if (!cgroup[6])
146*85e3b86eSChristian Brauner 		goto cleanup;
147*85e3b86eSChristian Brauner 
148*85e3b86eSChristian Brauner 	cgroup[7] = cg_name(cgroup[6], "H");
149*85e3b86eSChristian Brauner 	if (!cgroup[7])
150*85e3b86eSChristian Brauner 		goto cleanup;
151*85e3b86eSChristian Brauner 
152*85e3b86eSChristian Brauner 	cgroup[8] = cg_name(cgroup[0], "I");
153*85e3b86eSChristian Brauner 	if (!cgroup[8])
154*85e3b86eSChristian Brauner 		goto cleanup;
155*85e3b86eSChristian Brauner 
156*85e3b86eSChristian Brauner 	cgroup[9] = cg_name(cgroup[0], "K");
157*85e3b86eSChristian Brauner 	if (!cgroup[9])
158*85e3b86eSChristian Brauner 		goto cleanup;
159*85e3b86eSChristian Brauner 
160*85e3b86eSChristian Brauner 	for (i = 0; i < 10; i++)
161*85e3b86eSChristian Brauner 		if (cg_create(cgroup[i]))
162*85e3b86eSChristian Brauner 			goto cleanup;
163*85e3b86eSChristian Brauner 
164*85e3b86eSChristian Brauner 	pids[0] = cg_run_nowait(cgroup[2], child_fn, NULL);
165*85e3b86eSChristian Brauner 	pids[1] = cg_run_nowait(cgroup[7], child_fn, NULL);
166*85e3b86eSChristian Brauner 	pids[2] = cg_run_nowait(cgroup[9], child_fn, NULL);
167*85e3b86eSChristian Brauner 	pids[3] = cg_run_nowait(cgroup[9], child_fn, NULL);
168*85e3b86eSChristian Brauner 	pids[4] = cg_run_nowait(cgroup[9], child_fn, NULL);
169*85e3b86eSChristian Brauner 
170*85e3b86eSChristian Brauner 	/*
171*85e3b86eSChristian Brauner 	 * Wait until all child processes will enter
172*85e3b86eSChristian Brauner 	 * corresponding cgroups.
173*85e3b86eSChristian Brauner 	 */
174*85e3b86eSChristian Brauner 
175*85e3b86eSChristian Brauner 	if (cg_wait_for_proc_count(cgroup[2], 1) ||
176*85e3b86eSChristian Brauner 	    cg_wait_for_proc_count(cgroup[7], 1) ||
177*85e3b86eSChristian Brauner 	    cg_wait_for_proc_count(cgroup[9], 3))
178*85e3b86eSChristian Brauner 		goto cleanup;
179*85e3b86eSChristian Brauner 
180*85e3b86eSChristian Brauner 	/*
181*85e3b86eSChristian Brauner 	 * Kill A and check that we get an empty notification.
182*85e3b86eSChristian Brauner 	 */
183*85e3b86eSChristian Brauner 	if (cg_kill_wait(cgroup[0]))
184*85e3b86eSChristian Brauner 		goto cleanup;
185*85e3b86eSChristian Brauner 
186*85e3b86eSChristian Brauner 	ret = KSFT_PASS;
187*85e3b86eSChristian Brauner 
188*85e3b86eSChristian Brauner cleanup:
189*85e3b86eSChristian Brauner 	for (i = 0; i < 5; i++)
190*85e3b86eSChristian Brauner 		wait_for_pid(pids[i]);
191*85e3b86eSChristian Brauner 
192*85e3b86eSChristian Brauner 	if (ret == KSFT_PASS &&
193*85e3b86eSChristian Brauner 	    cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n"))
194*85e3b86eSChristian Brauner 		ret = KSFT_FAIL;
195*85e3b86eSChristian Brauner 
196*85e3b86eSChristian Brauner 	for (i = 9; i >= 0 && cgroup[i]; i--) {
197*85e3b86eSChristian Brauner 		cg_destroy(cgroup[i]);
198*85e3b86eSChristian Brauner 		free(cgroup[i]);
199*85e3b86eSChristian Brauner 	}
200*85e3b86eSChristian Brauner 
201*85e3b86eSChristian Brauner 	return ret;
202*85e3b86eSChristian Brauner }
203*85e3b86eSChristian Brauner 
forkbomb_fn(const char * cgroup,void * arg)204*85e3b86eSChristian Brauner static int forkbomb_fn(const char *cgroup, void *arg)
205*85e3b86eSChristian Brauner {
206*85e3b86eSChristian Brauner 	int ppid;
207*85e3b86eSChristian Brauner 
208*85e3b86eSChristian Brauner 	fork();
209*85e3b86eSChristian Brauner 	fork();
210*85e3b86eSChristian Brauner 
211*85e3b86eSChristian Brauner 	ppid = getppid();
212*85e3b86eSChristian Brauner 
213*85e3b86eSChristian Brauner 	while (getppid() == ppid)
214*85e3b86eSChristian Brauner 		usleep(1000);
215*85e3b86eSChristian Brauner 
216*85e3b86eSChristian Brauner 	return getppid() == ppid;
217*85e3b86eSChristian Brauner }
218*85e3b86eSChristian Brauner 
219*85e3b86eSChristian Brauner /*
220*85e3b86eSChristian Brauner  * The test runs a fork bomb in a cgroup and tries to kill it.
221*85e3b86eSChristian Brauner  */
test_cgkill_forkbomb(const char * root)222*85e3b86eSChristian Brauner static int test_cgkill_forkbomb(const char *root)
223*85e3b86eSChristian Brauner {
224*85e3b86eSChristian Brauner 	int ret = KSFT_FAIL;
225*85e3b86eSChristian Brauner 	char *cgroup = NULL;
226*85e3b86eSChristian Brauner 	pid_t pid = -ESRCH;
227*85e3b86eSChristian Brauner 
228*85e3b86eSChristian Brauner 	cgroup = cg_name(root, "cg_forkbomb_test");
229*85e3b86eSChristian Brauner 	if (!cgroup)
230*85e3b86eSChristian Brauner 		goto cleanup;
231*85e3b86eSChristian Brauner 
232*85e3b86eSChristian Brauner 	if (cg_create(cgroup))
233*85e3b86eSChristian Brauner 		goto cleanup;
234*85e3b86eSChristian Brauner 
235*85e3b86eSChristian Brauner 	pid = cg_run_nowait(cgroup, forkbomb_fn, NULL);
236*85e3b86eSChristian Brauner 	if (pid < 0)
237*85e3b86eSChristian Brauner 		goto cleanup;
238*85e3b86eSChristian Brauner 
239*85e3b86eSChristian Brauner 	usleep(100000);
240*85e3b86eSChristian Brauner 
241*85e3b86eSChristian Brauner 	if (cg_kill_wait(cgroup))
242*85e3b86eSChristian Brauner 		goto cleanup;
243*85e3b86eSChristian Brauner 
244*85e3b86eSChristian Brauner 	if (cg_wait_for_proc_count(cgroup, 0))
245*85e3b86eSChristian Brauner 		goto cleanup;
246*85e3b86eSChristian Brauner 
247*85e3b86eSChristian Brauner 	ret = KSFT_PASS;
248*85e3b86eSChristian Brauner 
249*85e3b86eSChristian Brauner cleanup:
250*85e3b86eSChristian Brauner 	if (pid > 0)
251*85e3b86eSChristian Brauner 		wait_for_pid(pid);
252*85e3b86eSChristian Brauner 
253*85e3b86eSChristian Brauner 	if (ret == KSFT_PASS &&
254*85e3b86eSChristian Brauner 	    cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
255*85e3b86eSChristian Brauner 		ret = KSFT_FAIL;
256*85e3b86eSChristian Brauner 
257*85e3b86eSChristian Brauner 	if (cgroup)
258*85e3b86eSChristian Brauner 		cg_destroy(cgroup);
259*85e3b86eSChristian Brauner 	free(cgroup);
260*85e3b86eSChristian Brauner 	return ret;
261*85e3b86eSChristian Brauner }
262*85e3b86eSChristian Brauner 
263*85e3b86eSChristian Brauner #define T(x) { x, #x }
264*85e3b86eSChristian Brauner struct cgkill_test {
265*85e3b86eSChristian Brauner 	int (*fn)(const char *root);
266*85e3b86eSChristian Brauner 	const char *name;
267*85e3b86eSChristian Brauner } tests[] = {
268*85e3b86eSChristian Brauner 	T(test_cgkill_simple),
269*85e3b86eSChristian Brauner 	T(test_cgkill_tree),
270*85e3b86eSChristian Brauner 	T(test_cgkill_forkbomb),
271*85e3b86eSChristian Brauner };
272*85e3b86eSChristian Brauner #undef T
273*85e3b86eSChristian Brauner 
main(int argc,char * argv[])274*85e3b86eSChristian Brauner int main(int argc, char *argv[])
275*85e3b86eSChristian Brauner {
276*85e3b86eSChristian Brauner 	char root[PATH_MAX];
277*85e3b86eSChristian Brauner 	int i, ret = EXIT_SUCCESS;
278*85e3b86eSChristian Brauner 
279*85e3b86eSChristian Brauner 	if (cg_find_unified_root(root, sizeof(root)))
280*85e3b86eSChristian Brauner 		ksft_exit_skip("cgroup v2 isn't mounted\n");
281*85e3b86eSChristian Brauner 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
282*85e3b86eSChristian Brauner 		switch (tests[i].fn(root)) {
283*85e3b86eSChristian Brauner 		case KSFT_PASS:
284*85e3b86eSChristian Brauner 			ksft_test_result_pass("%s\n", tests[i].name);
285*85e3b86eSChristian Brauner 			break;
286*85e3b86eSChristian Brauner 		case KSFT_SKIP:
287*85e3b86eSChristian Brauner 			ksft_test_result_skip("%s\n", tests[i].name);
288*85e3b86eSChristian Brauner 			break;
289*85e3b86eSChristian Brauner 		default:
290*85e3b86eSChristian Brauner 			ret = EXIT_FAILURE;
291*85e3b86eSChristian Brauner 			ksft_test_result_fail("%s\n", tests[i].name);
292*85e3b86eSChristian Brauner 			break;
293*85e3b86eSChristian Brauner 		}
294*85e3b86eSChristian Brauner 	}
295*85e3b86eSChristian Brauner 
296*85e3b86eSChristian Brauner 	return ret;
297*85e3b86eSChristian Brauner }
298