1d863cb03SClaudio /* SPDX-License-Identifier: GPL-2.0 */
2d863cb03SClaudio 
3d863cb03SClaudio #include <linux/limits.h>
4d863cb03SClaudio #include <sys/types.h>
5d863cb03SClaudio #include <unistd.h>
6d863cb03SClaudio #include <stdio.h>
7d863cb03SClaudio #include <errno.h>
811318989SMichal Koutný #include <signal.h>
911318989SMichal Koutný #include <string.h>
1011318989SMichal Koutný #include <pthread.h>
11d863cb03SClaudio 
12d863cb03SClaudio #include "../kselftest.h"
13d863cb03SClaudio #include "cgroup_util.h"
14d863cb03SClaudio 
15d863cb03SClaudio /*
16d863cb03SClaudio  * A(0) - B(0) - C(1)
17d863cb03SClaudio  *        \ D(0)
18d863cb03SClaudio  *
19d863cb03SClaudio  * A, B and C's "populated" fields would be 1 while D's 0.
20d863cb03SClaudio  * test that after the one process in C is moved to root,
21d863cb03SClaudio  * A,B and C's "populated" fields would flip to "0" and file
22d863cb03SClaudio  * modified events will be generated on the
23d863cb03SClaudio  * "cgroup.events" files of both cgroups.
24d863cb03SClaudio  */
25d863cb03SClaudio static int test_cgcore_populated(const char *root)
26d863cb03SClaudio {
27d863cb03SClaudio 	int ret = KSFT_FAIL;
28d863cb03SClaudio 	char *cg_test_a = NULL, *cg_test_b = NULL;
29d863cb03SClaudio 	char *cg_test_c = NULL, *cg_test_d = NULL;
30d863cb03SClaudio 
31d863cb03SClaudio 	cg_test_a = cg_name(root, "cg_test_a");
32d863cb03SClaudio 	cg_test_b = cg_name(root, "cg_test_a/cg_test_b");
33d863cb03SClaudio 	cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c");
34d863cb03SClaudio 	cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d");
35d863cb03SClaudio 
36d863cb03SClaudio 	if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d)
37d863cb03SClaudio 		goto cleanup;
38d863cb03SClaudio 
39d863cb03SClaudio 	if (cg_create(cg_test_a))
40d863cb03SClaudio 		goto cleanup;
41d863cb03SClaudio 
42d863cb03SClaudio 	if (cg_create(cg_test_b))
43d863cb03SClaudio 		goto cleanup;
44d863cb03SClaudio 
45d863cb03SClaudio 	if (cg_create(cg_test_c))
46d863cb03SClaudio 		goto cleanup;
47d863cb03SClaudio 
48d863cb03SClaudio 	if (cg_create(cg_test_d))
49d863cb03SClaudio 		goto cleanup;
50d863cb03SClaudio 
51d863cb03SClaudio 	if (cg_enter_current(cg_test_c))
52d863cb03SClaudio 		goto cleanup;
53d863cb03SClaudio 
54d863cb03SClaudio 	if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n"))
55d863cb03SClaudio 		goto cleanup;
56d863cb03SClaudio 
57d863cb03SClaudio 	if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n"))
58d863cb03SClaudio 		goto cleanup;
59d863cb03SClaudio 
60d863cb03SClaudio 	if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n"))
61d863cb03SClaudio 		goto cleanup;
62d863cb03SClaudio 
63d863cb03SClaudio 	if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
64d863cb03SClaudio 		goto cleanup;
65d863cb03SClaudio 
66d863cb03SClaudio 	if (cg_enter_current(root))
67d863cb03SClaudio 		goto cleanup;
68d863cb03SClaudio 
69d863cb03SClaudio 	if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n"))
70d863cb03SClaudio 		goto cleanup;
71d863cb03SClaudio 
72d863cb03SClaudio 	if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n"))
73d863cb03SClaudio 		goto cleanup;
74d863cb03SClaudio 
75d863cb03SClaudio 	if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n"))
76d863cb03SClaudio 		goto cleanup;
77d863cb03SClaudio 
78d863cb03SClaudio 	if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
79d863cb03SClaudio 		goto cleanup;
80d863cb03SClaudio 
81d863cb03SClaudio 	ret = KSFT_PASS;
82d863cb03SClaudio 
83d863cb03SClaudio cleanup:
84d863cb03SClaudio 	if (cg_test_d)
85d863cb03SClaudio 		cg_destroy(cg_test_d);
86d863cb03SClaudio 	if (cg_test_c)
87d863cb03SClaudio 		cg_destroy(cg_test_c);
88d863cb03SClaudio 	if (cg_test_b)
89d863cb03SClaudio 		cg_destroy(cg_test_b);
90d863cb03SClaudio 	if (cg_test_a)
91d863cb03SClaudio 		cg_destroy(cg_test_a);
92d863cb03SClaudio 	free(cg_test_d);
93d863cb03SClaudio 	free(cg_test_c);
94d863cb03SClaudio 	free(cg_test_b);
95d863cb03SClaudio 	free(cg_test_a);
96d863cb03SClaudio 	return ret;
97d863cb03SClaudio }
98d863cb03SClaudio 
99d863cb03SClaudio /*
100d863cb03SClaudio  * A (domain threaded) - B (threaded) - C (domain)
101d863cb03SClaudio  *
102d863cb03SClaudio  * test that C can't be used until it is turned into a
103d863cb03SClaudio  * threaded cgroup.  "cgroup.type" file will report "domain (invalid)" in
104d863cb03SClaudio  * these cases. Operations which fail due to invalid topology use
105d863cb03SClaudio  * EOPNOTSUPP as the errno.
106d863cb03SClaudio  */
107d863cb03SClaudio static int test_cgcore_invalid_domain(const char *root)
108d863cb03SClaudio {
109d863cb03SClaudio 	int ret = KSFT_FAIL;
110d863cb03SClaudio 	char *grandparent = NULL, *parent = NULL, *child = NULL;
111d863cb03SClaudio 
112d863cb03SClaudio 	grandparent = cg_name(root, "cg_test_grandparent");
113d863cb03SClaudio 	parent = cg_name(root, "cg_test_grandparent/cg_test_parent");
114d863cb03SClaudio 	child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child");
115d863cb03SClaudio 	if (!parent || !child || !grandparent)
116d863cb03SClaudio 		goto cleanup;
117d863cb03SClaudio 
118d863cb03SClaudio 	if (cg_create(grandparent))
119d863cb03SClaudio 		goto cleanup;
120d863cb03SClaudio 
121d863cb03SClaudio 	if (cg_create(parent))
122d863cb03SClaudio 		goto cleanup;
123d863cb03SClaudio 
124d863cb03SClaudio 	if (cg_create(child))
125d863cb03SClaudio 		goto cleanup;
126d863cb03SClaudio 
127d863cb03SClaudio 	if (cg_write(parent, "cgroup.type", "threaded"))
128d863cb03SClaudio 		goto cleanup;
129d863cb03SClaudio 
130d863cb03SClaudio 	if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n"))
131d863cb03SClaudio 		goto cleanup;
132d863cb03SClaudio 
133d863cb03SClaudio 	if (!cg_enter_current(child))
134d863cb03SClaudio 		goto cleanup;
135d863cb03SClaudio 
136d863cb03SClaudio 	if (errno != EOPNOTSUPP)
137d863cb03SClaudio 		goto cleanup;
138d863cb03SClaudio 
139d863cb03SClaudio 	ret = KSFT_PASS;
140d863cb03SClaudio 
141d863cb03SClaudio cleanup:
142d863cb03SClaudio 	cg_enter_current(root);
143d863cb03SClaudio 	if (child)
144d863cb03SClaudio 		cg_destroy(child);
145d863cb03SClaudio 	if (parent)
146d863cb03SClaudio 		cg_destroy(parent);
147d863cb03SClaudio 	if (grandparent)
148d863cb03SClaudio 		cg_destroy(grandparent);
149d863cb03SClaudio 	free(child);
150d863cb03SClaudio 	free(parent);
151d863cb03SClaudio 	free(grandparent);
152d863cb03SClaudio 	return ret;
153d863cb03SClaudio }
154d863cb03SClaudio 
155d863cb03SClaudio /*
156d863cb03SClaudio  * Test that when a child becomes threaded
157d863cb03SClaudio  * the parent type becomes domain threaded.
158d863cb03SClaudio  */
159d863cb03SClaudio static int test_cgcore_parent_becomes_threaded(const char *root)
160d863cb03SClaudio {
161d863cb03SClaudio 	int ret = KSFT_FAIL;
162d863cb03SClaudio 	char *parent = NULL, *child = NULL;
163d863cb03SClaudio 
164d863cb03SClaudio 	parent = cg_name(root, "cg_test_parent");
165d863cb03SClaudio 	child = cg_name(root, "cg_test_parent/cg_test_child");
166d863cb03SClaudio 	if (!parent || !child)
167d863cb03SClaudio 		goto cleanup;
168d863cb03SClaudio 
169d863cb03SClaudio 	if (cg_create(parent))
170d863cb03SClaudio 		goto cleanup;
171d863cb03SClaudio 
172d863cb03SClaudio 	if (cg_create(child))
173d863cb03SClaudio 		goto cleanup;
174d863cb03SClaudio 
175d863cb03SClaudio 	if (cg_write(child, "cgroup.type", "threaded"))
176d863cb03SClaudio 		goto cleanup;
177d863cb03SClaudio 
178d863cb03SClaudio 	if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n"))
179d863cb03SClaudio 		goto cleanup;
180d863cb03SClaudio 
181d863cb03SClaudio 	ret = KSFT_PASS;
182d863cb03SClaudio 
183d863cb03SClaudio cleanup:
184d863cb03SClaudio 	if (child)
185d863cb03SClaudio 		cg_destroy(child);
186d863cb03SClaudio 	if (parent)
187d863cb03SClaudio 		cg_destroy(parent);
188d863cb03SClaudio 	free(child);
189d863cb03SClaudio 	free(parent);
190d863cb03SClaudio 	return ret;
191d863cb03SClaudio 
192d863cb03SClaudio }
193d863cb03SClaudio 
194d863cb03SClaudio /*
195d863cb03SClaudio  * Test that there's no internal process constrain on threaded cgroups.
196d863cb03SClaudio  * You can add threads/processes on a parent with a controller enabled.
197d863cb03SClaudio  */
198d863cb03SClaudio static int test_cgcore_no_internal_process_constraint_on_threads(const char *root)
199d863cb03SClaudio {
200d863cb03SClaudio 	int ret = KSFT_FAIL;
201d863cb03SClaudio 	char *parent = NULL, *child = NULL;
202d863cb03SClaudio 
203d863cb03SClaudio 	if (cg_read_strstr(root, "cgroup.controllers", "cpu") ||
204f97f3f88SAlex Shi 	    cg_write(root, "cgroup.subtree_control", "+cpu")) {
205d863cb03SClaudio 		ret = KSFT_SKIP;
206d863cb03SClaudio 		goto cleanup;
207d863cb03SClaudio 	}
208d863cb03SClaudio 
209d863cb03SClaudio 	parent = cg_name(root, "cg_test_parent");
210d863cb03SClaudio 	child = cg_name(root, "cg_test_parent/cg_test_child");
211d863cb03SClaudio 	if (!parent || !child)
212d863cb03SClaudio 		goto cleanup;
213d863cb03SClaudio 
214d863cb03SClaudio 	if (cg_create(parent))
215d863cb03SClaudio 		goto cleanup;
216d863cb03SClaudio 
217d863cb03SClaudio 	if (cg_create(child))
218d863cb03SClaudio 		goto cleanup;
219d863cb03SClaudio 
220d863cb03SClaudio 	if (cg_write(parent, "cgroup.type", "threaded"))
221d863cb03SClaudio 		goto cleanup;
222d863cb03SClaudio 
223d863cb03SClaudio 	if (cg_write(child, "cgroup.type", "threaded"))
224d863cb03SClaudio 		goto cleanup;
225d863cb03SClaudio 
226d863cb03SClaudio 	if (cg_write(parent, "cgroup.subtree_control", "+cpu"))
227d863cb03SClaudio 		goto cleanup;
228d863cb03SClaudio 
229d863cb03SClaudio 	if (cg_enter_current(parent))
230d863cb03SClaudio 		goto cleanup;
231d863cb03SClaudio 
232d863cb03SClaudio 	ret = KSFT_PASS;
233d863cb03SClaudio 
234d863cb03SClaudio cleanup:
235d863cb03SClaudio 	cg_enter_current(root);
236d863cb03SClaudio 	cg_enter_current(root);
237d863cb03SClaudio 	if (child)
238d863cb03SClaudio 		cg_destroy(child);
239d863cb03SClaudio 	if (parent)
240d863cb03SClaudio 		cg_destroy(parent);
241d863cb03SClaudio 	free(child);
242d863cb03SClaudio 	free(parent);
243d863cb03SClaudio 	return ret;
244d863cb03SClaudio }
245d863cb03SClaudio 
246d863cb03SClaudio /*
247d863cb03SClaudio  * Test that you can't enable a controller on a child if it's not enabled
248d863cb03SClaudio  * on the parent.
249d863cb03SClaudio  */
250d863cb03SClaudio static int test_cgcore_top_down_constraint_enable(const char *root)
251d863cb03SClaudio {
252d863cb03SClaudio 	int ret = KSFT_FAIL;
253d863cb03SClaudio 	char *parent = NULL, *child = NULL;
254d863cb03SClaudio 
255d863cb03SClaudio 	parent = cg_name(root, "cg_test_parent");
256d863cb03SClaudio 	child = cg_name(root, "cg_test_parent/cg_test_child");
257d863cb03SClaudio 	if (!parent || !child)
258d863cb03SClaudio 		goto cleanup;
259d863cb03SClaudio 
260d863cb03SClaudio 	if (cg_create(parent))
261d863cb03SClaudio 		goto cleanup;
262d863cb03SClaudio 
263d863cb03SClaudio 	if (cg_create(child))
264d863cb03SClaudio 		goto cleanup;
265d863cb03SClaudio 
266d863cb03SClaudio 	if (!cg_write(child, "cgroup.subtree_control", "+memory"))
267d863cb03SClaudio 		goto cleanup;
268d863cb03SClaudio 
269d863cb03SClaudio 	ret = KSFT_PASS;
270d863cb03SClaudio 
271d863cb03SClaudio cleanup:
272d863cb03SClaudio 	if (child)
273d863cb03SClaudio 		cg_destroy(child);
274d863cb03SClaudio 	if (parent)
275d863cb03SClaudio 		cg_destroy(parent);
276d863cb03SClaudio 	free(child);
277d863cb03SClaudio 	free(parent);
278d863cb03SClaudio 	return ret;
279d863cb03SClaudio }
280d863cb03SClaudio 
281d863cb03SClaudio /*
282d863cb03SClaudio  * Test that you can't disable a controller on a parent
283d863cb03SClaudio  * if it's enabled in a child.
284d863cb03SClaudio  */
285d863cb03SClaudio static int test_cgcore_top_down_constraint_disable(const char *root)
286d863cb03SClaudio {
287d863cb03SClaudio 	int ret = KSFT_FAIL;
288d863cb03SClaudio 	char *parent = NULL, *child = NULL;
289d863cb03SClaudio 
290d863cb03SClaudio 	parent = cg_name(root, "cg_test_parent");
291d863cb03SClaudio 	child = cg_name(root, "cg_test_parent/cg_test_child");
292d863cb03SClaudio 	if (!parent || !child)
293d863cb03SClaudio 		goto cleanup;
294d863cb03SClaudio 
295d863cb03SClaudio 	if (cg_create(parent))
296d863cb03SClaudio 		goto cleanup;
297d863cb03SClaudio 
298d863cb03SClaudio 	if (cg_create(child))
299d863cb03SClaudio 		goto cleanup;
300d863cb03SClaudio 
301d863cb03SClaudio 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
302d863cb03SClaudio 		goto cleanup;
303d863cb03SClaudio 
304d863cb03SClaudio 	if (cg_write(child, "cgroup.subtree_control", "+memory"))
305d863cb03SClaudio 		goto cleanup;
306d863cb03SClaudio 
307d863cb03SClaudio 	if (!cg_write(parent, "cgroup.subtree_control", "-memory"))
308d863cb03SClaudio 		goto cleanup;
309d863cb03SClaudio 
310d863cb03SClaudio 	ret = KSFT_PASS;
311d863cb03SClaudio 
312d863cb03SClaudio cleanup:
313d863cb03SClaudio 	if (child)
314d863cb03SClaudio 		cg_destroy(child);
315d863cb03SClaudio 	if (parent)
316d863cb03SClaudio 		cg_destroy(parent);
317d863cb03SClaudio 	free(child);
318d863cb03SClaudio 	free(parent);
319d863cb03SClaudio 	return ret;
320d863cb03SClaudio }
321d863cb03SClaudio 
322d863cb03SClaudio /*
323d863cb03SClaudio  * Test internal process constraint.
324d863cb03SClaudio  * You can't add a pid to a domain parent if a controller is enabled.
325d863cb03SClaudio  */
326d863cb03SClaudio static int test_cgcore_internal_process_constraint(const char *root)
327d863cb03SClaudio {
328d863cb03SClaudio 	int ret = KSFT_FAIL;
329d863cb03SClaudio 	char *parent = NULL, *child = NULL;
330d863cb03SClaudio 
331d863cb03SClaudio 	parent = cg_name(root, "cg_test_parent");
332d863cb03SClaudio 	child = cg_name(root, "cg_test_parent/cg_test_child");
333d863cb03SClaudio 	if (!parent || !child)
334d863cb03SClaudio 		goto cleanup;
335d863cb03SClaudio 
336d863cb03SClaudio 	if (cg_create(parent))
337d863cb03SClaudio 		goto cleanup;
338d863cb03SClaudio 
339d863cb03SClaudio 	if (cg_create(child))
340d863cb03SClaudio 		goto cleanup;
341d863cb03SClaudio 
342d863cb03SClaudio 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
343d863cb03SClaudio 		goto cleanup;
344d863cb03SClaudio 
345d863cb03SClaudio 	if (!cg_enter_current(parent))
346d863cb03SClaudio 		goto cleanup;
347d863cb03SClaudio 
348d863cb03SClaudio 	ret = KSFT_PASS;
349d863cb03SClaudio 
350d863cb03SClaudio cleanup:
351d863cb03SClaudio 	if (child)
352d863cb03SClaudio 		cg_destroy(child);
353d863cb03SClaudio 	if (parent)
354d863cb03SClaudio 		cg_destroy(parent);
355d863cb03SClaudio 	free(child);
356d863cb03SClaudio 	free(parent);
357d863cb03SClaudio 	return ret;
358d863cb03SClaudio }
359d863cb03SClaudio 
36011318989SMichal Koutný static void *dummy_thread_fn(void *arg)
36111318989SMichal Koutný {
36211318989SMichal Koutný 	return (void *)(size_t)pause();
36311318989SMichal Koutný }
36411318989SMichal Koutný 
36511318989SMichal Koutný /*
36611318989SMichal Koutný  * Test threadgroup migration.
36711318989SMichal Koutný  * All threads of a process are migrated together.
36811318989SMichal Koutný  */
36911318989SMichal Koutný static int test_cgcore_proc_migration(const char *root)
37011318989SMichal Koutný {
37111318989SMichal Koutný 	int ret = KSFT_FAIL;
372192c197cSDan Carpenter 	int t, c_threads = 0, n_threads = 13;
37311318989SMichal Koutný 	char *src = NULL, *dst = NULL;
37411318989SMichal Koutný 	pthread_t threads[n_threads];
37511318989SMichal Koutný 
37611318989SMichal Koutný 	src = cg_name(root, "cg_src");
37711318989SMichal Koutný 	dst = cg_name(root, "cg_dst");
37811318989SMichal Koutný 	if (!src || !dst)
37911318989SMichal Koutný 		goto cleanup;
38011318989SMichal Koutný 
38111318989SMichal Koutný 	if (cg_create(src))
38211318989SMichal Koutný 		goto cleanup;
38311318989SMichal Koutný 	if (cg_create(dst))
38411318989SMichal Koutný 		goto cleanup;
38511318989SMichal Koutný 
38611318989SMichal Koutný 	if (cg_enter_current(src))
38711318989SMichal Koutný 		goto cleanup;
38811318989SMichal Koutný 
38911318989SMichal Koutný 	for (c_threads = 0; c_threads < n_threads; ++c_threads) {
39011318989SMichal Koutný 		if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL))
39111318989SMichal Koutný 			goto cleanup;
39211318989SMichal Koutný 	}
39311318989SMichal Koutný 
39411318989SMichal Koutný 	cg_enter_current(dst);
39511318989SMichal Koutný 	if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1)
39611318989SMichal Koutný 		goto cleanup;
39711318989SMichal Koutný 
39811318989SMichal Koutný 	ret = KSFT_PASS;
39911318989SMichal Koutný 
40011318989SMichal Koutný cleanup:
40111318989SMichal Koutný 	for (t = 0; t < c_threads; ++t) {
40211318989SMichal Koutný 		pthread_cancel(threads[t]);
40311318989SMichal Koutný 	}
40411318989SMichal Koutný 
40511318989SMichal Koutný 	for (t = 0; t < c_threads; ++t) {
40611318989SMichal Koutný 		pthread_join(threads[t], NULL);
40711318989SMichal Koutný 	}
40811318989SMichal Koutný 
40911318989SMichal Koutný 	cg_enter_current(root);
41011318989SMichal Koutný 
41111318989SMichal Koutný 	if (dst)
41211318989SMichal Koutný 		cg_destroy(dst);
41311318989SMichal Koutný 	if (src)
41411318989SMichal Koutný 		cg_destroy(src);
41511318989SMichal Koutný 	free(dst);
41611318989SMichal Koutný 	free(src);
41711318989SMichal Koutný 	return ret;
41811318989SMichal Koutný }
41911318989SMichal Koutný 
42011318989SMichal Koutný static void *migrating_thread_fn(void *arg)
42111318989SMichal Koutný {
42211318989SMichal Koutný 	int g, i, n_iterations = 1000;
42311318989SMichal Koutný 	char **grps = arg;
42411318989SMichal Koutný 	char lines[3][PATH_MAX];
42511318989SMichal Koutný 
42611318989SMichal Koutný 	for (g = 1; g < 3; ++g)
42711318989SMichal Koutný 		snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0]));
42811318989SMichal Koutný 
42911318989SMichal Koutný 	for (i = 0; i < n_iterations; ++i) {
43011318989SMichal Koutný 		cg_enter_current_thread(grps[(i % 2) + 1]);
43111318989SMichal Koutný 
43211318989SMichal Koutný 		if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1]))
43311318989SMichal Koutný 			return (void *)-1;
43411318989SMichal Koutný 	}
43511318989SMichal Koutný 	return NULL;
43611318989SMichal Koutný }
43711318989SMichal Koutný 
43811318989SMichal Koutný /*
43911318989SMichal Koutný  * Test single thread migration.
44011318989SMichal Koutný  * Threaded cgroups allow successful migration of a thread.
44111318989SMichal Koutný  */
44211318989SMichal Koutný static int test_cgcore_thread_migration(const char *root)
44311318989SMichal Koutný {
44411318989SMichal Koutný 	int ret = KSFT_FAIL;
44511318989SMichal Koutný 	char *dom = NULL;
44611318989SMichal Koutný 	char line[PATH_MAX];
44711318989SMichal Koutný 	char *grps[3] = { (char *)root, NULL, NULL };
44811318989SMichal Koutný 	pthread_t thr;
44911318989SMichal Koutný 	void *retval;
45011318989SMichal Koutný 
45111318989SMichal Koutný 	dom = cg_name(root, "cg_dom");
45211318989SMichal Koutný 	grps[1] = cg_name(root, "cg_dom/cg_src");
45311318989SMichal Koutný 	grps[2] = cg_name(root, "cg_dom/cg_dst");
45411318989SMichal Koutný 	if (!grps[1] || !grps[2] || !dom)
45511318989SMichal Koutný 		goto cleanup;
45611318989SMichal Koutný 
45711318989SMichal Koutný 	if (cg_create(dom))
45811318989SMichal Koutný 		goto cleanup;
45911318989SMichal Koutný 	if (cg_create(grps[1]))
46011318989SMichal Koutný 		goto cleanup;
46111318989SMichal Koutný 	if (cg_create(grps[2]))
46211318989SMichal Koutný 		goto cleanup;
46311318989SMichal Koutný 
46411318989SMichal Koutný 	if (cg_write(grps[1], "cgroup.type", "threaded"))
46511318989SMichal Koutný 		goto cleanup;
46611318989SMichal Koutný 	if (cg_write(grps[2], "cgroup.type", "threaded"))
46711318989SMichal Koutný 		goto cleanup;
46811318989SMichal Koutný 
46911318989SMichal Koutný 	if (cg_enter_current(grps[1]))
47011318989SMichal Koutný 		goto cleanup;
47111318989SMichal Koutný 
47211318989SMichal Koutný 	if (pthread_create(&thr, NULL, migrating_thread_fn, grps))
47311318989SMichal Koutný 		goto cleanup;
47411318989SMichal Koutný 
47511318989SMichal Koutný 	if (pthread_join(thr, &retval))
47611318989SMichal Koutný 		goto cleanup;
47711318989SMichal Koutný 
47811318989SMichal Koutný 	if (retval)
47911318989SMichal Koutný 		goto cleanup;
48011318989SMichal Koutný 
48111318989SMichal Koutný 	snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0]));
48211318989SMichal Koutný 	if (proc_read_strstr(0, 1, "cgroup", line))
48311318989SMichal Koutný 		goto cleanup;
48411318989SMichal Koutný 
48511318989SMichal Koutný 	ret = KSFT_PASS;
48611318989SMichal Koutný 
48711318989SMichal Koutný cleanup:
48811318989SMichal Koutný 	cg_enter_current(root);
48911318989SMichal Koutný 	if (grps[2])
49011318989SMichal Koutný 		cg_destroy(grps[2]);
49111318989SMichal Koutný 	if (grps[1])
49211318989SMichal Koutný 		cg_destroy(grps[1]);
49311318989SMichal Koutný 	if (dom)
49411318989SMichal Koutný 		cg_destroy(dom);
49511318989SMichal Koutný 	free(grps[2]);
49611318989SMichal Koutný 	free(grps[1]);
49711318989SMichal Koutný 	free(dom);
49811318989SMichal Koutný 	return ret;
49911318989SMichal Koutný }
50011318989SMichal Koutný 
501d863cb03SClaudio #define T(x) { x, #x }
502d863cb03SClaudio struct corecg_test {
503d863cb03SClaudio 	int (*fn)(const char *root);
504d863cb03SClaudio 	const char *name;
505d863cb03SClaudio } tests[] = {
506d863cb03SClaudio 	T(test_cgcore_internal_process_constraint),
507d863cb03SClaudio 	T(test_cgcore_top_down_constraint_enable),
508d863cb03SClaudio 	T(test_cgcore_top_down_constraint_disable),
509d863cb03SClaudio 	T(test_cgcore_no_internal_process_constraint_on_threads),
510d863cb03SClaudio 	T(test_cgcore_parent_becomes_threaded),
511d863cb03SClaudio 	T(test_cgcore_invalid_domain),
512d863cb03SClaudio 	T(test_cgcore_populated),
51311318989SMichal Koutný 	T(test_cgcore_proc_migration),
51411318989SMichal Koutný 	T(test_cgcore_thread_migration),
515d863cb03SClaudio };
516d863cb03SClaudio #undef T
517d863cb03SClaudio 
518d863cb03SClaudio int main(int argc, char *argv[])
519d863cb03SClaudio {
520d863cb03SClaudio 	char root[PATH_MAX];
521d863cb03SClaudio 	int i, ret = EXIT_SUCCESS;
522d863cb03SClaudio 
523d863cb03SClaudio 	if (cg_find_unified_root(root, sizeof(root)))
524d863cb03SClaudio 		ksft_exit_skip("cgroup v2 isn't mounted\n");
52500e38a5dSAlex Shi 
52600e38a5dSAlex Shi 	if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
52700e38a5dSAlex Shi 		if (cg_write(root, "cgroup.subtree_control", "+memory"))
52800e38a5dSAlex Shi 			ksft_exit_skip("Failed to set memory controller\n");
52900e38a5dSAlex Shi 
530d863cb03SClaudio 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
531d863cb03SClaudio 		switch (tests[i].fn(root)) {
532d863cb03SClaudio 		case KSFT_PASS:
533d863cb03SClaudio 			ksft_test_result_pass("%s\n", tests[i].name);
534d863cb03SClaudio 			break;
535d863cb03SClaudio 		case KSFT_SKIP:
536d863cb03SClaudio 			ksft_test_result_skip("%s\n", tests[i].name);
537d863cb03SClaudio 			break;
538d863cb03SClaudio 		default:
539d863cb03SClaudio 			ret = EXIT_FAILURE;
540d863cb03SClaudio 			ksft_test_result_fail("%s\n", tests[i].name);
541d863cb03SClaudio 			break;
542d863cb03SClaudio 		}
543d863cb03SClaudio 	}
544d863cb03SClaudio 
545d863cb03SClaudio 	return ret;
546d863cb03SClaudio }
547