184092dbcSRoman Gushchin /* SPDX-License-Identifier: GPL-2.0 */
284092dbcSRoman Gushchin #define _GNU_SOURCE
384092dbcSRoman Gushchin 
484092dbcSRoman Gushchin #include <linux/limits.h>
5a987785dSJay Kamat #include <linux/oom.h>
684092dbcSRoman Gushchin #include <fcntl.h>
784092dbcSRoman Gushchin #include <stdio.h>
884092dbcSRoman Gushchin #include <stdlib.h>
984092dbcSRoman Gushchin #include <string.h>
1084092dbcSRoman Gushchin #include <sys/stat.h>
1184092dbcSRoman Gushchin #include <sys/types.h>
1284092dbcSRoman Gushchin #include <unistd.h>
135f8f0193SMike Rapoport #include <sys/socket.h>
145f8f0193SMike Rapoport #include <sys/wait.h>
155f8f0193SMike Rapoport #include <arpa/inet.h>
165f8f0193SMike Rapoport #include <netinet/in.h>
175f8f0193SMike Rapoport #include <netdb.h>
185f8f0193SMike Rapoport #include <errno.h>
1984092dbcSRoman Gushchin 
2084092dbcSRoman Gushchin #include "../kselftest.h"
2184092dbcSRoman Gushchin #include "cgroup_util.h"
2284092dbcSRoman Gushchin 
2384092dbcSRoman Gushchin /*
2484092dbcSRoman Gushchin  * This test creates two nested cgroups with and without enabling
2584092dbcSRoman Gushchin  * the memory controller.
2684092dbcSRoman Gushchin  */
2784092dbcSRoman Gushchin static int test_memcg_subtree_control(const char *root)
2884092dbcSRoman Gushchin {
29e14d314cSRoman Gushchin 	char *parent, *child, *parent2 = NULL, *child2 = NULL;
3084092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
3184092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
3284092dbcSRoman Gushchin 
3384092dbcSRoman Gushchin 	/* Create two nested cgroups with the memory controller enabled */
3484092dbcSRoman Gushchin 	parent = cg_name(root, "memcg_test_0");
3584092dbcSRoman Gushchin 	child = cg_name(root, "memcg_test_0/memcg_test_1");
3684092dbcSRoman Gushchin 	if (!parent || !child)
37e14d314cSRoman Gushchin 		goto cleanup_free;
3884092dbcSRoman Gushchin 
3984092dbcSRoman Gushchin 	if (cg_create(parent))
40e14d314cSRoman Gushchin 		goto cleanup_free;
4184092dbcSRoman Gushchin 
4284092dbcSRoman Gushchin 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
43e14d314cSRoman Gushchin 		goto cleanup_parent;
4484092dbcSRoman Gushchin 
4584092dbcSRoman Gushchin 	if (cg_create(child))
46e14d314cSRoman Gushchin 		goto cleanup_parent;
4784092dbcSRoman Gushchin 
4884092dbcSRoman Gushchin 	if (cg_read_strstr(child, "cgroup.controllers", "memory"))
49e14d314cSRoman Gushchin 		goto cleanup_child;
5084092dbcSRoman Gushchin 
5184092dbcSRoman Gushchin 	/* Create two nested cgroups without enabling memory controller */
5284092dbcSRoman Gushchin 	parent2 = cg_name(root, "memcg_test_1");
5384092dbcSRoman Gushchin 	child2 = cg_name(root, "memcg_test_1/memcg_test_1");
5484092dbcSRoman Gushchin 	if (!parent2 || !child2)
55e14d314cSRoman Gushchin 		goto cleanup_free2;
5684092dbcSRoman Gushchin 
5784092dbcSRoman Gushchin 	if (cg_create(parent2))
58e14d314cSRoman Gushchin 		goto cleanup_free2;
5984092dbcSRoman Gushchin 
6084092dbcSRoman Gushchin 	if (cg_create(child2))
61e14d314cSRoman Gushchin 		goto cleanup_parent2;
6284092dbcSRoman Gushchin 
6384092dbcSRoman Gushchin 	if (cg_read(child2, "cgroup.controllers", buf, sizeof(buf)))
64e14d314cSRoman Gushchin 		goto cleanup_all;
6584092dbcSRoman Gushchin 
6684092dbcSRoman Gushchin 	if (!cg_read_strstr(child2, "cgroup.controllers", "memory"))
67e14d314cSRoman Gushchin 		goto cleanup_all;
6884092dbcSRoman Gushchin 
6984092dbcSRoman Gushchin 	ret = KSFT_PASS;
7084092dbcSRoman Gushchin 
71e14d314cSRoman Gushchin cleanup_all:
7284092dbcSRoman Gushchin 	cg_destroy(child2);
73e14d314cSRoman Gushchin cleanup_parent2:
7484092dbcSRoman Gushchin 	cg_destroy(parent2);
75e14d314cSRoman Gushchin cleanup_free2:
7684092dbcSRoman Gushchin 	free(parent2);
7784092dbcSRoman Gushchin 	free(child2);
78e14d314cSRoman Gushchin cleanup_child:
79e14d314cSRoman Gushchin 	cg_destroy(child);
80e14d314cSRoman Gushchin cleanup_parent:
81e14d314cSRoman Gushchin 	cg_destroy(parent);
82e14d314cSRoman Gushchin cleanup_free:
83e14d314cSRoman Gushchin 	free(parent);
84e14d314cSRoman Gushchin 	free(child);
8584092dbcSRoman Gushchin 
8684092dbcSRoman Gushchin 	return ret;
8784092dbcSRoman Gushchin }
8884092dbcSRoman Gushchin 
8984092dbcSRoman Gushchin static int alloc_anon_50M_check(const char *cgroup, void *arg)
9084092dbcSRoman Gushchin {
9184092dbcSRoman Gushchin 	size_t size = MB(50);
9284092dbcSRoman Gushchin 	char *buf, *ptr;
9384092dbcSRoman Gushchin 	long anon, current;
9484092dbcSRoman Gushchin 	int ret = -1;
9584092dbcSRoman Gushchin 
9684092dbcSRoman Gushchin 	buf = malloc(size);
9784092dbcSRoman Gushchin 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
9884092dbcSRoman Gushchin 		*ptr = 0;
9984092dbcSRoman Gushchin 
10084092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
10184092dbcSRoman Gushchin 	if (current < size)
10284092dbcSRoman Gushchin 		goto cleanup;
10384092dbcSRoman Gushchin 
10484092dbcSRoman Gushchin 	if (!values_close(size, current, 3))
10584092dbcSRoman Gushchin 		goto cleanup;
10684092dbcSRoman Gushchin 
10784092dbcSRoman Gushchin 	anon = cg_read_key_long(cgroup, "memory.stat", "anon ");
10884092dbcSRoman Gushchin 	if (anon < 0)
10984092dbcSRoman Gushchin 		goto cleanup;
11084092dbcSRoman Gushchin 
11184092dbcSRoman Gushchin 	if (!values_close(anon, current, 3))
11284092dbcSRoman Gushchin 		goto cleanup;
11384092dbcSRoman Gushchin 
11484092dbcSRoman Gushchin 	ret = 0;
11584092dbcSRoman Gushchin cleanup:
11684092dbcSRoman Gushchin 	free(buf);
11784092dbcSRoman Gushchin 	return ret;
11884092dbcSRoman Gushchin }
11984092dbcSRoman Gushchin 
12084092dbcSRoman Gushchin static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
12184092dbcSRoman Gushchin {
12284092dbcSRoman Gushchin 	size_t size = MB(50);
12384092dbcSRoman Gushchin 	int ret = -1;
12484092dbcSRoman Gushchin 	long current, file;
12584092dbcSRoman Gushchin 	int fd;
12684092dbcSRoman Gushchin 
12784092dbcSRoman Gushchin 	fd = get_temp_fd();
12884092dbcSRoman Gushchin 	if (fd < 0)
12984092dbcSRoman Gushchin 		return -1;
13084092dbcSRoman Gushchin 
13184092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
13284092dbcSRoman Gushchin 		goto cleanup;
13384092dbcSRoman Gushchin 
13484092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
13584092dbcSRoman Gushchin 	if (current < size)
13684092dbcSRoman Gushchin 		goto cleanup;
13784092dbcSRoman Gushchin 
13884092dbcSRoman Gushchin 	file = cg_read_key_long(cgroup, "memory.stat", "file ");
13984092dbcSRoman Gushchin 	if (file < 0)
14084092dbcSRoman Gushchin 		goto cleanup;
14184092dbcSRoman Gushchin 
14284092dbcSRoman Gushchin 	if (!values_close(file, current, 10))
14384092dbcSRoman Gushchin 		goto cleanup;
14484092dbcSRoman Gushchin 
14584092dbcSRoman Gushchin 	ret = 0;
14684092dbcSRoman Gushchin 
14784092dbcSRoman Gushchin cleanup:
14884092dbcSRoman Gushchin 	close(fd);
14984092dbcSRoman Gushchin 	return ret;
15084092dbcSRoman Gushchin }
15184092dbcSRoman Gushchin 
15284092dbcSRoman Gushchin /*
15384092dbcSRoman Gushchin  * This test create a memory cgroup, allocates
15484092dbcSRoman Gushchin  * some anonymous memory and some pagecache
15584092dbcSRoman Gushchin  * and check memory.current and some memory.stat values.
15684092dbcSRoman Gushchin  */
15784092dbcSRoman Gushchin static int test_memcg_current(const char *root)
15884092dbcSRoman Gushchin {
15984092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
16084092dbcSRoman Gushchin 	long current;
16184092dbcSRoman Gushchin 	char *memcg;
16284092dbcSRoman Gushchin 
16384092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
16484092dbcSRoman Gushchin 	if (!memcg)
16584092dbcSRoman Gushchin 		goto cleanup;
16684092dbcSRoman Gushchin 
16784092dbcSRoman Gushchin 	if (cg_create(memcg))
16884092dbcSRoman Gushchin 		goto cleanup;
16984092dbcSRoman Gushchin 
17084092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
17184092dbcSRoman Gushchin 	if (current != 0)
17284092dbcSRoman Gushchin 		goto cleanup;
17384092dbcSRoman Gushchin 
17484092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_anon_50M_check, NULL))
17584092dbcSRoman Gushchin 		goto cleanup;
17684092dbcSRoman Gushchin 
17784092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_50M_check, NULL))
17884092dbcSRoman Gushchin 		goto cleanup;
17984092dbcSRoman Gushchin 
18084092dbcSRoman Gushchin 	ret = KSFT_PASS;
18184092dbcSRoman Gushchin 
18284092dbcSRoman Gushchin cleanup:
18384092dbcSRoman Gushchin 	cg_destroy(memcg);
18484092dbcSRoman Gushchin 	free(memcg);
18584092dbcSRoman Gushchin 
18684092dbcSRoman Gushchin 	return ret;
18784092dbcSRoman Gushchin }
18884092dbcSRoman Gushchin 
18984092dbcSRoman Gushchin static int alloc_pagecache_50M(const char *cgroup, void *arg)
19084092dbcSRoman Gushchin {
19184092dbcSRoman Gushchin 	int fd = (long)arg;
19284092dbcSRoman Gushchin 
19384092dbcSRoman Gushchin 	return alloc_pagecache(fd, MB(50));
19484092dbcSRoman Gushchin }
19584092dbcSRoman Gushchin 
19684092dbcSRoman Gushchin static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg)
19784092dbcSRoman Gushchin {
19884092dbcSRoman Gushchin 	int fd = (long)arg;
19984092dbcSRoman Gushchin 	int ppid = getppid();
20084092dbcSRoman Gushchin 
20184092dbcSRoman Gushchin 	if (alloc_pagecache(fd, MB(50)))
20284092dbcSRoman Gushchin 		return -1;
20384092dbcSRoman Gushchin 
20484092dbcSRoman Gushchin 	while (getppid() == ppid)
20584092dbcSRoman Gushchin 		sleep(1);
20684092dbcSRoman Gushchin 
20784092dbcSRoman Gushchin 	return 0;
20884092dbcSRoman Gushchin }
20984092dbcSRoman Gushchin 
210a987785dSJay Kamat static int alloc_anon_noexit(const char *cgroup, void *arg)
211a987785dSJay Kamat {
212a987785dSJay Kamat 	int ppid = getppid();
213a987785dSJay Kamat 
214a987785dSJay Kamat 	if (alloc_anon(cgroup, arg))
215a987785dSJay Kamat 		return -1;
216a987785dSJay Kamat 
217a987785dSJay Kamat 	while (getppid() == ppid)
218a987785dSJay Kamat 		sleep(1);
219a987785dSJay Kamat 
220a987785dSJay Kamat 	return 0;
221a987785dSJay Kamat }
222a987785dSJay Kamat 
223a987785dSJay Kamat /*
224a987785dSJay Kamat  * Wait until processes are killed asynchronously by the OOM killer
225a987785dSJay Kamat  * If we exceed a timeout, fail.
226a987785dSJay Kamat  */
227a987785dSJay Kamat static int cg_test_proc_killed(const char *cgroup)
228a987785dSJay Kamat {
229a987785dSJay Kamat 	int limit;
230a987785dSJay Kamat 
231a987785dSJay Kamat 	for (limit = 10; limit > 0; limit--) {
232a987785dSJay Kamat 		if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
233a987785dSJay Kamat 			return 0;
234a987785dSJay Kamat 
235a987785dSJay Kamat 		usleep(100000);
236a987785dSJay Kamat 	}
237a987785dSJay Kamat 	return -1;
238a987785dSJay Kamat }
239a987785dSJay Kamat 
24084092dbcSRoman Gushchin /*
24184092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
24284092dbcSRoman Gushchin  * A       memory.min = 50M,  memory.max = 200M
24384092dbcSRoman Gushchin  * A/B     memory.min = 50M,  memory.current = 50M
24484092dbcSRoman Gushchin  * A/B/C   memory.min = 75M,  memory.current = 50M
24584092dbcSRoman Gushchin  * A/B/D   memory.min = 25M,  memory.current = 50M
24684092dbcSRoman Gushchin  * A/B/E   memory.min = 500M, memory.current = 0
24784092dbcSRoman Gushchin  * A/B/F   memory.min = 0,    memory.current = 50M
24884092dbcSRoman Gushchin  *
24984092dbcSRoman Gushchin  * Usages are pagecache, but the test keeps a running
25084092dbcSRoman Gushchin  * process in every leaf cgroup.
25184092dbcSRoman Gushchin  * Then it creates A/G and creates a significant
25284092dbcSRoman Gushchin  * memory pressure in it.
25384092dbcSRoman Gushchin  *
25484092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
25584092dbcSRoman Gushchin  * A/B/C  memory.current ~= 33M
25684092dbcSRoman Gushchin  * A/B/D  memory.current ~= 17M
25784092dbcSRoman Gushchin  * A/B/E  memory.current ~= 0
25884092dbcSRoman Gushchin  *
25984092dbcSRoman Gushchin  * After that it tries to allocate more than there is
26084092dbcSRoman Gushchin  * unprotected memory in A available, and checks
26184092dbcSRoman Gushchin  * checks that memory.min protects pagecache even
26284092dbcSRoman Gushchin  * in this case.
26384092dbcSRoman Gushchin  */
26484092dbcSRoman Gushchin static int test_memcg_min(const char *root)
26584092dbcSRoman Gushchin {
26684092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
26784092dbcSRoman Gushchin 	char *parent[3] = {NULL};
26884092dbcSRoman Gushchin 	char *children[4] = {NULL};
26984092dbcSRoman Gushchin 	long c[4];
27084092dbcSRoman Gushchin 	int i, attempts;
27184092dbcSRoman Gushchin 	int fd;
27284092dbcSRoman Gushchin 
27384092dbcSRoman Gushchin 	fd = get_temp_fd();
27484092dbcSRoman Gushchin 	if (fd < 0)
27584092dbcSRoman Gushchin 		goto cleanup;
27684092dbcSRoman Gushchin 
27784092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
27884092dbcSRoman Gushchin 	if (!parent[0])
27984092dbcSRoman Gushchin 		goto cleanup;
28084092dbcSRoman Gushchin 
28184092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
28284092dbcSRoman Gushchin 	if (!parent[1])
28384092dbcSRoman Gushchin 		goto cleanup;
28484092dbcSRoman Gushchin 
28584092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
28684092dbcSRoman Gushchin 	if (!parent[2])
28784092dbcSRoman Gushchin 		goto cleanup;
28884092dbcSRoman Gushchin 
28984092dbcSRoman Gushchin 	if (cg_create(parent[0]))
29084092dbcSRoman Gushchin 		goto cleanup;
29184092dbcSRoman Gushchin 
29284092dbcSRoman Gushchin 	if (cg_read_long(parent[0], "memory.min")) {
29384092dbcSRoman Gushchin 		ret = KSFT_SKIP;
29484092dbcSRoman Gushchin 		goto cleanup;
29584092dbcSRoman Gushchin 	}
29684092dbcSRoman Gushchin 
29784092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
29884092dbcSRoman Gushchin 		goto cleanup;
29984092dbcSRoman Gushchin 
30084092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
30184092dbcSRoman Gushchin 		goto cleanup;
30284092dbcSRoman Gushchin 
30384092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
30484092dbcSRoman Gushchin 		goto cleanup;
30584092dbcSRoman Gushchin 
30684092dbcSRoman Gushchin 	if (cg_create(parent[1]))
30784092dbcSRoman Gushchin 		goto cleanup;
30884092dbcSRoman Gushchin 
30984092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
31084092dbcSRoman Gushchin 		goto cleanup;
31184092dbcSRoman Gushchin 
31284092dbcSRoman Gushchin 	if (cg_create(parent[2]))
31384092dbcSRoman Gushchin 		goto cleanup;
31484092dbcSRoman Gushchin 
31584092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
31684092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
31784092dbcSRoman Gushchin 		if (!children[i])
31884092dbcSRoman Gushchin 			goto cleanup;
31984092dbcSRoman Gushchin 
32084092dbcSRoman Gushchin 		if (cg_create(children[i]))
32184092dbcSRoman Gushchin 			goto cleanup;
32284092dbcSRoman Gushchin 
32384092dbcSRoman Gushchin 		if (i == 2)
32484092dbcSRoman Gushchin 			continue;
32584092dbcSRoman Gushchin 
32684092dbcSRoman Gushchin 		cg_run_nowait(children[i], alloc_pagecache_50M_noexit,
32784092dbcSRoman Gushchin 			      (void *)(long)fd);
32884092dbcSRoman Gushchin 	}
32984092dbcSRoman Gushchin 
33084092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.min", "50M"))
33184092dbcSRoman Gushchin 		goto cleanup;
33284092dbcSRoman Gushchin 	if (cg_write(parent[1], "memory.min", "50M"))
33384092dbcSRoman Gushchin 		goto cleanup;
33484092dbcSRoman Gushchin 	if (cg_write(children[0], "memory.min", "75M"))
33584092dbcSRoman Gushchin 		goto cleanup;
33684092dbcSRoman Gushchin 	if (cg_write(children[1], "memory.min", "25M"))
33784092dbcSRoman Gushchin 		goto cleanup;
33884092dbcSRoman Gushchin 	if (cg_write(children[2], "memory.min", "500M"))
33984092dbcSRoman Gushchin 		goto cleanup;
34084092dbcSRoman Gushchin 	if (cg_write(children[3], "memory.min", "0"))
34184092dbcSRoman Gushchin 		goto cleanup;
34284092dbcSRoman Gushchin 
34384092dbcSRoman Gushchin 	attempts = 0;
34484092dbcSRoman Gushchin 	while (!values_close(cg_read_long(parent[1], "memory.current"),
34584092dbcSRoman Gushchin 			     MB(150), 3)) {
34684092dbcSRoman Gushchin 		if (attempts++ > 5)
34784092dbcSRoman Gushchin 			break;
34884092dbcSRoman Gushchin 		sleep(1);
34984092dbcSRoman Gushchin 	}
35084092dbcSRoman Gushchin 
35184092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
35284092dbcSRoman Gushchin 		goto cleanup;
35384092dbcSRoman Gushchin 
35484092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
35584092dbcSRoman Gushchin 		goto cleanup;
35684092dbcSRoman Gushchin 
35784092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
35884092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
35984092dbcSRoman Gushchin 
36084092dbcSRoman Gushchin 	if (!values_close(c[0], MB(33), 10))
36184092dbcSRoman Gushchin 		goto cleanup;
36284092dbcSRoman Gushchin 
36384092dbcSRoman Gushchin 	if (!values_close(c[1], MB(17), 10))
36484092dbcSRoman Gushchin 		goto cleanup;
36584092dbcSRoman Gushchin 
36684092dbcSRoman Gushchin 	if (!values_close(c[2], 0, 1))
36784092dbcSRoman Gushchin 		goto cleanup;
36884092dbcSRoman Gushchin 
36984092dbcSRoman Gushchin 	if (!cg_run(parent[2], alloc_anon, (void *)MB(170)))
37084092dbcSRoman Gushchin 		goto cleanup;
37184092dbcSRoman Gushchin 
37284092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
37384092dbcSRoman Gushchin 		goto cleanup;
37484092dbcSRoman Gushchin 
37584092dbcSRoman Gushchin 	ret = KSFT_PASS;
37684092dbcSRoman Gushchin 
37784092dbcSRoman Gushchin cleanup:
37884092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
37984092dbcSRoman Gushchin 		if (!children[i])
38084092dbcSRoman Gushchin 			continue;
38184092dbcSRoman Gushchin 
38284092dbcSRoman Gushchin 		cg_destroy(children[i]);
38384092dbcSRoman Gushchin 		free(children[i]);
38484092dbcSRoman Gushchin 	}
38584092dbcSRoman Gushchin 
38684092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
38784092dbcSRoman Gushchin 		if (!parent[i])
38884092dbcSRoman Gushchin 			continue;
38984092dbcSRoman Gushchin 
39084092dbcSRoman Gushchin 		cg_destroy(parent[i]);
39184092dbcSRoman Gushchin 		free(parent[i]);
39284092dbcSRoman Gushchin 	}
39384092dbcSRoman Gushchin 	close(fd);
39484092dbcSRoman Gushchin 	return ret;
39584092dbcSRoman Gushchin }
39684092dbcSRoman Gushchin 
39784092dbcSRoman Gushchin /*
39884092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
39984092dbcSRoman Gushchin  * A       memory.low = 50M,  memory.max = 200M
40084092dbcSRoman Gushchin  * A/B     memory.low = 50M,  memory.current = 50M
40184092dbcSRoman Gushchin  * A/B/C   memory.low = 75M,  memory.current = 50M
40284092dbcSRoman Gushchin  * A/B/D   memory.low = 25M,  memory.current = 50M
40384092dbcSRoman Gushchin  * A/B/E   memory.low = 500M, memory.current = 0
40484092dbcSRoman Gushchin  * A/B/F   memory.low = 0,    memory.current = 50M
40584092dbcSRoman Gushchin  *
40684092dbcSRoman Gushchin  * Usages are pagecache.
40784092dbcSRoman Gushchin  * Then it creates A/G an creates a significant
40884092dbcSRoman Gushchin  * memory pressure in it.
40984092dbcSRoman Gushchin  *
41084092dbcSRoman Gushchin  * Then it checks actual memory usages and expects that:
41184092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
41284092dbcSRoman Gushchin  * A/B/   memory.current ~= 33M
41384092dbcSRoman Gushchin  * A/B/D  memory.current ~= 17M
41484092dbcSRoman Gushchin  * A/B/E  memory.current ~= 0
41584092dbcSRoman Gushchin  *
41684092dbcSRoman Gushchin  * After that it tries to allocate more than there is
41784092dbcSRoman Gushchin  * unprotected memory in A available,
41884092dbcSRoman Gushchin  * and checks low and oom events in memory.events.
41984092dbcSRoman Gushchin  */
42084092dbcSRoman Gushchin static int test_memcg_low(const char *root)
42184092dbcSRoman Gushchin {
42284092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
42384092dbcSRoman Gushchin 	char *parent[3] = {NULL};
42484092dbcSRoman Gushchin 	char *children[4] = {NULL};
42584092dbcSRoman Gushchin 	long low, oom;
42684092dbcSRoman Gushchin 	long c[4];
42784092dbcSRoman Gushchin 	int i;
42884092dbcSRoman Gushchin 	int fd;
42984092dbcSRoman Gushchin 
43084092dbcSRoman Gushchin 	fd = get_temp_fd();
43184092dbcSRoman Gushchin 	if (fd < 0)
43284092dbcSRoman Gushchin 		goto cleanup;
43384092dbcSRoman Gushchin 
43484092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
43584092dbcSRoman Gushchin 	if (!parent[0])
43684092dbcSRoman Gushchin 		goto cleanup;
43784092dbcSRoman Gushchin 
43884092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
43984092dbcSRoman Gushchin 	if (!parent[1])
44084092dbcSRoman Gushchin 		goto cleanup;
44184092dbcSRoman Gushchin 
44284092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
44384092dbcSRoman Gushchin 	if (!parent[2])
44484092dbcSRoman Gushchin 		goto cleanup;
44584092dbcSRoman Gushchin 
44684092dbcSRoman Gushchin 	if (cg_create(parent[0]))
44784092dbcSRoman Gushchin 		goto cleanup;
44884092dbcSRoman Gushchin 
44984092dbcSRoman Gushchin 	if (cg_read_long(parent[0], "memory.low"))
45084092dbcSRoman Gushchin 		goto cleanup;
45184092dbcSRoman Gushchin 
45284092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
45384092dbcSRoman Gushchin 		goto cleanup;
45484092dbcSRoman Gushchin 
45584092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
45684092dbcSRoman Gushchin 		goto cleanup;
45784092dbcSRoman Gushchin 
45884092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
45984092dbcSRoman Gushchin 		goto cleanup;
46084092dbcSRoman Gushchin 
46184092dbcSRoman Gushchin 	if (cg_create(parent[1]))
46284092dbcSRoman Gushchin 		goto cleanup;
46384092dbcSRoman Gushchin 
46484092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
46584092dbcSRoman Gushchin 		goto cleanup;
46684092dbcSRoman Gushchin 
46784092dbcSRoman Gushchin 	if (cg_create(parent[2]))
46884092dbcSRoman Gushchin 		goto cleanup;
46984092dbcSRoman Gushchin 
47084092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
47184092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
47284092dbcSRoman Gushchin 		if (!children[i])
47384092dbcSRoman Gushchin 			goto cleanup;
47484092dbcSRoman Gushchin 
47584092dbcSRoman Gushchin 		if (cg_create(children[i]))
47684092dbcSRoman Gushchin 			goto cleanup;
47784092dbcSRoman Gushchin 
47884092dbcSRoman Gushchin 		if (i == 2)
47984092dbcSRoman Gushchin 			continue;
48084092dbcSRoman Gushchin 
48184092dbcSRoman Gushchin 		if (cg_run(children[i], alloc_pagecache_50M, (void *)(long)fd))
48284092dbcSRoman Gushchin 			goto cleanup;
48384092dbcSRoman Gushchin 	}
48484092dbcSRoman Gushchin 
48584092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.low", "50M"))
48684092dbcSRoman Gushchin 		goto cleanup;
48784092dbcSRoman Gushchin 	if (cg_write(parent[1], "memory.low", "50M"))
48884092dbcSRoman Gushchin 		goto cleanup;
48984092dbcSRoman Gushchin 	if (cg_write(children[0], "memory.low", "75M"))
49084092dbcSRoman Gushchin 		goto cleanup;
49184092dbcSRoman Gushchin 	if (cg_write(children[1], "memory.low", "25M"))
49284092dbcSRoman Gushchin 		goto cleanup;
49384092dbcSRoman Gushchin 	if (cg_write(children[2], "memory.low", "500M"))
49484092dbcSRoman Gushchin 		goto cleanup;
49584092dbcSRoman Gushchin 	if (cg_write(children[3], "memory.low", "0"))
49684092dbcSRoman Gushchin 		goto cleanup;
49784092dbcSRoman Gushchin 
49884092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
49984092dbcSRoman Gushchin 		goto cleanup;
50084092dbcSRoman Gushchin 
50184092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
50284092dbcSRoman Gushchin 		goto cleanup;
50384092dbcSRoman Gushchin 
50484092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
50584092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
50684092dbcSRoman Gushchin 
50784092dbcSRoman Gushchin 	if (!values_close(c[0], MB(33), 10))
50884092dbcSRoman Gushchin 		goto cleanup;
50984092dbcSRoman Gushchin 
51084092dbcSRoman Gushchin 	if (!values_close(c[1], MB(17), 10))
51184092dbcSRoman Gushchin 		goto cleanup;
51284092dbcSRoman Gushchin 
51384092dbcSRoman Gushchin 	if (!values_close(c[2], 0, 1))
51484092dbcSRoman Gushchin 		goto cleanup;
51584092dbcSRoman Gushchin 
51684092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(166))) {
51784092dbcSRoman Gushchin 		fprintf(stderr,
51884092dbcSRoman Gushchin 			"memory.low prevents from allocating anon memory\n");
51984092dbcSRoman Gushchin 		goto cleanup;
52084092dbcSRoman Gushchin 	}
52184092dbcSRoman Gushchin 
52284092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
52384092dbcSRoman Gushchin 		oom = cg_read_key_long(children[i], "memory.events", "oom ");
52484092dbcSRoman Gushchin 		low = cg_read_key_long(children[i], "memory.events", "low ");
52584092dbcSRoman Gushchin 
52684092dbcSRoman Gushchin 		if (oom)
52784092dbcSRoman Gushchin 			goto cleanup;
52884092dbcSRoman Gushchin 		if (i < 2 && low <= 0)
52984092dbcSRoman Gushchin 			goto cleanup;
53084092dbcSRoman Gushchin 		if (i >= 2 && low)
53184092dbcSRoman Gushchin 			goto cleanup;
53284092dbcSRoman Gushchin 	}
53384092dbcSRoman Gushchin 
53484092dbcSRoman Gushchin 	ret = KSFT_PASS;
53584092dbcSRoman Gushchin 
53684092dbcSRoman Gushchin cleanup:
53784092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
53884092dbcSRoman Gushchin 		if (!children[i])
53984092dbcSRoman Gushchin 			continue;
54084092dbcSRoman Gushchin 
54184092dbcSRoman Gushchin 		cg_destroy(children[i]);
54284092dbcSRoman Gushchin 		free(children[i]);
54384092dbcSRoman Gushchin 	}
54484092dbcSRoman Gushchin 
54584092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
54684092dbcSRoman Gushchin 		if (!parent[i])
54784092dbcSRoman Gushchin 			continue;
54884092dbcSRoman Gushchin 
54984092dbcSRoman Gushchin 		cg_destroy(parent[i]);
55084092dbcSRoman Gushchin 		free(parent[i]);
55184092dbcSRoman Gushchin 	}
55284092dbcSRoman Gushchin 	close(fd);
55384092dbcSRoman Gushchin 	return ret;
55484092dbcSRoman Gushchin }
55584092dbcSRoman Gushchin 
55684092dbcSRoman Gushchin static int alloc_pagecache_max_30M(const char *cgroup, void *arg)
55784092dbcSRoman Gushchin {
55884092dbcSRoman Gushchin 	size_t size = MB(50);
55984092dbcSRoman Gushchin 	int ret = -1;
56084092dbcSRoman Gushchin 	long current;
56184092dbcSRoman Gushchin 	int fd;
56284092dbcSRoman Gushchin 
56384092dbcSRoman Gushchin 	fd = get_temp_fd();
56484092dbcSRoman Gushchin 	if (fd < 0)
56584092dbcSRoman Gushchin 		return -1;
56684092dbcSRoman Gushchin 
56784092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
56884092dbcSRoman Gushchin 		goto cleanup;
56984092dbcSRoman Gushchin 
57084092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
57184092dbcSRoman Gushchin 	if (current <= MB(29) || current > MB(30))
57284092dbcSRoman Gushchin 		goto cleanup;
57384092dbcSRoman Gushchin 
57484092dbcSRoman Gushchin 	ret = 0;
57584092dbcSRoman Gushchin 
57684092dbcSRoman Gushchin cleanup:
57784092dbcSRoman Gushchin 	close(fd);
57884092dbcSRoman Gushchin 	return ret;
57984092dbcSRoman Gushchin 
58084092dbcSRoman Gushchin }
58184092dbcSRoman Gushchin 
58284092dbcSRoman Gushchin /*
58384092dbcSRoman Gushchin  * This test checks that memory.high limits the amount of
58484092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
58584092dbcSRoman Gushchin  * or pagecache.
58684092dbcSRoman Gushchin  */
58784092dbcSRoman Gushchin static int test_memcg_high(const char *root)
58884092dbcSRoman Gushchin {
58984092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
59084092dbcSRoman Gushchin 	char *memcg;
59184092dbcSRoman Gushchin 	long high;
59284092dbcSRoman Gushchin 
59384092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
59484092dbcSRoman Gushchin 	if (!memcg)
59584092dbcSRoman Gushchin 		goto cleanup;
59684092dbcSRoman Gushchin 
59784092dbcSRoman Gushchin 	if (cg_create(memcg))
59884092dbcSRoman Gushchin 		goto cleanup;
59984092dbcSRoman Gushchin 
60084092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.high", "max\n"))
60184092dbcSRoman Gushchin 		goto cleanup;
60284092dbcSRoman Gushchin 
60384092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
60484092dbcSRoman Gushchin 		goto cleanup;
60584092dbcSRoman Gushchin 
60684092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.high", "30M"))
60784092dbcSRoman Gushchin 		goto cleanup;
60884092dbcSRoman Gushchin 
60984092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_anon, (void *)MB(100)))
61084092dbcSRoman Gushchin 		goto cleanup;
61184092dbcSRoman Gushchin 
61284092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_pagecache_50M_check, NULL))
61384092dbcSRoman Gushchin 		goto cleanup;
61484092dbcSRoman Gushchin 
61584092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
61684092dbcSRoman Gushchin 		goto cleanup;
61784092dbcSRoman Gushchin 
61884092dbcSRoman Gushchin 	high = cg_read_key_long(memcg, "memory.events", "high ");
61984092dbcSRoman Gushchin 	if (high <= 0)
62084092dbcSRoman Gushchin 		goto cleanup;
62184092dbcSRoman Gushchin 
62284092dbcSRoman Gushchin 	ret = KSFT_PASS;
62384092dbcSRoman Gushchin 
62484092dbcSRoman Gushchin cleanup:
62584092dbcSRoman Gushchin 	cg_destroy(memcg);
62684092dbcSRoman Gushchin 	free(memcg);
62784092dbcSRoman Gushchin 
62884092dbcSRoman Gushchin 	return ret;
62984092dbcSRoman Gushchin }
63084092dbcSRoman Gushchin 
63184092dbcSRoman Gushchin /*
63284092dbcSRoman Gushchin  * This test checks that memory.max limits the amount of
63384092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
63484092dbcSRoman Gushchin  * or pagecache.
63584092dbcSRoman Gushchin  */
63684092dbcSRoman Gushchin static int test_memcg_max(const char *root)
63784092dbcSRoman Gushchin {
63884092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
63984092dbcSRoman Gushchin 	char *memcg;
64084092dbcSRoman Gushchin 	long current, max;
64184092dbcSRoman Gushchin 
64284092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
64384092dbcSRoman Gushchin 	if (!memcg)
64484092dbcSRoman Gushchin 		goto cleanup;
64584092dbcSRoman Gushchin 
64684092dbcSRoman Gushchin 	if (cg_create(memcg))
64784092dbcSRoman Gushchin 		goto cleanup;
64884092dbcSRoman Gushchin 
64984092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
65084092dbcSRoman Gushchin 		goto cleanup;
65184092dbcSRoman Gushchin 
65284092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
65384092dbcSRoman Gushchin 		goto cleanup;
65484092dbcSRoman Gushchin 
65584092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
65684092dbcSRoman Gushchin 		goto cleanup;
65784092dbcSRoman Gushchin 
65884092dbcSRoman Gushchin 	/* Should be killed by OOM killer */
65984092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
66084092dbcSRoman Gushchin 		goto cleanup;
66184092dbcSRoman Gushchin 
66284092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
66384092dbcSRoman Gushchin 		goto cleanup;
66484092dbcSRoman Gushchin 
66584092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
66684092dbcSRoman Gushchin 	if (current > MB(30) || !current)
66784092dbcSRoman Gushchin 		goto cleanup;
66884092dbcSRoman Gushchin 
66984092dbcSRoman Gushchin 	max = cg_read_key_long(memcg, "memory.events", "max ");
67084092dbcSRoman Gushchin 	if (max <= 0)
67184092dbcSRoman Gushchin 		goto cleanup;
67284092dbcSRoman Gushchin 
67384092dbcSRoman Gushchin 	ret = KSFT_PASS;
67484092dbcSRoman Gushchin 
67584092dbcSRoman Gushchin cleanup:
67684092dbcSRoman Gushchin 	cg_destroy(memcg);
67784092dbcSRoman Gushchin 	free(memcg);
67884092dbcSRoman Gushchin 
67984092dbcSRoman Gushchin 	return ret;
68084092dbcSRoman Gushchin }
68184092dbcSRoman Gushchin 
682478b2784SMike Rapoport static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
683478b2784SMike Rapoport {
684478b2784SMike Rapoport 	long mem_max = (long)arg;
685478b2784SMike Rapoport 	size_t size = MB(50);
686478b2784SMike Rapoport 	char *buf, *ptr;
687478b2784SMike Rapoport 	long mem_current, swap_current;
688478b2784SMike Rapoport 	int ret = -1;
689478b2784SMike Rapoport 
690478b2784SMike Rapoport 	buf = malloc(size);
691478b2784SMike Rapoport 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
692478b2784SMike Rapoport 		*ptr = 0;
693478b2784SMike Rapoport 
694478b2784SMike Rapoport 	mem_current = cg_read_long(cgroup, "memory.current");
695478b2784SMike Rapoport 	if (!mem_current || !values_close(mem_current, mem_max, 3))
696478b2784SMike Rapoport 		goto cleanup;
697478b2784SMike Rapoport 
698478b2784SMike Rapoport 	swap_current = cg_read_long(cgroup, "memory.swap.current");
699478b2784SMike Rapoport 	if (!swap_current ||
700478b2784SMike Rapoport 	    !values_close(mem_current + swap_current, size, 3))
701478b2784SMike Rapoport 		goto cleanup;
702478b2784SMike Rapoport 
703478b2784SMike Rapoport 	ret = 0;
704478b2784SMike Rapoport cleanup:
705478b2784SMike Rapoport 	free(buf);
706478b2784SMike Rapoport 	return ret;
707478b2784SMike Rapoport }
708478b2784SMike Rapoport 
709478b2784SMike Rapoport /*
710478b2784SMike Rapoport  * This test checks that memory.swap.max limits the amount of
711478b2784SMike Rapoport  * anonymous memory which can be swapped out.
712478b2784SMike Rapoport  */
713478b2784SMike Rapoport static int test_memcg_swap_max(const char *root)
714478b2784SMike Rapoport {
715478b2784SMike Rapoport 	int ret = KSFT_FAIL;
716478b2784SMike Rapoport 	char *memcg;
717478b2784SMike Rapoport 	long max;
718478b2784SMike Rapoport 
719478b2784SMike Rapoport 	if (!is_swap_enabled())
720478b2784SMike Rapoport 		return KSFT_SKIP;
721478b2784SMike Rapoport 
722478b2784SMike Rapoport 	memcg = cg_name(root, "memcg_test");
723478b2784SMike Rapoport 	if (!memcg)
724478b2784SMike Rapoport 		goto cleanup;
725478b2784SMike Rapoport 
726478b2784SMike Rapoport 	if (cg_create(memcg))
727478b2784SMike Rapoport 		goto cleanup;
728478b2784SMike Rapoport 
729478b2784SMike Rapoport 	if (cg_read_long(memcg, "memory.swap.current")) {
730478b2784SMike Rapoport 		ret = KSFT_SKIP;
731478b2784SMike Rapoport 		goto cleanup;
732478b2784SMike Rapoport 	}
733478b2784SMike Rapoport 
734478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
735478b2784SMike Rapoport 		goto cleanup;
736478b2784SMike Rapoport 
737478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.swap.max", "max\n"))
738478b2784SMike Rapoport 		goto cleanup;
739478b2784SMike Rapoport 
740478b2784SMike Rapoport 	if (cg_write(memcg, "memory.swap.max", "30M"))
741478b2784SMike Rapoport 		goto cleanup;
742478b2784SMike Rapoport 
743478b2784SMike Rapoport 	if (cg_write(memcg, "memory.max", "30M"))
744478b2784SMike Rapoport 		goto cleanup;
745478b2784SMike Rapoport 
746478b2784SMike Rapoport 	/* Should be killed by OOM killer */
747478b2784SMike Rapoport 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
748478b2784SMike Rapoport 		goto cleanup;
749478b2784SMike Rapoport 
750478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
751478b2784SMike Rapoport 		goto cleanup;
752478b2784SMike Rapoport 
753478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
754478b2784SMike Rapoport 		goto cleanup;
755478b2784SMike Rapoport 
756478b2784SMike Rapoport 	if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30)))
757478b2784SMike Rapoport 		goto cleanup;
758478b2784SMike Rapoport 
759478b2784SMike Rapoport 	max = cg_read_key_long(memcg, "memory.events", "max ");
760478b2784SMike Rapoport 	if (max <= 0)
761478b2784SMike Rapoport 		goto cleanup;
762478b2784SMike Rapoport 
763478b2784SMike Rapoport 	ret = KSFT_PASS;
764478b2784SMike Rapoport 
765478b2784SMike Rapoport cleanup:
766478b2784SMike Rapoport 	cg_destroy(memcg);
767478b2784SMike Rapoport 	free(memcg);
768478b2784SMike Rapoport 
769478b2784SMike Rapoport 	return ret;
770478b2784SMike Rapoport }
771478b2784SMike Rapoport 
77284092dbcSRoman Gushchin /*
77384092dbcSRoman Gushchin  * This test disables swapping and tries to allocate anonymous memory
77484092dbcSRoman Gushchin  * up to OOM. Then it checks for oom and oom_kill events in
77584092dbcSRoman Gushchin  * memory.events.
77684092dbcSRoman Gushchin  */
77784092dbcSRoman Gushchin static int test_memcg_oom_events(const char *root)
77884092dbcSRoman Gushchin {
77984092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
78084092dbcSRoman Gushchin 	char *memcg;
78184092dbcSRoman Gushchin 
78284092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
78384092dbcSRoman Gushchin 	if (!memcg)
78484092dbcSRoman Gushchin 		goto cleanup;
78584092dbcSRoman Gushchin 
78684092dbcSRoman Gushchin 	if (cg_create(memcg))
78784092dbcSRoman Gushchin 		goto cleanup;
78884092dbcSRoman Gushchin 
78984092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
79084092dbcSRoman Gushchin 		goto cleanup;
79184092dbcSRoman Gushchin 
79284092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
79384092dbcSRoman Gushchin 		goto cleanup;
79484092dbcSRoman Gushchin 
79584092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
79684092dbcSRoman Gushchin 		goto cleanup;
79784092dbcSRoman Gushchin 
79884092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "cgroup.procs", ""))
79984092dbcSRoman Gushchin 		goto cleanup;
80084092dbcSRoman Gushchin 
80184092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
80284092dbcSRoman Gushchin 		goto cleanup;
80384092dbcSRoman Gushchin 
80484092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
80584092dbcSRoman Gushchin 		goto cleanup;
80684092dbcSRoman Gushchin 
80784092dbcSRoman Gushchin 	ret = KSFT_PASS;
80884092dbcSRoman Gushchin 
80984092dbcSRoman Gushchin cleanup:
81084092dbcSRoman Gushchin 	cg_destroy(memcg);
81184092dbcSRoman Gushchin 	free(memcg);
81284092dbcSRoman Gushchin 
81384092dbcSRoman Gushchin 	return ret;
81484092dbcSRoman Gushchin }
81584092dbcSRoman Gushchin 
8165f8f0193SMike Rapoport struct tcp_server_args {
8175f8f0193SMike Rapoport 	unsigned short port;
8185f8f0193SMike Rapoport 	int ctl[2];
8195f8f0193SMike Rapoport };
8205f8f0193SMike Rapoport 
8215f8f0193SMike Rapoport static int tcp_server(const char *cgroup, void *arg)
8225f8f0193SMike Rapoport {
8235f8f0193SMike Rapoport 	struct tcp_server_args *srv_args = arg;
8245f8f0193SMike Rapoport 	struct sockaddr_in6 saddr = { 0 };
8255f8f0193SMike Rapoport 	socklen_t slen = sizeof(saddr);
8265f8f0193SMike Rapoport 	int sk, client_sk, ctl_fd, yes = 1, ret = -1;
8275f8f0193SMike Rapoport 
8285f8f0193SMike Rapoport 	close(srv_args->ctl[0]);
8295f8f0193SMike Rapoport 	ctl_fd = srv_args->ctl[1];
8305f8f0193SMike Rapoport 
8315f8f0193SMike Rapoport 	saddr.sin6_family = AF_INET6;
8325f8f0193SMike Rapoport 	saddr.sin6_addr = in6addr_any;
8335f8f0193SMike Rapoport 	saddr.sin6_port = htons(srv_args->port);
8345f8f0193SMike Rapoport 
8355f8f0193SMike Rapoport 	sk = socket(AF_INET6, SOCK_STREAM, 0);
8365f8f0193SMike Rapoport 	if (sk < 0)
8375f8f0193SMike Rapoport 		return ret;
8385f8f0193SMike Rapoport 
8395f8f0193SMike Rapoport 	if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
8405f8f0193SMike Rapoport 		goto cleanup;
8415f8f0193SMike Rapoport 
8425f8f0193SMike Rapoport 	if (bind(sk, (struct sockaddr *)&saddr, slen)) {
8435f8f0193SMike Rapoport 		write(ctl_fd, &errno, sizeof(errno));
8445f8f0193SMike Rapoport 		goto cleanup;
8455f8f0193SMike Rapoport 	}
8465f8f0193SMike Rapoport 
8475f8f0193SMike Rapoport 	if (listen(sk, 1))
8485f8f0193SMike Rapoport 		goto cleanup;
8495f8f0193SMike Rapoport 
8505f8f0193SMike Rapoport 	ret = 0;
8515f8f0193SMike Rapoport 	if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) {
8525f8f0193SMike Rapoport 		ret = -1;
8535f8f0193SMike Rapoport 		goto cleanup;
8545f8f0193SMike Rapoport 	}
8555f8f0193SMike Rapoport 
8565f8f0193SMike Rapoport 	client_sk = accept(sk, NULL, NULL);
8575f8f0193SMike Rapoport 	if (client_sk < 0)
8585f8f0193SMike Rapoport 		goto cleanup;
8595f8f0193SMike Rapoport 
8605f8f0193SMike Rapoport 	ret = -1;
8615f8f0193SMike Rapoport 	for (;;) {
8625f8f0193SMike Rapoport 		uint8_t buf[0x100000];
8635f8f0193SMike Rapoport 
8645f8f0193SMike Rapoport 		if (write(client_sk, buf, sizeof(buf)) <= 0) {
8655f8f0193SMike Rapoport 			if (errno == ECONNRESET)
8665f8f0193SMike Rapoport 				ret = 0;
8675f8f0193SMike Rapoport 			break;
8685f8f0193SMike Rapoport 		}
8695f8f0193SMike Rapoport 	}
8705f8f0193SMike Rapoport 
8715f8f0193SMike Rapoport 	close(client_sk);
8725f8f0193SMike Rapoport 
8735f8f0193SMike Rapoport cleanup:
8745f8f0193SMike Rapoport 	close(sk);
8755f8f0193SMike Rapoport 	return ret;
8765f8f0193SMike Rapoport }
8775f8f0193SMike Rapoport 
8785f8f0193SMike Rapoport static int tcp_client(const char *cgroup, unsigned short port)
8795f8f0193SMike Rapoport {
8805f8f0193SMike Rapoport 	const char server[] = "localhost";
8815f8f0193SMike Rapoport 	struct addrinfo *ai;
8825f8f0193SMike Rapoport 	char servport[6];
8835f8f0193SMike Rapoport 	int retries = 0x10; /* nice round number */
8845f8f0193SMike Rapoport 	int sk, ret;
8855f8f0193SMike Rapoport 
8865f8f0193SMike Rapoport 	snprintf(servport, sizeof(servport), "%hd", port);
8875f8f0193SMike Rapoport 	ret = getaddrinfo(server, servport, NULL, &ai);
8885f8f0193SMike Rapoport 	if (ret)
8895f8f0193SMike Rapoport 		return ret;
8905f8f0193SMike Rapoport 
8915f8f0193SMike Rapoport 	sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
8925f8f0193SMike Rapoport 	if (sk < 0)
8935f8f0193SMike Rapoport 		goto free_ainfo;
8945f8f0193SMike Rapoport 
8955f8f0193SMike Rapoport 	ret = connect(sk, ai->ai_addr, ai->ai_addrlen);
8965f8f0193SMike Rapoport 	if (ret < 0)
8975f8f0193SMike Rapoport 		goto close_sk;
8985f8f0193SMike Rapoport 
8995f8f0193SMike Rapoport 	ret = KSFT_FAIL;
9005f8f0193SMike Rapoport 	while (retries--) {
9015f8f0193SMike Rapoport 		uint8_t buf[0x100000];
9025f8f0193SMike Rapoport 		long current, sock;
9035f8f0193SMike Rapoport 
9045f8f0193SMike Rapoport 		if (read(sk, buf, sizeof(buf)) <= 0)
9055f8f0193SMike Rapoport 			goto close_sk;
9065f8f0193SMike Rapoport 
9075f8f0193SMike Rapoport 		current = cg_read_long(cgroup, "memory.current");
9085f8f0193SMike Rapoport 		sock = cg_read_key_long(cgroup, "memory.stat", "sock ");
9095f8f0193SMike Rapoport 
9105f8f0193SMike Rapoport 		if (current < 0 || sock < 0)
9115f8f0193SMike Rapoport 			goto close_sk;
9125f8f0193SMike Rapoport 
9135f8f0193SMike Rapoport 		if (current < sock)
9145f8f0193SMike Rapoport 			goto close_sk;
9155f8f0193SMike Rapoport 
9165f8f0193SMike Rapoport 		if (values_close(current, sock, 10)) {
9175f8f0193SMike Rapoport 			ret = KSFT_PASS;
9185f8f0193SMike Rapoport 			break;
9195f8f0193SMike Rapoport 		}
9205f8f0193SMike Rapoport 	}
9215f8f0193SMike Rapoport 
9225f8f0193SMike Rapoport close_sk:
9235f8f0193SMike Rapoport 	close(sk);
9245f8f0193SMike Rapoport free_ainfo:
9255f8f0193SMike Rapoport 	freeaddrinfo(ai);
9265f8f0193SMike Rapoport 	return ret;
9275f8f0193SMike Rapoport }
9285f8f0193SMike Rapoport 
9295f8f0193SMike Rapoport /*
9305f8f0193SMike Rapoport  * This test checks socket memory accounting.
9315f8f0193SMike Rapoport  * The test forks a TCP server listens on a random port between 1000
9325f8f0193SMike Rapoport  * and 61000. Once it gets a client connection, it starts writing to
9335f8f0193SMike Rapoport  * its socket.
9345f8f0193SMike Rapoport  * The TCP client interleaves reads from the socket with check whether
9355f8f0193SMike Rapoport  * memory.current and memory.stat.sock are similar.
9365f8f0193SMike Rapoport  */
9375f8f0193SMike Rapoport static int test_memcg_sock(const char *root)
9385f8f0193SMike Rapoport {
9395f8f0193SMike Rapoport 	int bind_retries = 5, ret = KSFT_FAIL, pid, err;
9405f8f0193SMike Rapoport 	unsigned short port;
9415f8f0193SMike Rapoport 	char *memcg;
9425f8f0193SMike Rapoport 
9435f8f0193SMike Rapoport 	memcg = cg_name(root, "memcg_test");
9445f8f0193SMike Rapoport 	if (!memcg)
9455f8f0193SMike Rapoport 		goto cleanup;
9465f8f0193SMike Rapoport 
9475f8f0193SMike Rapoport 	if (cg_create(memcg))
9485f8f0193SMike Rapoport 		goto cleanup;
9495f8f0193SMike Rapoport 
9505f8f0193SMike Rapoport 	while (bind_retries--) {
9515f8f0193SMike Rapoport 		struct tcp_server_args args;
9525f8f0193SMike Rapoport 
9535f8f0193SMike Rapoport 		if (pipe(args.ctl))
9545f8f0193SMike Rapoport 			goto cleanup;
9555f8f0193SMike Rapoport 
9565f8f0193SMike Rapoport 		port = args.port = 1000 + rand() % 60000;
9575f8f0193SMike Rapoport 
9585f8f0193SMike Rapoport 		pid = cg_run_nowait(memcg, tcp_server, &args);
9595f8f0193SMike Rapoport 		if (pid < 0)
9605f8f0193SMike Rapoport 			goto cleanup;
9615f8f0193SMike Rapoport 
9625f8f0193SMike Rapoport 		close(args.ctl[1]);
9635f8f0193SMike Rapoport 		if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err))
9645f8f0193SMike Rapoport 			goto cleanup;
9655f8f0193SMike Rapoport 		close(args.ctl[0]);
9665f8f0193SMike Rapoport 
9675f8f0193SMike Rapoport 		if (!err)
9685f8f0193SMike Rapoport 			break;
9695f8f0193SMike Rapoport 		if (err != EADDRINUSE)
9705f8f0193SMike Rapoport 			goto cleanup;
9715f8f0193SMike Rapoport 
9725f8f0193SMike Rapoport 		waitpid(pid, NULL, 0);
9735f8f0193SMike Rapoport 	}
9745f8f0193SMike Rapoport 
9755f8f0193SMike Rapoport 	if (err == EADDRINUSE) {
9765f8f0193SMike Rapoport 		ret = KSFT_SKIP;
9775f8f0193SMike Rapoport 		goto cleanup;
9785f8f0193SMike Rapoport 	}
9795f8f0193SMike Rapoport 
9805f8f0193SMike Rapoport 	if (tcp_client(memcg, port) != KSFT_PASS)
9815f8f0193SMike Rapoport 		goto cleanup;
9825f8f0193SMike Rapoport 
9835f8f0193SMike Rapoport 	waitpid(pid, &err, 0);
9845f8f0193SMike Rapoport 	if (WEXITSTATUS(err))
9855f8f0193SMike Rapoport 		goto cleanup;
9865f8f0193SMike Rapoport 
9875f8f0193SMike Rapoport 	if (cg_read_long(memcg, "memory.current") < 0)
9885f8f0193SMike Rapoport 		goto cleanup;
9895f8f0193SMike Rapoport 
9905f8f0193SMike Rapoport 	if (cg_read_key_long(memcg, "memory.stat", "sock "))
9915f8f0193SMike Rapoport 		goto cleanup;
9925f8f0193SMike Rapoport 
9935f8f0193SMike Rapoport 	ret = KSFT_PASS;
9945f8f0193SMike Rapoport 
9955f8f0193SMike Rapoport cleanup:
9965f8f0193SMike Rapoport 	cg_destroy(memcg);
9975f8f0193SMike Rapoport 	free(memcg);
9985f8f0193SMike Rapoport 
9995f8f0193SMike Rapoport 	return ret;
10005f8f0193SMike Rapoport }
10015f8f0193SMike Rapoport 
1002a987785dSJay Kamat /*
1003a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1004a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1005a987785dSJay Kamat  * processes in the leaf (but not the parent) were killed.
1006a987785dSJay Kamat  */
1007a987785dSJay Kamat static int test_memcg_oom_group_leaf_events(const char *root)
1008a987785dSJay Kamat {
1009a987785dSJay Kamat 	int ret = KSFT_FAIL;
1010a987785dSJay Kamat 	char *parent, *child;
1011a987785dSJay Kamat 
1012a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1013a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1014a987785dSJay Kamat 
1015a987785dSJay Kamat 	if (!parent || !child)
1016a987785dSJay Kamat 		goto cleanup;
1017a987785dSJay Kamat 
1018a987785dSJay Kamat 	if (cg_create(parent))
1019a987785dSJay Kamat 		goto cleanup;
1020a987785dSJay Kamat 
1021a987785dSJay Kamat 	if (cg_create(child))
1022a987785dSJay Kamat 		goto cleanup;
1023a987785dSJay Kamat 
1024a987785dSJay Kamat 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
1025a987785dSJay Kamat 		goto cleanup;
1026a987785dSJay Kamat 
1027a987785dSJay Kamat 	if (cg_write(child, "memory.max", "50M"))
1028a987785dSJay Kamat 		goto cleanup;
1029a987785dSJay Kamat 
1030a987785dSJay Kamat 	if (cg_write(child, "memory.swap.max", "0"))
1031a987785dSJay Kamat 		goto cleanup;
1032a987785dSJay Kamat 
1033a987785dSJay Kamat 	if (cg_write(child, "memory.oom.group", "1"))
1034a987785dSJay Kamat 		goto cleanup;
1035a987785dSJay Kamat 
1036a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1037a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1038a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1039a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1040a987785dSJay Kamat 		goto cleanup;
1041a987785dSJay Kamat 
1042a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1043a987785dSJay Kamat 		goto cleanup;
1044a987785dSJay Kamat 
1045a987785dSJay Kamat 	if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
1046a987785dSJay Kamat 		goto cleanup;
1047a987785dSJay Kamat 
1048a987785dSJay Kamat 	if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0)
1049a987785dSJay Kamat 		goto cleanup;
1050a987785dSJay Kamat 
1051a987785dSJay Kamat 	ret = KSFT_PASS;
1052a987785dSJay Kamat 
1053a987785dSJay Kamat cleanup:
1054a987785dSJay Kamat 	if (child)
1055a987785dSJay Kamat 		cg_destroy(child);
1056a987785dSJay Kamat 	if (parent)
1057a987785dSJay Kamat 		cg_destroy(parent);
1058a987785dSJay Kamat 	free(child);
1059a987785dSJay Kamat 	free(parent);
1060a987785dSJay Kamat 
1061a987785dSJay Kamat 	return ret;
1062a987785dSJay Kamat }
1063a987785dSJay Kamat 
1064a987785dSJay Kamat /*
1065a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1066a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1067a987785dSJay Kamat  * processes in the parent and leaf were killed.
1068a987785dSJay Kamat  */
1069a987785dSJay Kamat static int test_memcg_oom_group_parent_events(const char *root)
1070a987785dSJay Kamat {
1071a987785dSJay Kamat 	int ret = KSFT_FAIL;
1072a987785dSJay Kamat 	char *parent, *child;
1073a987785dSJay Kamat 
1074a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1075a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1076a987785dSJay Kamat 
1077a987785dSJay Kamat 	if (!parent || !child)
1078a987785dSJay Kamat 		goto cleanup;
1079a987785dSJay Kamat 
1080a987785dSJay Kamat 	if (cg_create(parent))
1081a987785dSJay Kamat 		goto cleanup;
1082a987785dSJay Kamat 
1083a987785dSJay Kamat 	if (cg_create(child))
1084a987785dSJay Kamat 		goto cleanup;
1085a987785dSJay Kamat 
1086a987785dSJay Kamat 	if (cg_write(parent, "memory.max", "80M"))
1087a987785dSJay Kamat 		goto cleanup;
1088a987785dSJay Kamat 
1089a987785dSJay Kamat 	if (cg_write(parent, "memory.swap.max", "0"))
1090a987785dSJay Kamat 		goto cleanup;
1091a987785dSJay Kamat 
1092a987785dSJay Kamat 	if (cg_write(parent, "memory.oom.group", "1"))
1093a987785dSJay Kamat 		goto cleanup;
1094a987785dSJay Kamat 
1095a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1096a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1097a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1098a987785dSJay Kamat 
1099a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1100a987785dSJay Kamat 		goto cleanup;
1101a987785dSJay Kamat 
1102a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1103a987785dSJay Kamat 		goto cleanup;
1104a987785dSJay Kamat 	if (cg_test_proc_killed(parent))
1105a987785dSJay Kamat 		goto cleanup;
1106a987785dSJay Kamat 
1107a987785dSJay Kamat 	ret = KSFT_PASS;
1108a987785dSJay Kamat 
1109a987785dSJay Kamat cleanup:
1110a987785dSJay Kamat 	if (child)
1111a987785dSJay Kamat 		cg_destroy(child);
1112a987785dSJay Kamat 	if (parent)
1113a987785dSJay Kamat 		cg_destroy(parent);
1114a987785dSJay Kamat 	free(child);
1115a987785dSJay Kamat 	free(parent);
1116a987785dSJay Kamat 
1117a987785dSJay Kamat 	return ret;
1118a987785dSJay Kamat }
1119a987785dSJay Kamat 
1120a987785dSJay Kamat /*
1121a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1122a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1123a987785dSJay Kamat  * processes were killed except those set with OOM_SCORE_ADJ_MIN
1124a987785dSJay Kamat  */
1125a987785dSJay Kamat static int test_memcg_oom_group_score_events(const char *root)
1126a987785dSJay Kamat {
1127a987785dSJay Kamat 	int ret = KSFT_FAIL;
1128a987785dSJay Kamat 	char *memcg;
1129a987785dSJay Kamat 	int safe_pid;
1130a987785dSJay Kamat 
1131a987785dSJay Kamat 	memcg = cg_name(root, "memcg_test_0");
1132a987785dSJay Kamat 
1133a987785dSJay Kamat 	if (!memcg)
1134a987785dSJay Kamat 		goto cleanup;
1135a987785dSJay Kamat 
1136a987785dSJay Kamat 	if (cg_create(memcg))
1137a987785dSJay Kamat 		goto cleanup;
1138a987785dSJay Kamat 
1139a987785dSJay Kamat 	if (cg_write(memcg, "memory.max", "50M"))
1140a987785dSJay Kamat 		goto cleanup;
1141a987785dSJay Kamat 
1142a987785dSJay Kamat 	if (cg_write(memcg, "memory.swap.max", "0"))
1143a987785dSJay Kamat 		goto cleanup;
1144a987785dSJay Kamat 
1145a987785dSJay Kamat 	if (cg_write(memcg, "memory.oom.group", "1"))
1146a987785dSJay Kamat 		goto cleanup;
1147a987785dSJay Kamat 
1148a987785dSJay Kamat 	safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1149a987785dSJay Kamat 	if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN))
1150a987785dSJay Kamat 		goto cleanup;
1151a987785dSJay Kamat 
1152a987785dSJay Kamat 	cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1153a987785dSJay Kamat 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
1154a987785dSJay Kamat 		goto cleanup;
1155a987785dSJay Kamat 
1156a987785dSJay Kamat 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3)
1157a987785dSJay Kamat 		goto cleanup;
1158a987785dSJay Kamat 
1159a987785dSJay Kamat 	if (kill(safe_pid, SIGKILL))
1160a987785dSJay Kamat 		goto cleanup;
1161a987785dSJay Kamat 
1162a987785dSJay Kamat 	ret = KSFT_PASS;
1163a987785dSJay Kamat 
1164a987785dSJay Kamat cleanup:
1165a987785dSJay Kamat 	if (memcg)
1166a987785dSJay Kamat 		cg_destroy(memcg);
1167a987785dSJay Kamat 	free(memcg);
1168a987785dSJay Kamat 
1169a987785dSJay Kamat 	return ret;
1170a987785dSJay Kamat }
1171a987785dSJay Kamat 
1172a987785dSJay Kamat 
117384092dbcSRoman Gushchin #define T(x) { x, #x }
117484092dbcSRoman Gushchin struct memcg_test {
117584092dbcSRoman Gushchin 	int (*fn)(const char *root);
117684092dbcSRoman Gushchin 	const char *name;
117784092dbcSRoman Gushchin } tests[] = {
117884092dbcSRoman Gushchin 	T(test_memcg_subtree_control),
117984092dbcSRoman Gushchin 	T(test_memcg_current),
118084092dbcSRoman Gushchin 	T(test_memcg_min),
118184092dbcSRoman Gushchin 	T(test_memcg_low),
118284092dbcSRoman Gushchin 	T(test_memcg_high),
118384092dbcSRoman Gushchin 	T(test_memcg_max),
118484092dbcSRoman Gushchin 	T(test_memcg_oom_events),
1185478b2784SMike Rapoport 	T(test_memcg_swap_max),
11865f8f0193SMike Rapoport 	T(test_memcg_sock),
1187a987785dSJay Kamat 	T(test_memcg_oom_group_leaf_events),
1188a987785dSJay Kamat 	T(test_memcg_oom_group_parent_events),
1189a987785dSJay Kamat 	T(test_memcg_oom_group_score_events),
119084092dbcSRoman Gushchin };
119184092dbcSRoman Gushchin #undef T
119284092dbcSRoman Gushchin 
119384092dbcSRoman Gushchin int main(int argc, char **argv)
119484092dbcSRoman Gushchin {
119584092dbcSRoman Gushchin 	char root[PATH_MAX];
119684092dbcSRoman Gushchin 	int i, ret = EXIT_SUCCESS;
119784092dbcSRoman Gushchin 
119884092dbcSRoman Gushchin 	if (cg_find_unified_root(root, sizeof(root)))
119984092dbcSRoman Gushchin 		ksft_exit_skip("cgroup v2 isn't mounted\n");
120084092dbcSRoman Gushchin 
120184092dbcSRoman Gushchin 	/*
120284092dbcSRoman Gushchin 	 * Check that memory controller is available:
120384092dbcSRoman Gushchin 	 * memory is listed in cgroup.controllers
120484092dbcSRoman Gushchin 	 */
120584092dbcSRoman Gushchin 	if (cg_read_strstr(root, "cgroup.controllers", "memory"))
120684092dbcSRoman Gushchin 		ksft_exit_skip("memory controller isn't available\n");
120784092dbcSRoman Gushchin 
120884092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
120984092dbcSRoman Gushchin 		switch (tests[i].fn(root)) {
121084092dbcSRoman Gushchin 		case KSFT_PASS:
121184092dbcSRoman Gushchin 			ksft_test_result_pass("%s\n", tests[i].name);
121284092dbcSRoman Gushchin 			break;
121384092dbcSRoman Gushchin 		case KSFT_SKIP:
121484092dbcSRoman Gushchin 			ksft_test_result_skip("%s\n", tests[i].name);
121584092dbcSRoman Gushchin 			break;
121684092dbcSRoman Gushchin 		default:
121784092dbcSRoman Gushchin 			ret = EXIT_FAILURE;
121884092dbcSRoman Gushchin 			ksft_test_result_fail("%s\n", tests[i].name);
121984092dbcSRoman Gushchin 			break;
122084092dbcSRoman Gushchin 		}
122184092dbcSRoman Gushchin 	}
122284092dbcSRoman Gushchin 
122384092dbcSRoman Gushchin 	return ret;
122484092dbcSRoman Gushchin }
1225