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>
196323ec54SShakeel Butt #include <sys/mman.h>
2084092dbcSRoman Gushchin 
2184092dbcSRoman Gushchin #include "../kselftest.h"
2284092dbcSRoman Gushchin #include "cgroup_util.h"
2384092dbcSRoman Gushchin 
24*cdc69458SDavid Vernet static bool has_recursiveprot;
25*cdc69458SDavid Vernet 
2684092dbcSRoman Gushchin /*
2784092dbcSRoman Gushchin  * This test creates two nested cgroups with and without enabling
2884092dbcSRoman Gushchin  * the memory controller.
2984092dbcSRoman Gushchin  */
3084092dbcSRoman Gushchin static int test_memcg_subtree_control(const char *root)
3184092dbcSRoman Gushchin {
32e14d314cSRoman Gushchin 	char *parent, *child, *parent2 = NULL, *child2 = NULL;
3384092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
3484092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
3584092dbcSRoman Gushchin 
3684092dbcSRoman Gushchin 	/* Create two nested cgroups with the memory controller enabled */
3784092dbcSRoman Gushchin 	parent = cg_name(root, "memcg_test_0");
3884092dbcSRoman Gushchin 	child = cg_name(root, "memcg_test_0/memcg_test_1");
3984092dbcSRoman Gushchin 	if (!parent || !child)
40e14d314cSRoman Gushchin 		goto cleanup_free;
4184092dbcSRoman Gushchin 
4284092dbcSRoman Gushchin 	if (cg_create(parent))
43e14d314cSRoman Gushchin 		goto cleanup_free;
4484092dbcSRoman Gushchin 
4584092dbcSRoman Gushchin 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
46e14d314cSRoman Gushchin 		goto cleanup_parent;
4784092dbcSRoman Gushchin 
4884092dbcSRoman Gushchin 	if (cg_create(child))
49e14d314cSRoman Gushchin 		goto cleanup_parent;
5084092dbcSRoman Gushchin 
5184092dbcSRoman Gushchin 	if (cg_read_strstr(child, "cgroup.controllers", "memory"))
52e14d314cSRoman Gushchin 		goto cleanup_child;
5384092dbcSRoman Gushchin 
5484092dbcSRoman Gushchin 	/* Create two nested cgroups without enabling memory controller */
5584092dbcSRoman Gushchin 	parent2 = cg_name(root, "memcg_test_1");
5684092dbcSRoman Gushchin 	child2 = cg_name(root, "memcg_test_1/memcg_test_1");
5784092dbcSRoman Gushchin 	if (!parent2 || !child2)
58e14d314cSRoman Gushchin 		goto cleanup_free2;
5984092dbcSRoman Gushchin 
6084092dbcSRoman Gushchin 	if (cg_create(parent2))
61e14d314cSRoman Gushchin 		goto cleanup_free2;
6284092dbcSRoman Gushchin 
6384092dbcSRoman Gushchin 	if (cg_create(child2))
64e14d314cSRoman Gushchin 		goto cleanup_parent2;
6584092dbcSRoman Gushchin 
6684092dbcSRoman Gushchin 	if (cg_read(child2, "cgroup.controllers", buf, sizeof(buf)))
67e14d314cSRoman Gushchin 		goto cleanup_all;
6884092dbcSRoman Gushchin 
6984092dbcSRoman Gushchin 	if (!cg_read_strstr(child2, "cgroup.controllers", "memory"))
70e14d314cSRoman Gushchin 		goto cleanup_all;
7184092dbcSRoman Gushchin 
7284092dbcSRoman Gushchin 	ret = KSFT_PASS;
7384092dbcSRoman Gushchin 
74e14d314cSRoman Gushchin cleanup_all:
7584092dbcSRoman Gushchin 	cg_destroy(child2);
76e14d314cSRoman Gushchin cleanup_parent2:
7784092dbcSRoman Gushchin 	cg_destroy(parent2);
78e14d314cSRoman Gushchin cleanup_free2:
7984092dbcSRoman Gushchin 	free(parent2);
8084092dbcSRoman Gushchin 	free(child2);
81e14d314cSRoman Gushchin cleanup_child:
82e14d314cSRoman Gushchin 	cg_destroy(child);
83e14d314cSRoman Gushchin cleanup_parent:
84e14d314cSRoman Gushchin 	cg_destroy(parent);
85e14d314cSRoman Gushchin cleanup_free:
86e14d314cSRoman Gushchin 	free(parent);
87e14d314cSRoman Gushchin 	free(child);
8884092dbcSRoman Gushchin 
8984092dbcSRoman Gushchin 	return ret;
9084092dbcSRoman Gushchin }
9184092dbcSRoman Gushchin 
9284092dbcSRoman Gushchin static int alloc_anon_50M_check(const char *cgroup, void *arg)
9384092dbcSRoman Gushchin {
9484092dbcSRoman Gushchin 	size_t size = MB(50);
9584092dbcSRoman Gushchin 	char *buf, *ptr;
9684092dbcSRoman Gushchin 	long anon, current;
9784092dbcSRoman Gushchin 	int ret = -1;
9884092dbcSRoman Gushchin 
9984092dbcSRoman Gushchin 	buf = malloc(size);
10084092dbcSRoman Gushchin 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
10184092dbcSRoman Gushchin 		*ptr = 0;
10284092dbcSRoman Gushchin 
10384092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
10484092dbcSRoman Gushchin 	if (current < size)
10584092dbcSRoman Gushchin 		goto cleanup;
10684092dbcSRoman Gushchin 
10784092dbcSRoman Gushchin 	if (!values_close(size, current, 3))
10884092dbcSRoman Gushchin 		goto cleanup;
10984092dbcSRoman Gushchin 
11084092dbcSRoman Gushchin 	anon = cg_read_key_long(cgroup, "memory.stat", "anon ");
11184092dbcSRoman Gushchin 	if (anon < 0)
11284092dbcSRoman Gushchin 		goto cleanup;
11384092dbcSRoman Gushchin 
11484092dbcSRoman Gushchin 	if (!values_close(anon, current, 3))
11584092dbcSRoman Gushchin 		goto cleanup;
11684092dbcSRoman Gushchin 
11784092dbcSRoman Gushchin 	ret = 0;
11884092dbcSRoman Gushchin cleanup:
11984092dbcSRoman Gushchin 	free(buf);
12084092dbcSRoman Gushchin 	return ret;
12184092dbcSRoman Gushchin }
12284092dbcSRoman Gushchin 
12384092dbcSRoman Gushchin static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
12484092dbcSRoman Gushchin {
12584092dbcSRoman Gushchin 	size_t size = MB(50);
12684092dbcSRoman Gushchin 	int ret = -1;
12784092dbcSRoman Gushchin 	long current, file;
12884092dbcSRoman Gushchin 	int fd;
12984092dbcSRoman Gushchin 
13084092dbcSRoman Gushchin 	fd = get_temp_fd();
13184092dbcSRoman Gushchin 	if (fd < 0)
13284092dbcSRoman Gushchin 		return -1;
13384092dbcSRoman Gushchin 
13484092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
13584092dbcSRoman Gushchin 		goto cleanup;
13684092dbcSRoman Gushchin 
13784092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
13884092dbcSRoman Gushchin 	if (current < size)
13984092dbcSRoman Gushchin 		goto cleanup;
14084092dbcSRoman Gushchin 
14184092dbcSRoman Gushchin 	file = cg_read_key_long(cgroup, "memory.stat", "file ");
14284092dbcSRoman Gushchin 	if (file < 0)
14384092dbcSRoman Gushchin 		goto cleanup;
14484092dbcSRoman Gushchin 
14584092dbcSRoman Gushchin 	if (!values_close(file, current, 10))
14684092dbcSRoman Gushchin 		goto cleanup;
14784092dbcSRoman Gushchin 
14884092dbcSRoman Gushchin 	ret = 0;
14984092dbcSRoman Gushchin 
15084092dbcSRoman Gushchin cleanup:
15184092dbcSRoman Gushchin 	close(fd);
15284092dbcSRoman Gushchin 	return ret;
15384092dbcSRoman Gushchin }
15484092dbcSRoman Gushchin 
15584092dbcSRoman Gushchin /*
15684092dbcSRoman Gushchin  * This test create a memory cgroup, allocates
15784092dbcSRoman Gushchin  * some anonymous memory and some pagecache
15884092dbcSRoman Gushchin  * and check memory.current and some memory.stat values.
15984092dbcSRoman Gushchin  */
16084092dbcSRoman Gushchin static int test_memcg_current(const char *root)
16184092dbcSRoman Gushchin {
16284092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
16384092dbcSRoman Gushchin 	long current;
16484092dbcSRoman Gushchin 	char *memcg;
16584092dbcSRoman Gushchin 
16684092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
16784092dbcSRoman Gushchin 	if (!memcg)
16884092dbcSRoman Gushchin 		goto cleanup;
16984092dbcSRoman Gushchin 
17084092dbcSRoman Gushchin 	if (cg_create(memcg))
17184092dbcSRoman Gushchin 		goto cleanup;
17284092dbcSRoman Gushchin 
17384092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
17484092dbcSRoman Gushchin 	if (current != 0)
17584092dbcSRoman Gushchin 		goto cleanup;
17684092dbcSRoman Gushchin 
17784092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_anon_50M_check, NULL))
17884092dbcSRoman Gushchin 		goto cleanup;
17984092dbcSRoman Gushchin 
18084092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_50M_check, NULL))
18184092dbcSRoman Gushchin 		goto cleanup;
18284092dbcSRoman Gushchin 
18384092dbcSRoman Gushchin 	ret = KSFT_PASS;
18484092dbcSRoman Gushchin 
18584092dbcSRoman Gushchin cleanup:
18684092dbcSRoman Gushchin 	cg_destroy(memcg);
18784092dbcSRoman Gushchin 	free(memcg);
18884092dbcSRoman Gushchin 
18984092dbcSRoman Gushchin 	return ret;
19084092dbcSRoman Gushchin }
19184092dbcSRoman Gushchin 
19284092dbcSRoman Gushchin static int alloc_pagecache_50M(const char *cgroup, void *arg)
19384092dbcSRoman Gushchin {
19484092dbcSRoman Gushchin 	int fd = (long)arg;
19584092dbcSRoman Gushchin 
19684092dbcSRoman Gushchin 	return alloc_pagecache(fd, MB(50));
19784092dbcSRoman Gushchin }
19884092dbcSRoman Gushchin 
19984092dbcSRoman Gushchin static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg)
20084092dbcSRoman Gushchin {
20184092dbcSRoman Gushchin 	int fd = (long)arg;
20284092dbcSRoman Gushchin 	int ppid = getppid();
20384092dbcSRoman Gushchin 
20484092dbcSRoman Gushchin 	if (alloc_pagecache(fd, MB(50)))
20584092dbcSRoman Gushchin 		return -1;
20684092dbcSRoman Gushchin 
20784092dbcSRoman Gushchin 	while (getppid() == ppid)
20884092dbcSRoman Gushchin 		sleep(1);
20984092dbcSRoman Gushchin 
21084092dbcSRoman Gushchin 	return 0;
21184092dbcSRoman Gushchin }
21284092dbcSRoman Gushchin 
213a987785dSJay Kamat static int alloc_anon_noexit(const char *cgroup, void *arg)
214a987785dSJay Kamat {
215a987785dSJay Kamat 	int ppid = getppid();
216a3622a53SYosry Ahmed 	size_t size = (unsigned long)arg;
217a3622a53SYosry Ahmed 	char *buf, *ptr;
218a987785dSJay Kamat 
219a3622a53SYosry Ahmed 	buf = malloc(size);
220a3622a53SYosry Ahmed 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
221a3622a53SYosry Ahmed 		*ptr = 0;
222a987785dSJay Kamat 
223a987785dSJay Kamat 	while (getppid() == ppid)
224a987785dSJay Kamat 		sleep(1);
225a987785dSJay Kamat 
226a3622a53SYosry Ahmed 	free(buf);
227a987785dSJay Kamat 	return 0;
228a987785dSJay Kamat }
229a987785dSJay Kamat 
230a987785dSJay Kamat /*
231a987785dSJay Kamat  * Wait until processes are killed asynchronously by the OOM killer
232a987785dSJay Kamat  * If we exceed a timeout, fail.
233a987785dSJay Kamat  */
234a987785dSJay Kamat static int cg_test_proc_killed(const char *cgroup)
235a987785dSJay Kamat {
236a987785dSJay Kamat 	int limit;
237a987785dSJay Kamat 
238a987785dSJay Kamat 	for (limit = 10; limit > 0; limit--) {
239a987785dSJay Kamat 		if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
240a987785dSJay Kamat 			return 0;
241a987785dSJay Kamat 
242a987785dSJay Kamat 		usleep(100000);
243a987785dSJay Kamat 	}
244a987785dSJay Kamat 	return -1;
245a987785dSJay Kamat }
246a987785dSJay Kamat 
24784092dbcSRoman Gushchin /*
24884092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
24984092dbcSRoman Gushchin  * A       memory.min = 50M,  memory.max = 200M
25084092dbcSRoman Gushchin  * A/B     memory.min = 50M,  memory.current = 50M
25184092dbcSRoman Gushchin  * A/B/C   memory.min = 75M,  memory.current = 50M
25284092dbcSRoman Gushchin  * A/B/D   memory.min = 25M,  memory.current = 50M
253f0cdaa56SDavid Vernet  * A/B/E   memory.min = 0,    memory.current = 50M
254f0cdaa56SDavid Vernet  * A/B/F   memory.min = 500M, memory.current = 0
25584092dbcSRoman Gushchin  *
25684092dbcSRoman Gushchin  * Usages are pagecache, but the test keeps a running
25784092dbcSRoman Gushchin  * process in every leaf cgroup.
25884092dbcSRoman Gushchin  * Then it creates A/G and creates a significant
25984092dbcSRoman Gushchin  * memory pressure in it.
26084092dbcSRoman Gushchin  *
26184092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
26284092dbcSRoman Gushchin  * A/B/C  memory.current ~= 33M
26384092dbcSRoman Gushchin  * A/B/D  memory.current ~= 17M
264f0cdaa56SDavid Vernet  * A/B/F  memory.current ~= 0
26584092dbcSRoman Gushchin  *
26684092dbcSRoman Gushchin  * After that it tries to allocate more than there is
26784092dbcSRoman Gushchin  * unprotected memory in A available, and checks
26884092dbcSRoman Gushchin  * checks that memory.min protects pagecache even
26984092dbcSRoman Gushchin  * in this case.
27084092dbcSRoman Gushchin  */
27184092dbcSRoman Gushchin static int test_memcg_min(const char *root)
27284092dbcSRoman Gushchin {
27384092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
27484092dbcSRoman Gushchin 	char *parent[3] = {NULL};
27584092dbcSRoman Gushchin 	char *children[4] = {NULL};
27684092dbcSRoman Gushchin 	long c[4];
27784092dbcSRoman Gushchin 	int i, attempts;
27884092dbcSRoman Gushchin 	int fd;
27984092dbcSRoman Gushchin 
28084092dbcSRoman Gushchin 	fd = get_temp_fd();
28184092dbcSRoman Gushchin 	if (fd < 0)
28284092dbcSRoman Gushchin 		goto cleanup;
28384092dbcSRoman Gushchin 
28484092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
28584092dbcSRoman Gushchin 	if (!parent[0])
28684092dbcSRoman Gushchin 		goto cleanup;
28784092dbcSRoman Gushchin 
28884092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
28984092dbcSRoman Gushchin 	if (!parent[1])
29084092dbcSRoman Gushchin 		goto cleanup;
29184092dbcSRoman Gushchin 
29284092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
29384092dbcSRoman Gushchin 	if (!parent[2])
29484092dbcSRoman Gushchin 		goto cleanup;
29584092dbcSRoman Gushchin 
29684092dbcSRoman Gushchin 	if (cg_create(parent[0]))
29784092dbcSRoman Gushchin 		goto cleanup;
29884092dbcSRoman Gushchin 
29984092dbcSRoman Gushchin 	if (cg_read_long(parent[0], "memory.min")) {
30084092dbcSRoman Gushchin 		ret = KSFT_SKIP;
30184092dbcSRoman Gushchin 		goto cleanup;
30284092dbcSRoman Gushchin 	}
30384092dbcSRoman Gushchin 
30484092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
30584092dbcSRoman Gushchin 		goto cleanup;
30684092dbcSRoman Gushchin 
30784092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
30884092dbcSRoman Gushchin 		goto cleanup;
30984092dbcSRoman Gushchin 
31084092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
31184092dbcSRoman Gushchin 		goto cleanup;
31284092dbcSRoman Gushchin 
31384092dbcSRoman Gushchin 	if (cg_create(parent[1]))
31484092dbcSRoman Gushchin 		goto cleanup;
31584092dbcSRoman Gushchin 
31684092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
31784092dbcSRoman Gushchin 		goto cleanup;
31884092dbcSRoman Gushchin 
31984092dbcSRoman Gushchin 	if (cg_create(parent[2]))
32084092dbcSRoman Gushchin 		goto cleanup;
32184092dbcSRoman Gushchin 
32284092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
32384092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
32484092dbcSRoman Gushchin 		if (!children[i])
32584092dbcSRoman Gushchin 			goto cleanup;
32684092dbcSRoman Gushchin 
32784092dbcSRoman Gushchin 		if (cg_create(children[i]))
32884092dbcSRoman Gushchin 			goto cleanup;
32984092dbcSRoman Gushchin 
330f0cdaa56SDavid Vernet 		if (i > 2)
33184092dbcSRoman Gushchin 			continue;
33284092dbcSRoman Gushchin 
33384092dbcSRoman Gushchin 		cg_run_nowait(children[i], alloc_pagecache_50M_noexit,
33484092dbcSRoman Gushchin 			      (void *)(long)fd);
33584092dbcSRoman Gushchin 	}
33684092dbcSRoman Gushchin 
33784092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.min", "50M"))
33884092dbcSRoman Gushchin 		goto cleanup;
33984092dbcSRoman Gushchin 	if (cg_write(parent[1], "memory.min", "50M"))
34084092dbcSRoman Gushchin 		goto cleanup;
34184092dbcSRoman Gushchin 	if (cg_write(children[0], "memory.min", "75M"))
34284092dbcSRoman Gushchin 		goto cleanup;
34384092dbcSRoman Gushchin 	if (cg_write(children[1], "memory.min", "25M"))
34484092dbcSRoman Gushchin 		goto cleanup;
345f0cdaa56SDavid Vernet 	if (cg_write(children[2], "memory.min", "0"))
34684092dbcSRoman Gushchin 		goto cleanup;
347f0cdaa56SDavid Vernet 	if (cg_write(children[3], "memory.min", "500M"))
34884092dbcSRoman Gushchin 		goto cleanup;
34984092dbcSRoman Gushchin 
35084092dbcSRoman Gushchin 	attempts = 0;
35184092dbcSRoman Gushchin 	while (!values_close(cg_read_long(parent[1], "memory.current"),
35284092dbcSRoman Gushchin 			     MB(150), 3)) {
35384092dbcSRoman Gushchin 		if (attempts++ > 5)
35484092dbcSRoman Gushchin 			break;
35584092dbcSRoman Gushchin 		sleep(1);
35684092dbcSRoman Gushchin 	}
35784092dbcSRoman Gushchin 
35884092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
35984092dbcSRoman Gushchin 		goto cleanup;
36084092dbcSRoman Gushchin 
36184092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
36284092dbcSRoman Gushchin 		goto cleanup;
36384092dbcSRoman Gushchin 
36484092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
36584092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
36684092dbcSRoman Gushchin 
36784092dbcSRoman Gushchin 	if (!values_close(c[0], MB(33), 10))
36884092dbcSRoman Gushchin 		goto cleanup;
36984092dbcSRoman Gushchin 
37084092dbcSRoman Gushchin 	if (!values_close(c[1], MB(17), 10))
37184092dbcSRoman Gushchin 		goto cleanup;
37284092dbcSRoman Gushchin 
373f0cdaa56SDavid Vernet 	if (c[3] != 0)
37484092dbcSRoman Gushchin 		goto cleanup;
37584092dbcSRoman Gushchin 
37684092dbcSRoman Gushchin 	if (!cg_run(parent[2], alloc_anon, (void *)MB(170)))
37784092dbcSRoman Gushchin 		goto cleanup;
37884092dbcSRoman Gushchin 
37984092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
38084092dbcSRoman Gushchin 		goto cleanup;
38184092dbcSRoman Gushchin 
38284092dbcSRoman Gushchin 	ret = KSFT_PASS;
38384092dbcSRoman Gushchin 
38484092dbcSRoman Gushchin cleanup:
38584092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
38684092dbcSRoman Gushchin 		if (!children[i])
38784092dbcSRoman Gushchin 			continue;
38884092dbcSRoman Gushchin 
38984092dbcSRoman Gushchin 		cg_destroy(children[i]);
39084092dbcSRoman Gushchin 		free(children[i]);
39184092dbcSRoman Gushchin 	}
39284092dbcSRoman Gushchin 
39384092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
39484092dbcSRoman Gushchin 		if (!parent[i])
39584092dbcSRoman Gushchin 			continue;
39684092dbcSRoman Gushchin 
39784092dbcSRoman Gushchin 		cg_destroy(parent[i]);
39884092dbcSRoman Gushchin 		free(parent[i]);
39984092dbcSRoman Gushchin 	}
40084092dbcSRoman Gushchin 	close(fd);
40184092dbcSRoman Gushchin 	return ret;
40284092dbcSRoman Gushchin }
40384092dbcSRoman Gushchin 
40484092dbcSRoman Gushchin /*
40584092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
40684092dbcSRoman Gushchin  * A       memory.low = 50M,  memory.max = 200M
40784092dbcSRoman Gushchin  * A/B     memory.low = 50M,  memory.current = 50M
40884092dbcSRoman Gushchin  * A/B/C   memory.low = 75M,  memory.current = 50M
40984092dbcSRoman Gushchin  * A/B/D   memory.low = 25M,  memory.current = 50M
410f0cdaa56SDavid Vernet  * A/B/E   memory.low = 0,    memory.current = 50M
411f0cdaa56SDavid Vernet  * A/B/F   memory.low = 500M, memory.current = 0
41284092dbcSRoman Gushchin  *
41384092dbcSRoman Gushchin  * Usages are pagecache.
41484092dbcSRoman Gushchin  * Then it creates A/G an creates a significant
41584092dbcSRoman Gushchin  * memory pressure in it.
41684092dbcSRoman Gushchin  *
41784092dbcSRoman Gushchin  * Then it checks actual memory usages and expects that:
41884092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
41984092dbcSRoman Gushchin  * A/B/   memory.current ~= 33M
42084092dbcSRoman Gushchin  * A/B/D  memory.current ~= 17M
421f0cdaa56SDavid Vernet  * A/B/F  memory.current ~= 0
42284092dbcSRoman Gushchin  *
42384092dbcSRoman Gushchin  * After that it tries to allocate more than there is
42484092dbcSRoman Gushchin  * unprotected memory in A available,
42584092dbcSRoman Gushchin  * and checks low and oom events in memory.events.
42684092dbcSRoman Gushchin  */
42784092dbcSRoman Gushchin static int test_memcg_low(const char *root)
42884092dbcSRoman Gushchin {
42984092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
43084092dbcSRoman Gushchin 	char *parent[3] = {NULL};
43184092dbcSRoman Gushchin 	char *children[4] = {NULL};
43284092dbcSRoman Gushchin 	long low, oom;
43384092dbcSRoman Gushchin 	long c[4];
43484092dbcSRoman Gushchin 	int i;
43584092dbcSRoman Gushchin 	int fd;
43684092dbcSRoman Gushchin 
43784092dbcSRoman Gushchin 	fd = get_temp_fd();
43884092dbcSRoman Gushchin 	if (fd < 0)
43984092dbcSRoman Gushchin 		goto cleanup;
44084092dbcSRoman Gushchin 
44184092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
44284092dbcSRoman Gushchin 	if (!parent[0])
44384092dbcSRoman Gushchin 		goto cleanup;
44484092dbcSRoman Gushchin 
44584092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
44684092dbcSRoman Gushchin 	if (!parent[1])
44784092dbcSRoman Gushchin 		goto cleanup;
44884092dbcSRoman Gushchin 
44984092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
45084092dbcSRoman Gushchin 	if (!parent[2])
45184092dbcSRoman Gushchin 		goto cleanup;
45284092dbcSRoman Gushchin 
45384092dbcSRoman Gushchin 	if (cg_create(parent[0]))
45484092dbcSRoman Gushchin 		goto cleanup;
45584092dbcSRoman Gushchin 
45684092dbcSRoman Gushchin 	if (cg_read_long(parent[0], "memory.low"))
45784092dbcSRoman Gushchin 		goto cleanup;
45884092dbcSRoman Gushchin 
45984092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
46084092dbcSRoman Gushchin 		goto cleanup;
46184092dbcSRoman Gushchin 
46284092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
46384092dbcSRoman Gushchin 		goto cleanup;
46484092dbcSRoman Gushchin 
46584092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
46684092dbcSRoman Gushchin 		goto cleanup;
46784092dbcSRoman Gushchin 
46884092dbcSRoman Gushchin 	if (cg_create(parent[1]))
46984092dbcSRoman Gushchin 		goto cleanup;
47084092dbcSRoman Gushchin 
47184092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
47284092dbcSRoman Gushchin 		goto cleanup;
47384092dbcSRoman Gushchin 
47484092dbcSRoman Gushchin 	if (cg_create(parent[2]))
47584092dbcSRoman Gushchin 		goto cleanup;
47684092dbcSRoman Gushchin 
47784092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
47884092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
47984092dbcSRoman Gushchin 		if (!children[i])
48084092dbcSRoman Gushchin 			goto cleanup;
48184092dbcSRoman Gushchin 
48284092dbcSRoman Gushchin 		if (cg_create(children[i]))
48384092dbcSRoman Gushchin 			goto cleanup;
48484092dbcSRoman Gushchin 
485f0cdaa56SDavid Vernet 		if (i > 2)
48684092dbcSRoman Gushchin 			continue;
48784092dbcSRoman Gushchin 
48884092dbcSRoman Gushchin 		if (cg_run(children[i], alloc_pagecache_50M, (void *)(long)fd))
48984092dbcSRoman Gushchin 			goto cleanup;
49084092dbcSRoman Gushchin 	}
49184092dbcSRoman Gushchin 
49284092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.low", "50M"))
49384092dbcSRoman Gushchin 		goto cleanup;
49484092dbcSRoman Gushchin 	if (cg_write(parent[1], "memory.low", "50M"))
49584092dbcSRoman Gushchin 		goto cleanup;
49684092dbcSRoman Gushchin 	if (cg_write(children[0], "memory.low", "75M"))
49784092dbcSRoman Gushchin 		goto cleanup;
49884092dbcSRoman Gushchin 	if (cg_write(children[1], "memory.low", "25M"))
49984092dbcSRoman Gushchin 		goto cleanup;
500f0cdaa56SDavid Vernet 	if (cg_write(children[2], "memory.low", "0"))
50184092dbcSRoman Gushchin 		goto cleanup;
502f0cdaa56SDavid Vernet 	if (cg_write(children[3], "memory.low", "500M"))
50384092dbcSRoman Gushchin 		goto cleanup;
50484092dbcSRoman Gushchin 
50584092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
50684092dbcSRoman Gushchin 		goto cleanup;
50784092dbcSRoman Gushchin 
50884092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
50984092dbcSRoman Gushchin 		goto cleanup;
51084092dbcSRoman Gushchin 
51184092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
51284092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
51384092dbcSRoman Gushchin 
51484092dbcSRoman Gushchin 	if (!values_close(c[0], MB(33), 10))
51584092dbcSRoman Gushchin 		goto cleanup;
51684092dbcSRoman Gushchin 
51784092dbcSRoman Gushchin 	if (!values_close(c[1], MB(17), 10))
51884092dbcSRoman Gushchin 		goto cleanup;
51984092dbcSRoman Gushchin 
520f0cdaa56SDavid Vernet 	if (c[3] != 0)
52184092dbcSRoman Gushchin 		goto cleanup;
52284092dbcSRoman Gushchin 
52384092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(166))) {
52484092dbcSRoman Gushchin 		fprintf(stderr,
52584092dbcSRoman Gushchin 			"memory.low prevents from allocating anon memory\n");
52684092dbcSRoman Gushchin 		goto cleanup;
52784092dbcSRoman Gushchin 	}
52884092dbcSRoman Gushchin 
52984092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
530*cdc69458SDavid Vernet 		int no_low_events_index = has_recursiveprot ? 2 : 1;
531*cdc69458SDavid Vernet 
53284092dbcSRoman Gushchin 		oom = cg_read_key_long(children[i], "memory.events", "oom ");
53384092dbcSRoman Gushchin 		low = cg_read_key_long(children[i], "memory.events", "low ");
53484092dbcSRoman Gushchin 
53584092dbcSRoman Gushchin 		if (oom)
53684092dbcSRoman Gushchin 			goto cleanup;
537*cdc69458SDavid Vernet 		if (i <= no_low_events_index && low <= 0)
53884092dbcSRoman Gushchin 			goto cleanup;
539*cdc69458SDavid Vernet 		if (i > no_low_events_index && low)
54084092dbcSRoman Gushchin 			goto cleanup;
541*cdc69458SDavid Vernet 
54284092dbcSRoman Gushchin 	}
54384092dbcSRoman Gushchin 
54484092dbcSRoman Gushchin 	ret = KSFT_PASS;
54584092dbcSRoman Gushchin 
54684092dbcSRoman Gushchin cleanup:
54784092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
54884092dbcSRoman Gushchin 		if (!children[i])
54984092dbcSRoman Gushchin 			continue;
55084092dbcSRoman Gushchin 
55184092dbcSRoman Gushchin 		cg_destroy(children[i]);
55284092dbcSRoman Gushchin 		free(children[i]);
55384092dbcSRoman Gushchin 	}
55484092dbcSRoman Gushchin 
55584092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
55684092dbcSRoman Gushchin 		if (!parent[i])
55784092dbcSRoman Gushchin 			continue;
55884092dbcSRoman Gushchin 
55984092dbcSRoman Gushchin 		cg_destroy(parent[i]);
56084092dbcSRoman Gushchin 		free(parent[i]);
56184092dbcSRoman Gushchin 	}
56284092dbcSRoman Gushchin 	close(fd);
56384092dbcSRoman Gushchin 	return ret;
56484092dbcSRoman Gushchin }
56584092dbcSRoman Gushchin 
56684092dbcSRoman Gushchin static int alloc_pagecache_max_30M(const char *cgroup, void *arg)
56784092dbcSRoman Gushchin {
56884092dbcSRoman Gushchin 	size_t size = MB(50);
56984092dbcSRoman Gushchin 	int ret = -1;
57084092dbcSRoman Gushchin 	long current;
57184092dbcSRoman Gushchin 	int fd;
57284092dbcSRoman Gushchin 
57384092dbcSRoman Gushchin 	fd = get_temp_fd();
57484092dbcSRoman Gushchin 	if (fd < 0)
57584092dbcSRoman Gushchin 		return -1;
57684092dbcSRoman Gushchin 
57784092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
57884092dbcSRoman Gushchin 		goto cleanup;
57984092dbcSRoman Gushchin 
58084092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
58184092dbcSRoman Gushchin 	if (current <= MB(29) || current > MB(30))
58284092dbcSRoman Gushchin 		goto cleanup;
58384092dbcSRoman Gushchin 
58484092dbcSRoman Gushchin 	ret = 0;
58584092dbcSRoman Gushchin 
58684092dbcSRoman Gushchin cleanup:
58784092dbcSRoman Gushchin 	close(fd);
58884092dbcSRoman Gushchin 	return ret;
58984092dbcSRoman Gushchin 
59084092dbcSRoman Gushchin }
59184092dbcSRoman Gushchin 
59284092dbcSRoman Gushchin /*
59384092dbcSRoman Gushchin  * This test checks that memory.high limits the amount of
59484092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
59584092dbcSRoman Gushchin  * or pagecache.
59684092dbcSRoman Gushchin  */
59784092dbcSRoman Gushchin static int test_memcg_high(const char *root)
59884092dbcSRoman Gushchin {
59984092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
60084092dbcSRoman Gushchin 	char *memcg;
60184092dbcSRoman Gushchin 	long high;
60284092dbcSRoman Gushchin 
60384092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
60484092dbcSRoman Gushchin 	if (!memcg)
60584092dbcSRoman Gushchin 		goto cleanup;
60684092dbcSRoman Gushchin 
60784092dbcSRoman Gushchin 	if (cg_create(memcg))
60884092dbcSRoman Gushchin 		goto cleanup;
60984092dbcSRoman Gushchin 
61084092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.high", "max\n"))
61184092dbcSRoman Gushchin 		goto cleanup;
61284092dbcSRoman Gushchin 
61384092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
61484092dbcSRoman Gushchin 		goto cleanup;
61584092dbcSRoman Gushchin 
61684092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.high", "30M"))
61784092dbcSRoman Gushchin 		goto cleanup;
61884092dbcSRoman Gushchin 
619be74553fSRoman Gushchin 	if (cg_run(memcg, alloc_anon, (void *)MB(31)))
62084092dbcSRoman Gushchin 		goto cleanup;
62184092dbcSRoman Gushchin 
62284092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_pagecache_50M_check, NULL))
62384092dbcSRoman Gushchin 		goto cleanup;
62484092dbcSRoman Gushchin 
62584092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
62684092dbcSRoman Gushchin 		goto cleanup;
62784092dbcSRoman Gushchin 
62884092dbcSRoman Gushchin 	high = cg_read_key_long(memcg, "memory.events", "high ");
62984092dbcSRoman Gushchin 	if (high <= 0)
63084092dbcSRoman Gushchin 		goto cleanup;
63184092dbcSRoman Gushchin 
63284092dbcSRoman Gushchin 	ret = KSFT_PASS;
63384092dbcSRoman Gushchin 
63484092dbcSRoman Gushchin cleanup:
63584092dbcSRoman Gushchin 	cg_destroy(memcg);
63684092dbcSRoman Gushchin 	free(memcg);
63784092dbcSRoman Gushchin 
63884092dbcSRoman Gushchin 	return ret;
63984092dbcSRoman Gushchin }
64084092dbcSRoman Gushchin 
6416323ec54SShakeel Butt static int alloc_anon_mlock(const char *cgroup, void *arg)
6426323ec54SShakeel Butt {
6436323ec54SShakeel Butt 	size_t size = (size_t)arg;
6446323ec54SShakeel Butt 	void *buf;
6456323ec54SShakeel Butt 
6466323ec54SShakeel Butt 	buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
6476323ec54SShakeel Butt 		   0, 0);
6486323ec54SShakeel Butt 	if (buf == MAP_FAILED)
6496323ec54SShakeel Butt 		return -1;
6506323ec54SShakeel Butt 
6516323ec54SShakeel Butt 	mlock(buf, size);
6526323ec54SShakeel Butt 	munmap(buf, size);
6536323ec54SShakeel Butt 	return 0;
6546323ec54SShakeel Butt }
6556323ec54SShakeel Butt 
6566323ec54SShakeel Butt /*
6576323ec54SShakeel Butt  * This test checks that memory.high is able to throttle big single shot
6586323ec54SShakeel Butt  * allocation i.e. large allocation within one kernel entry.
6596323ec54SShakeel Butt  */
6606323ec54SShakeel Butt static int test_memcg_high_sync(const char *root)
6616323ec54SShakeel Butt {
6626323ec54SShakeel Butt 	int ret = KSFT_FAIL, pid, fd = -1;
6636323ec54SShakeel Butt 	char *memcg;
6646323ec54SShakeel Butt 	long pre_high, pre_max;
6656323ec54SShakeel Butt 	long post_high, post_max;
6666323ec54SShakeel Butt 
6676323ec54SShakeel Butt 	memcg = cg_name(root, "memcg_test");
6686323ec54SShakeel Butt 	if (!memcg)
6696323ec54SShakeel Butt 		goto cleanup;
6706323ec54SShakeel Butt 
6716323ec54SShakeel Butt 	if (cg_create(memcg))
6726323ec54SShakeel Butt 		goto cleanup;
6736323ec54SShakeel Butt 
6746323ec54SShakeel Butt 	pre_high = cg_read_key_long(memcg, "memory.events", "high ");
6756323ec54SShakeel Butt 	pre_max = cg_read_key_long(memcg, "memory.events", "max ");
6766323ec54SShakeel Butt 	if (pre_high < 0 || pre_max < 0)
6776323ec54SShakeel Butt 		goto cleanup;
6786323ec54SShakeel Butt 
6796323ec54SShakeel Butt 	if (cg_write(memcg, "memory.swap.max", "0"))
6806323ec54SShakeel Butt 		goto cleanup;
6816323ec54SShakeel Butt 
6826323ec54SShakeel Butt 	if (cg_write(memcg, "memory.high", "30M"))
6836323ec54SShakeel Butt 		goto cleanup;
6846323ec54SShakeel Butt 
6856323ec54SShakeel Butt 	if (cg_write(memcg, "memory.max", "140M"))
6866323ec54SShakeel Butt 		goto cleanup;
6876323ec54SShakeel Butt 
6886323ec54SShakeel Butt 	fd = memcg_prepare_for_wait(memcg);
6896323ec54SShakeel Butt 	if (fd < 0)
6906323ec54SShakeel Butt 		goto cleanup;
6916323ec54SShakeel Butt 
6926323ec54SShakeel Butt 	pid = cg_run_nowait(memcg, alloc_anon_mlock, (void *)MB(200));
6936323ec54SShakeel Butt 	if (pid < 0)
6946323ec54SShakeel Butt 		goto cleanup;
6956323ec54SShakeel Butt 
6966323ec54SShakeel Butt 	cg_wait_for(fd);
6976323ec54SShakeel Butt 
6986323ec54SShakeel Butt 	post_high = cg_read_key_long(memcg, "memory.events", "high ");
6996323ec54SShakeel Butt 	post_max = cg_read_key_long(memcg, "memory.events", "max ");
7006323ec54SShakeel Butt 	if (post_high < 0 || post_max < 0)
7016323ec54SShakeel Butt 		goto cleanup;
7026323ec54SShakeel Butt 
7036323ec54SShakeel Butt 	if (pre_high == post_high || pre_max != post_max)
7046323ec54SShakeel Butt 		goto cleanup;
7056323ec54SShakeel Butt 
7066323ec54SShakeel Butt 	ret = KSFT_PASS;
7076323ec54SShakeel Butt 
7086323ec54SShakeel Butt cleanup:
7096323ec54SShakeel Butt 	if (fd >= 0)
7106323ec54SShakeel Butt 		close(fd);
7116323ec54SShakeel Butt 	cg_destroy(memcg);
7126323ec54SShakeel Butt 	free(memcg);
7136323ec54SShakeel Butt 
7146323ec54SShakeel Butt 	return ret;
7156323ec54SShakeel Butt }
7166323ec54SShakeel Butt 
71784092dbcSRoman Gushchin /*
71884092dbcSRoman Gushchin  * This test checks that memory.max limits the amount of
71984092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
72084092dbcSRoman Gushchin  * or pagecache.
72184092dbcSRoman Gushchin  */
72284092dbcSRoman Gushchin static int test_memcg_max(const char *root)
72384092dbcSRoman Gushchin {
72484092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
72584092dbcSRoman Gushchin 	char *memcg;
72684092dbcSRoman Gushchin 	long current, max;
72784092dbcSRoman Gushchin 
72884092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
72984092dbcSRoman Gushchin 	if (!memcg)
73084092dbcSRoman Gushchin 		goto cleanup;
73184092dbcSRoman Gushchin 
73284092dbcSRoman Gushchin 	if (cg_create(memcg))
73384092dbcSRoman Gushchin 		goto cleanup;
73484092dbcSRoman Gushchin 
73584092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
73684092dbcSRoman Gushchin 		goto cleanup;
73784092dbcSRoman Gushchin 
73884092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
73984092dbcSRoman Gushchin 		goto cleanup;
74084092dbcSRoman Gushchin 
74184092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
74284092dbcSRoman Gushchin 		goto cleanup;
74384092dbcSRoman Gushchin 
74484092dbcSRoman Gushchin 	/* Should be killed by OOM killer */
74584092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
74684092dbcSRoman Gushchin 		goto cleanup;
74784092dbcSRoman Gushchin 
74884092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
74984092dbcSRoman Gushchin 		goto cleanup;
75084092dbcSRoman Gushchin 
75184092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
75284092dbcSRoman Gushchin 	if (current > MB(30) || !current)
75384092dbcSRoman Gushchin 		goto cleanup;
75484092dbcSRoman Gushchin 
75584092dbcSRoman Gushchin 	max = cg_read_key_long(memcg, "memory.events", "max ");
75684092dbcSRoman Gushchin 	if (max <= 0)
75784092dbcSRoman Gushchin 		goto cleanup;
75884092dbcSRoman Gushchin 
75984092dbcSRoman Gushchin 	ret = KSFT_PASS;
76084092dbcSRoman Gushchin 
76184092dbcSRoman Gushchin cleanup:
76284092dbcSRoman Gushchin 	cg_destroy(memcg);
76384092dbcSRoman Gushchin 	free(memcg);
76484092dbcSRoman Gushchin 
76584092dbcSRoman Gushchin 	return ret;
76684092dbcSRoman Gushchin }
76784092dbcSRoman Gushchin 
768eae3cb2eSYosry Ahmed /*
769eae3cb2eSYosry Ahmed  * This test checks that memory.reclaim reclaims the given
770eae3cb2eSYosry Ahmed  * amount of memory (from both anon and file, if possible).
771eae3cb2eSYosry Ahmed  */
772eae3cb2eSYosry Ahmed static int test_memcg_reclaim(const char *root)
773eae3cb2eSYosry Ahmed {
774eae3cb2eSYosry Ahmed 	int ret = KSFT_FAIL, fd, retries;
775eae3cb2eSYosry Ahmed 	char *memcg;
776eae3cb2eSYosry Ahmed 	long current, expected_usage, to_reclaim;
777eae3cb2eSYosry Ahmed 	char buf[64];
778eae3cb2eSYosry Ahmed 
779eae3cb2eSYosry Ahmed 	memcg = cg_name(root, "memcg_test");
780eae3cb2eSYosry Ahmed 	if (!memcg)
781eae3cb2eSYosry Ahmed 		goto cleanup;
782eae3cb2eSYosry Ahmed 
783eae3cb2eSYosry Ahmed 	if (cg_create(memcg))
784eae3cb2eSYosry Ahmed 		goto cleanup;
785eae3cb2eSYosry Ahmed 
786eae3cb2eSYosry Ahmed 	current = cg_read_long(memcg, "memory.current");
787eae3cb2eSYosry Ahmed 	if (current != 0)
788eae3cb2eSYosry Ahmed 		goto cleanup;
789eae3cb2eSYosry Ahmed 
790eae3cb2eSYosry Ahmed 	fd = get_temp_fd();
791eae3cb2eSYosry Ahmed 	if (fd < 0)
792eae3cb2eSYosry Ahmed 		goto cleanup;
793eae3cb2eSYosry Ahmed 
794eae3cb2eSYosry Ahmed 	cg_run_nowait(memcg, alloc_pagecache_50M_noexit, (void *)(long)fd);
795eae3cb2eSYosry Ahmed 
796eae3cb2eSYosry Ahmed 	/*
797eae3cb2eSYosry Ahmed 	 * If swap is enabled, try to reclaim from both anon and file, else try
798eae3cb2eSYosry Ahmed 	 * to reclaim from file only.
799eae3cb2eSYosry Ahmed 	 */
800eae3cb2eSYosry Ahmed 	if (is_swap_enabled()) {
801eae3cb2eSYosry Ahmed 		cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(50));
802eae3cb2eSYosry Ahmed 		expected_usage = MB(100);
803eae3cb2eSYosry Ahmed 	} else
804eae3cb2eSYosry Ahmed 		expected_usage = MB(50);
805eae3cb2eSYosry Ahmed 
806eae3cb2eSYosry Ahmed 	/*
807eae3cb2eSYosry Ahmed 	 * Wait until current usage reaches the expected usage (or we run out of
808eae3cb2eSYosry Ahmed 	 * retries).
809eae3cb2eSYosry Ahmed 	 */
810eae3cb2eSYosry Ahmed 	retries = 5;
811eae3cb2eSYosry Ahmed 	while (!values_close(cg_read_long(memcg, "memory.current"),
812eae3cb2eSYosry Ahmed 			    expected_usage, 10)) {
813eae3cb2eSYosry Ahmed 		if (retries--) {
814eae3cb2eSYosry Ahmed 			sleep(1);
815eae3cb2eSYosry Ahmed 			continue;
816eae3cb2eSYosry Ahmed 		} else {
817eae3cb2eSYosry Ahmed 			fprintf(stderr,
818eae3cb2eSYosry Ahmed 				"failed to allocate %ld for memcg reclaim test\n",
819eae3cb2eSYosry Ahmed 				expected_usage);
820eae3cb2eSYosry Ahmed 			goto cleanup;
821eae3cb2eSYosry Ahmed 		}
822eae3cb2eSYosry Ahmed 	}
823eae3cb2eSYosry Ahmed 
824eae3cb2eSYosry Ahmed 	/*
825eae3cb2eSYosry Ahmed 	 * Reclaim until current reaches 30M, this makes sure we hit both anon
826eae3cb2eSYosry Ahmed 	 * and file if swap is enabled.
827eae3cb2eSYosry Ahmed 	 */
828eae3cb2eSYosry Ahmed 	retries = 5;
829eae3cb2eSYosry Ahmed 	while (true) {
830eae3cb2eSYosry Ahmed 		int err;
831eae3cb2eSYosry Ahmed 
832eae3cb2eSYosry Ahmed 		current = cg_read_long(memcg, "memory.current");
833eae3cb2eSYosry Ahmed 		to_reclaim = current - MB(30);
834eae3cb2eSYosry Ahmed 
835eae3cb2eSYosry Ahmed 		/*
836eae3cb2eSYosry Ahmed 		 * We only keep looping if we get EAGAIN, which means we could
837eae3cb2eSYosry Ahmed 		 * not reclaim the full amount.
838eae3cb2eSYosry Ahmed 		 */
839eae3cb2eSYosry Ahmed 		if (to_reclaim <= 0)
840eae3cb2eSYosry Ahmed 			goto cleanup;
841eae3cb2eSYosry Ahmed 
842eae3cb2eSYosry Ahmed 
843eae3cb2eSYosry Ahmed 		snprintf(buf, sizeof(buf), "%ld", to_reclaim);
844eae3cb2eSYosry Ahmed 		err = cg_write(memcg, "memory.reclaim", buf);
845eae3cb2eSYosry Ahmed 		if (!err) {
846eae3cb2eSYosry Ahmed 			/*
847eae3cb2eSYosry Ahmed 			 * If writing succeeds, then the written amount should have been
848eae3cb2eSYosry Ahmed 			 * fully reclaimed (and maybe more).
849eae3cb2eSYosry Ahmed 			 */
850eae3cb2eSYosry Ahmed 			current = cg_read_long(memcg, "memory.current");
851eae3cb2eSYosry Ahmed 			if (!values_close(current, MB(30), 3) && current > MB(30))
852eae3cb2eSYosry Ahmed 				goto cleanup;
853eae3cb2eSYosry Ahmed 			break;
854eae3cb2eSYosry Ahmed 		}
855eae3cb2eSYosry Ahmed 
856eae3cb2eSYosry Ahmed 		/* The kernel could not reclaim the full amount, try again. */
857eae3cb2eSYosry Ahmed 		if (err == -EAGAIN && retries--)
858eae3cb2eSYosry Ahmed 			continue;
859eae3cb2eSYosry Ahmed 
860eae3cb2eSYosry Ahmed 		/* We got an unexpected error or ran out of retries. */
861eae3cb2eSYosry Ahmed 		goto cleanup;
862eae3cb2eSYosry Ahmed 	}
863eae3cb2eSYosry Ahmed 
864eae3cb2eSYosry Ahmed 	ret = KSFT_PASS;
865eae3cb2eSYosry Ahmed cleanup:
866eae3cb2eSYosry Ahmed 	cg_destroy(memcg);
867eae3cb2eSYosry Ahmed 	free(memcg);
868eae3cb2eSYosry Ahmed 	close(fd);
869eae3cb2eSYosry Ahmed 
870eae3cb2eSYosry Ahmed 	return ret;
871eae3cb2eSYosry Ahmed }
872eae3cb2eSYosry Ahmed 
873478b2784SMike Rapoport static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
874478b2784SMike Rapoport {
875478b2784SMike Rapoport 	long mem_max = (long)arg;
876478b2784SMike Rapoport 	size_t size = MB(50);
877478b2784SMike Rapoport 	char *buf, *ptr;
878478b2784SMike Rapoport 	long mem_current, swap_current;
879478b2784SMike Rapoport 	int ret = -1;
880478b2784SMike Rapoport 
881478b2784SMike Rapoport 	buf = malloc(size);
882478b2784SMike Rapoport 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
883478b2784SMike Rapoport 		*ptr = 0;
884478b2784SMike Rapoport 
885478b2784SMike Rapoport 	mem_current = cg_read_long(cgroup, "memory.current");
886478b2784SMike Rapoport 	if (!mem_current || !values_close(mem_current, mem_max, 3))
887478b2784SMike Rapoport 		goto cleanup;
888478b2784SMike Rapoport 
889478b2784SMike Rapoport 	swap_current = cg_read_long(cgroup, "memory.swap.current");
890478b2784SMike Rapoport 	if (!swap_current ||
891478b2784SMike Rapoport 	    !values_close(mem_current + swap_current, size, 3))
892478b2784SMike Rapoport 		goto cleanup;
893478b2784SMike Rapoport 
894478b2784SMike Rapoport 	ret = 0;
895478b2784SMike Rapoport cleanup:
896478b2784SMike Rapoport 	free(buf);
897478b2784SMike Rapoport 	return ret;
898478b2784SMike Rapoport }
899478b2784SMike Rapoport 
900478b2784SMike Rapoport /*
901478b2784SMike Rapoport  * This test checks that memory.swap.max limits the amount of
902478b2784SMike Rapoport  * anonymous memory which can be swapped out.
903478b2784SMike Rapoport  */
904478b2784SMike Rapoport static int test_memcg_swap_max(const char *root)
905478b2784SMike Rapoport {
906478b2784SMike Rapoport 	int ret = KSFT_FAIL;
907478b2784SMike Rapoport 	char *memcg;
908478b2784SMike Rapoport 	long max;
909478b2784SMike Rapoport 
910478b2784SMike Rapoport 	if (!is_swap_enabled())
911478b2784SMike Rapoport 		return KSFT_SKIP;
912478b2784SMike Rapoport 
913478b2784SMike Rapoport 	memcg = cg_name(root, "memcg_test");
914478b2784SMike Rapoport 	if (!memcg)
915478b2784SMike Rapoport 		goto cleanup;
916478b2784SMike Rapoport 
917478b2784SMike Rapoport 	if (cg_create(memcg))
918478b2784SMike Rapoport 		goto cleanup;
919478b2784SMike Rapoport 
920478b2784SMike Rapoport 	if (cg_read_long(memcg, "memory.swap.current")) {
921478b2784SMike Rapoport 		ret = KSFT_SKIP;
922478b2784SMike Rapoport 		goto cleanup;
923478b2784SMike Rapoport 	}
924478b2784SMike Rapoport 
925478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
926478b2784SMike Rapoport 		goto cleanup;
927478b2784SMike Rapoport 
928478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.swap.max", "max\n"))
929478b2784SMike Rapoport 		goto cleanup;
930478b2784SMike Rapoport 
931478b2784SMike Rapoport 	if (cg_write(memcg, "memory.swap.max", "30M"))
932478b2784SMike Rapoport 		goto cleanup;
933478b2784SMike Rapoport 
934478b2784SMike Rapoport 	if (cg_write(memcg, "memory.max", "30M"))
935478b2784SMike Rapoport 		goto cleanup;
936478b2784SMike Rapoport 
937478b2784SMike Rapoport 	/* Should be killed by OOM killer */
938478b2784SMike Rapoport 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
939478b2784SMike Rapoport 		goto cleanup;
940478b2784SMike Rapoport 
941478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
942478b2784SMike Rapoport 		goto cleanup;
943478b2784SMike Rapoport 
944478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
945478b2784SMike Rapoport 		goto cleanup;
946478b2784SMike Rapoport 
947478b2784SMike Rapoport 	if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30)))
948478b2784SMike Rapoport 		goto cleanup;
949478b2784SMike Rapoport 
950478b2784SMike Rapoport 	max = cg_read_key_long(memcg, "memory.events", "max ");
951478b2784SMike Rapoport 	if (max <= 0)
952478b2784SMike Rapoport 		goto cleanup;
953478b2784SMike Rapoport 
954478b2784SMike Rapoport 	ret = KSFT_PASS;
955478b2784SMike Rapoport 
956478b2784SMike Rapoport cleanup:
957478b2784SMike Rapoport 	cg_destroy(memcg);
958478b2784SMike Rapoport 	free(memcg);
959478b2784SMike Rapoport 
960478b2784SMike Rapoport 	return ret;
961478b2784SMike Rapoport }
962478b2784SMike Rapoport 
96384092dbcSRoman Gushchin /*
96484092dbcSRoman Gushchin  * This test disables swapping and tries to allocate anonymous memory
96584092dbcSRoman Gushchin  * up to OOM. Then it checks for oom and oom_kill events in
96684092dbcSRoman Gushchin  * memory.events.
96784092dbcSRoman Gushchin  */
96884092dbcSRoman Gushchin static int test_memcg_oom_events(const char *root)
96984092dbcSRoman Gushchin {
97084092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
97184092dbcSRoman Gushchin 	char *memcg;
97284092dbcSRoman Gushchin 
97384092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
97484092dbcSRoman Gushchin 	if (!memcg)
97584092dbcSRoman Gushchin 		goto cleanup;
97684092dbcSRoman Gushchin 
97784092dbcSRoman Gushchin 	if (cg_create(memcg))
97884092dbcSRoman Gushchin 		goto cleanup;
97984092dbcSRoman Gushchin 
98084092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
98184092dbcSRoman Gushchin 		goto cleanup;
98284092dbcSRoman Gushchin 
98384092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
98484092dbcSRoman Gushchin 		goto cleanup;
98584092dbcSRoman Gushchin 
98684092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
98784092dbcSRoman Gushchin 		goto cleanup;
98884092dbcSRoman Gushchin 
98984092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "cgroup.procs", ""))
99084092dbcSRoman Gushchin 		goto cleanup;
99184092dbcSRoman Gushchin 
99284092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
99384092dbcSRoman Gushchin 		goto cleanup;
99484092dbcSRoman Gushchin 
99584092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
99684092dbcSRoman Gushchin 		goto cleanup;
99784092dbcSRoman Gushchin 
99884092dbcSRoman Gushchin 	ret = KSFT_PASS;
99984092dbcSRoman Gushchin 
100084092dbcSRoman Gushchin cleanup:
100184092dbcSRoman Gushchin 	cg_destroy(memcg);
100284092dbcSRoman Gushchin 	free(memcg);
100384092dbcSRoman Gushchin 
100484092dbcSRoman Gushchin 	return ret;
100584092dbcSRoman Gushchin }
100684092dbcSRoman Gushchin 
10075f8f0193SMike Rapoport struct tcp_server_args {
10085f8f0193SMike Rapoport 	unsigned short port;
10095f8f0193SMike Rapoport 	int ctl[2];
10105f8f0193SMike Rapoport };
10115f8f0193SMike Rapoport 
10125f8f0193SMike Rapoport static int tcp_server(const char *cgroup, void *arg)
10135f8f0193SMike Rapoport {
10145f8f0193SMike Rapoport 	struct tcp_server_args *srv_args = arg;
10155f8f0193SMike Rapoport 	struct sockaddr_in6 saddr = { 0 };
10165f8f0193SMike Rapoport 	socklen_t slen = sizeof(saddr);
10175f8f0193SMike Rapoport 	int sk, client_sk, ctl_fd, yes = 1, ret = -1;
10185f8f0193SMike Rapoport 
10195f8f0193SMike Rapoport 	close(srv_args->ctl[0]);
10205f8f0193SMike Rapoport 	ctl_fd = srv_args->ctl[1];
10215f8f0193SMike Rapoport 
10225f8f0193SMike Rapoport 	saddr.sin6_family = AF_INET6;
10235f8f0193SMike Rapoport 	saddr.sin6_addr = in6addr_any;
10245f8f0193SMike Rapoport 	saddr.sin6_port = htons(srv_args->port);
10255f8f0193SMike Rapoport 
10265f8f0193SMike Rapoport 	sk = socket(AF_INET6, SOCK_STREAM, 0);
10275f8f0193SMike Rapoport 	if (sk < 0)
10285f8f0193SMike Rapoport 		return ret;
10295f8f0193SMike Rapoport 
10305f8f0193SMike Rapoport 	if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
10315f8f0193SMike Rapoport 		goto cleanup;
10325f8f0193SMike Rapoport 
10335f8f0193SMike Rapoport 	if (bind(sk, (struct sockaddr *)&saddr, slen)) {
10345f8f0193SMike Rapoport 		write(ctl_fd, &errno, sizeof(errno));
10355f8f0193SMike Rapoport 		goto cleanup;
10365f8f0193SMike Rapoport 	}
10375f8f0193SMike Rapoport 
10385f8f0193SMike Rapoport 	if (listen(sk, 1))
10395f8f0193SMike Rapoport 		goto cleanup;
10405f8f0193SMike Rapoport 
10415f8f0193SMike Rapoport 	ret = 0;
10425f8f0193SMike Rapoport 	if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) {
10435f8f0193SMike Rapoport 		ret = -1;
10445f8f0193SMike Rapoport 		goto cleanup;
10455f8f0193SMike Rapoport 	}
10465f8f0193SMike Rapoport 
10475f8f0193SMike Rapoport 	client_sk = accept(sk, NULL, NULL);
10485f8f0193SMike Rapoport 	if (client_sk < 0)
10495f8f0193SMike Rapoport 		goto cleanup;
10505f8f0193SMike Rapoport 
10515f8f0193SMike Rapoport 	ret = -1;
10525f8f0193SMike Rapoport 	for (;;) {
10535f8f0193SMike Rapoport 		uint8_t buf[0x100000];
10545f8f0193SMike Rapoport 
10555f8f0193SMike Rapoport 		if (write(client_sk, buf, sizeof(buf)) <= 0) {
10565f8f0193SMike Rapoport 			if (errno == ECONNRESET)
10575f8f0193SMike Rapoport 				ret = 0;
10585f8f0193SMike Rapoport 			break;
10595f8f0193SMike Rapoport 		}
10605f8f0193SMike Rapoport 	}
10615f8f0193SMike Rapoport 
10625f8f0193SMike Rapoport 	close(client_sk);
10635f8f0193SMike Rapoport 
10645f8f0193SMike Rapoport cleanup:
10655f8f0193SMike Rapoport 	close(sk);
10665f8f0193SMike Rapoport 	return ret;
10675f8f0193SMike Rapoport }
10685f8f0193SMike Rapoport 
10695f8f0193SMike Rapoport static int tcp_client(const char *cgroup, unsigned short port)
10705f8f0193SMike Rapoport {
10715f8f0193SMike Rapoport 	const char server[] = "localhost";
10725f8f0193SMike Rapoport 	struct addrinfo *ai;
10735f8f0193SMike Rapoport 	char servport[6];
10745f8f0193SMike Rapoport 	int retries = 0x10; /* nice round number */
10755f8f0193SMike Rapoport 	int sk, ret;
10765f8f0193SMike Rapoport 
10775f8f0193SMike Rapoport 	snprintf(servport, sizeof(servport), "%hd", port);
10785f8f0193SMike Rapoport 	ret = getaddrinfo(server, servport, NULL, &ai);
10795f8f0193SMike Rapoport 	if (ret)
10805f8f0193SMike Rapoport 		return ret;
10815f8f0193SMike Rapoport 
10825f8f0193SMike Rapoport 	sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
10835f8f0193SMike Rapoport 	if (sk < 0)
10845f8f0193SMike Rapoport 		goto free_ainfo;
10855f8f0193SMike Rapoport 
10865f8f0193SMike Rapoport 	ret = connect(sk, ai->ai_addr, ai->ai_addrlen);
10875f8f0193SMike Rapoport 	if (ret < 0)
10885f8f0193SMike Rapoport 		goto close_sk;
10895f8f0193SMike Rapoport 
10905f8f0193SMike Rapoport 	ret = KSFT_FAIL;
10915f8f0193SMike Rapoport 	while (retries--) {
10925f8f0193SMike Rapoport 		uint8_t buf[0x100000];
10935f8f0193SMike Rapoport 		long current, sock;
10945f8f0193SMike Rapoport 
10955f8f0193SMike Rapoport 		if (read(sk, buf, sizeof(buf)) <= 0)
10965f8f0193SMike Rapoport 			goto close_sk;
10975f8f0193SMike Rapoport 
10985f8f0193SMike Rapoport 		current = cg_read_long(cgroup, "memory.current");
10995f8f0193SMike Rapoport 		sock = cg_read_key_long(cgroup, "memory.stat", "sock ");
11005f8f0193SMike Rapoport 
11015f8f0193SMike Rapoport 		if (current < 0 || sock < 0)
11025f8f0193SMike Rapoport 			goto close_sk;
11035f8f0193SMike Rapoport 
11045f8f0193SMike Rapoport 		if (current < sock)
11055f8f0193SMike Rapoport 			goto close_sk;
11065f8f0193SMike Rapoport 
11075f8f0193SMike Rapoport 		if (values_close(current, sock, 10)) {
11085f8f0193SMike Rapoport 			ret = KSFT_PASS;
11095f8f0193SMike Rapoport 			break;
11105f8f0193SMike Rapoport 		}
11115f8f0193SMike Rapoport 	}
11125f8f0193SMike Rapoport 
11135f8f0193SMike Rapoport close_sk:
11145f8f0193SMike Rapoport 	close(sk);
11155f8f0193SMike Rapoport free_ainfo:
11165f8f0193SMike Rapoport 	freeaddrinfo(ai);
11175f8f0193SMike Rapoport 	return ret;
11185f8f0193SMike Rapoport }
11195f8f0193SMike Rapoport 
11205f8f0193SMike Rapoport /*
11215f8f0193SMike Rapoport  * This test checks socket memory accounting.
11225f8f0193SMike Rapoport  * The test forks a TCP server listens on a random port between 1000
11235f8f0193SMike Rapoport  * and 61000. Once it gets a client connection, it starts writing to
11245f8f0193SMike Rapoport  * its socket.
11255f8f0193SMike Rapoport  * The TCP client interleaves reads from the socket with check whether
11265f8f0193SMike Rapoport  * memory.current and memory.stat.sock are similar.
11275f8f0193SMike Rapoport  */
11285f8f0193SMike Rapoport static int test_memcg_sock(const char *root)
11295f8f0193SMike Rapoport {
11305f8f0193SMike Rapoport 	int bind_retries = 5, ret = KSFT_FAIL, pid, err;
11315f8f0193SMike Rapoport 	unsigned short port;
11325f8f0193SMike Rapoport 	char *memcg;
11335f8f0193SMike Rapoport 
11345f8f0193SMike Rapoport 	memcg = cg_name(root, "memcg_test");
11355f8f0193SMike Rapoport 	if (!memcg)
11365f8f0193SMike Rapoport 		goto cleanup;
11375f8f0193SMike Rapoport 
11385f8f0193SMike Rapoport 	if (cg_create(memcg))
11395f8f0193SMike Rapoport 		goto cleanup;
11405f8f0193SMike Rapoport 
11415f8f0193SMike Rapoport 	while (bind_retries--) {
11425f8f0193SMike Rapoport 		struct tcp_server_args args;
11435f8f0193SMike Rapoport 
11445f8f0193SMike Rapoport 		if (pipe(args.ctl))
11455f8f0193SMike Rapoport 			goto cleanup;
11465f8f0193SMike Rapoport 
11475f8f0193SMike Rapoport 		port = args.port = 1000 + rand() % 60000;
11485f8f0193SMike Rapoport 
11495f8f0193SMike Rapoport 		pid = cg_run_nowait(memcg, tcp_server, &args);
11505f8f0193SMike Rapoport 		if (pid < 0)
11515f8f0193SMike Rapoport 			goto cleanup;
11525f8f0193SMike Rapoport 
11535f8f0193SMike Rapoport 		close(args.ctl[1]);
11545f8f0193SMike Rapoport 		if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err))
11555f8f0193SMike Rapoport 			goto cleanup;
11565f8f0193SMike Rapoport 		close(args.ctl[0]);
11575f8f0193SMike Rapoport 
11585f8f0193SMike Rapoport 		if (!err)
11595f8f0193SMike Rapoport 			break;
11605f8f0193SMike Rapoport 		if (err != EADDRINUSE)
11615f8f0193SMike Rapoport 			goto cleanup;
11625f8f0193SMike Rapoport 
11635f8f0193SMike Rapoport 		waitpid(pid, NULL, 0);
11645f8f0193SMike Rapoport 	}
11655f8f0193SMike Rapoport 
11665f8f0193SMike Rapoport 	if (err == EADDRINUSE) {
11675f8f0193SMike Rapoport 		ret = KSFT_SKIP;
11685f8f0193SMike Rapoport 		goto cleanup;
11695f8f0193SMike Rapoport 	}
11705f8f0193SMike Rapoport 
11715f8f0193SMike Rapoport 	if (tcp_client(memcg, port) != KSFT_PASS)
11725f8f0193SMike Rapoport 		goto cleanup;
11735f8f0193SMike Rapoport 
11745f8f0193SMike Rapoport 	waitpid(pid, &err, 0);
11755f8f0193SMike Rapoport 	if (WEXITSTATUS(err))
11765f8f0193SMike Rapoport 		goto cleanup;
11775f8f0193SMike Rapoport 
11785f8f0193SMike Rapoport 	if (cg_read_long(memcg, "memory.current") < 0)
11795f8f0193SMike Rapoport 		goto cleanup;
11805f8f0193SMike Rapoport 
11815f8f0193SMike Rapoport 	if (cg_read_key_long(memcg, "memory.stat", "sock "))
11825f8f0193SMike Rapoport 		goto cleanup;
11835f8f0193SMike Rapoport 
11845f8f0193SMike Rapoport 	ret = KSFT_PASS;
11855f8f0193SMike Rapoport 
11865f8f0193SMike Rapoport cleanup:
11875f8f0193SMike Rapoport 	cg_destroy(memcg);
11885f8f0193SMike Rapoport 	free(memcg);
11895f8f0193SMike Rapoport 
11905f8f0193SMike Rapoport 	return ret;
11915f8f0193SMike Rapoport }
11925f8f0193SMike Rapoport 
1193a987785dSJay Kamat /*
1194a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1195a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1196c85bcc91SRoman Gushchin  * processes in the leaf were killed. It also checks that oom_events
1197c85bcc91SRoman Gushchin  * were propagated to the parent level.
1198a987785dSJay Kamat  */
1199a987785dSJay Kamat static int test_memcg_oom_group_leaf_events(const char *root)
1200a987785dSJay Kamat {
1201a987785dSJay Kamat 	int ret = KSFT_FAIL;
1202a987785dSJay Kamat 	char *parent, *child;
1203a987785dSJay Kamat 
1204a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1205a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1206a987785dSJay Kamat 
1207a987785dSJay Kamat 	if (!parent || !child)
1208a987785dSJay Kamat 		goto cleanup;
1209a987785dSJay Kamat 
1210a987785dSJay Kamat 	if (cg_create(parent))
1211a987785dSJay Kamat 		goto cleanup;
1212a987785dSJay Kamat 
1213a987785dSJay Kamat 	if (cg_create(child))
1214a987785dSJay Kamat 		goto cleanup;
1215a987785dSJay Kamat 
1216a987785dSJay Kamat 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
1217a987785dSJay Kamat 		goto cleanup;
1218a987785dSJay Kamat 
1219a987785dSJay Kamat 	if (cg_write(child, "memory.max", "50M"))
1220a987785dSJay Kamat 		goto cleanup;
1221a987785dSJay Kamat 
1222a987785dSJay Kamat 	if (cg_write(child, "memory.swap.max", "0"))
1223a987785dSJay Kamat 		goto cleanup;
1224a987785dSJay Kamat 
1225a987785dSJay Kamat 	if (cg_write(child, "memory.oom.group", "1"))
1226a987785dSJay Kamat 		goto cleanup;
1227a987785dSJay Kamat 
1228a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1229a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1230a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1231a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1232a987785dSJay Kamat 		goto cleanup;
1233a987785dSJay Kamat 
1234a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1235a987785dSJay Kamat 		goto cleanup;
1236a987785dSJay Kamat 
1237a987785dSJay Kamat 	if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
1238a987785dSJay Kamat 		goto cleanup;
1239a987785dSJay Kamat 
1240c85bcc91SRoman Gushchin 	if (cg_read_key_long(parent, "memory.events", "oom_kill ") <= 0)
1241a987785dSJay Kamat 		goto cleanup;
1242a987785dSJay Kamat 
1243a987785dSJay Kamat 	ret = KSFT_PASS;
1244a987785dSJay Kamat 
1245a987785dSJay Kamat cleanup:
1246a987785dSJay Kamat 	if (child)
1247a987785dSJay Kamat 		cg_destroy(child);
1248a987785dSJay Kamat 	if (parent)
1249a987785dSJay Kamat 		cg_destroy(parent);
1250a987785dSJay Kamat 	free(child);
1251a987785dSJay Kamat 	free(parent);
1252a987785dSJay Kamat 
1253a987785dSJay Kamat 	return ret;
1254a987785dSJay Kamat }
1255a987785dSJay Kamat 
1256a987785dSJay Kamat /*
1257a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1258a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1259a987785dSJay Kamat  * processes in the parent and leaf were killed.
1260a987785dSJay Kamat  */
1261a987785dSJay Kamat static int test_memcg_oom_group_parent_events(const char *root)
1262a987785dSJay Kamat {
1263a987785dSJay Kamat 	int ret = KSFT_FAIL;
1264a987785dSJay Kamat 	char *parent, *child;
1265a987785dSJay Kamat 
1266a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1267a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1268a987785dSJay Kamat 
1269a987785dSJay Kamat 	if (!parent || !child)
1270a987785dSJay Kamat 		goto cleanup;
1271a987785dSJay Kamat 
1272a987785dSJay Kamat 	if (cg_create(parent))
1273a987785dSJay Kamat 		goto cleanup;
1274a987785dSJay Kamat 
1275a987785dSJay Kamat 	if (cg_create(child))
1276a987785dSJay Kamat 		goto cleanup;
1277a987785dSJay Kamat 
1278a987785dSJay Kamat 	if (cg_write(parent, "memory.max", "80M"))
1279a987785dSJay Kamat 		goto cleanup;
1280a987785dSJay Kamat 
1281a987785dSJay Kamat 	if (cg_write(parent, "memory.swap.max", "0"))
1282a987785dSJay Kamat 		goto cleanup;
1283a987785dSJay Kamat 
1284a987785dSJay Kamat 	if (cg_write(parent, "memory.oom.group", "1"))
1285a987785dSJay Kamat 		goto cleanup;
1286a987785dSJay Kamat 
1287a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1288a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1289a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1290a987785dSJay Kamat 
1291a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1292a987785dSJay Kamat 		goto cleanup;
1293a987785dSJay Kamat 
1294a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1295a987785dSJay Kamat 		goto cleanup;
1296a987785dSJay Kamat 	if (cg_test_proc_killed(parent))
1297a987785dSJay Kamat 		goto cleanup;
1298a987785dSJay Kamat 
1299a987785dSJay Kamat 	ret = KSFT_PASS;
1300a987785dSJay Kamat 
1301a987785dSJay Kamat cleanup:
1302a987785dSJay Kamat 	if (child)
1303a987785dSJay Kamat 		cg_destroy(child);
1304a987785dSJay Kamat 	if (parent)
1305a987785dSJay Kamat 		cg_destroy(parent);
1306a987785dSJay Kamat 	free(child);
1307a987785dSJay Kamat 	free(parent);
1308a987785dSJay Kamat 
1309a987785dSJay Kamat 	return ret;
1310a987785dSJay Kamat }
1311a987785dSJay Kamat 
1312a987785dSJay Kamat /*
1313a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1314a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1315a987785dSJay Kamat  * processes were killed except those set with OOM_SCORE_ADJ_MIN
1316a987785dSJay Kamat  */
1317a987785dSJay Kamat static int test_memcg_oom_group_score_events(const char *root)
1318a987785dSJay Kamat {
1319a987785dSJay Kamat 	int ret = KSFT_FAIL;
1320a987785dSJay Kamat 	char *memcg;
1321a987785dSJay Kamat 	int safe_pid;
1322a987785dSJay Kamat 
1323a987785dSJay Kamat 	memcg = cg_name(root, "memcg_test_0");
1324a987785dSJay Kamat 
1325a987785dSJay Kamat 	if (!memcg)
1326a987785dSJay Kamat 		goto cleanup;
1327a987785dSJay Kamat 
1328a987785dSJay Kamat 	if (cg_create(memcg))
1329a987785dSJay Kamat 		goto cleanup;
1330a987785dSJay Kamat 
1331a987785dSJay Kamat 	if (cg_write(memcg, "memory.max", "50M"))
1332a987785dSJay Kamat 		goto cleanup;
1333a987785dSJay Kamat 
1334a987785dSJay Kamat 	if (cg_write(memcg, "memory.swap.max", "0"))
1335a987785dSJay Kamat 		goto cleanup;
1336a987785dSJay Kamat 
1337a987785dSJay Kamat 	if (cg_write(memcg, "memory.oom.group", "1"))
1338a987785dSJay Kamat 		goto cleanup;
1339a987785dSJay Kamat 
1340a987785dSJay Kamat 	safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1341a987785dSJay Kamat 	if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN))
1342a987785dSJay Kamat 		goto cleanup;
1343a987785dSJay Kamat 
1344a987785dSJay Kamat 	cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1345a987785dSJay Kamat 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
1346a987785dSJay Kamat 		goto cleanup;
1347a987785dSJay Kamat 
1348a987785dSJay Kamat 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3)
1349a987785dSJay Kamat 		goto cleanup;
1350a987785dSJay Kamat 
1351a987785dSJay Kamat 	if (kill(safe_pid, SIGKILL))
1352a987785dSJay Kamat 		goto cleanup;
1353a987785dSJay Kamat 
1354a987785dSJay Kamat 	ret = KSFT_PASS;
1355a987785dSJay Kamat 
1356a987785dSJay Kamat cleanup:
1357a987785dSJay Kamat 	if (memcg)
1358a987785dSJay Kamat 		cg_destroy(memcg);
1359a987785dSJay Kamat 	free(memcg);
1360a987785dSJay Kamat 
1361a987785dSJay Kamat 	return ret;
1362a987785dSJay Kamat }
1363a987785dSJay Kamat 
1364a987785dSJay Kamat 
136584092dbcSRoman Gushchin #define T(x) { x, #x }
136684092dbcSRoman Gushchin struct memcg_test {
136784092dbcSRoman Gushchin 	int (*fn)(const char *root);
136884092dbcSRoman Gushchin 	const char *name;
136984092dbcSRoman Gushchin } tests[] = {
137084092dbcSRoman Gushchin 	T(test_memcg_subtree_control),
137184092dbcSRoman Gushchin 	T(test_memcg_current),
137284092dbcSRoman Gushchin 	T(test_memcg_min),
137384092dbcSRoman Gushchin 	T(test_memcg_low),
137484092dbcSRoman Gushchin 	T(test_memcg_high),
13756323ec54SShakeel Butt 	T(test_memcg_high_sync),
137684092dbcSRoman Gushchin 	T(test_memcg_max),
1377eae3cb2eSYosry Ahmed 	T(test_memcg_reclaim),
137884092dbcSRoman Gushchin 	T(test_memcg_oom_events),
1379478b2784SMike Rapoport 	T(test_memcg_swap_max),
13805f8f0193SMike Rapoport 	T(test_memcg_sock),
1381a987785dSJay Kamat 	T(test_memcg_oom_group_leaf_events),
1382a987785dSJay Kamat 	T(test_memcg_oom_group_parent_events),
1383a987785dSJay Kamat 	T(test_memcg_oom_group_score_events),
138484092dbcSRoman Gushchin };
138584092dbcSRoman Gushchin #undef T
138684092dbcSRoman Gushchin 
138784092dbcSRoman Gushchin int main(int argc, char **argv)
138884092dbcSRoman Gushchin {
138984092dbcSRoman Gushchin 	char root[PATH_MAX];
1390*cdc69458SDavid Vernet 	int i, proc_status, ret = EXIT_SUCCESS;
139184092dbcSRoman Gushchin 
139284092dbcSRoman Gushchin 	if (cg_find_unified_root(root, sizeof(root)))
139384092dbcSRoman Gushchin 		ksft_exit_skip("cgroup v2 isn't mounted\n");
139484092dbcSRoman Gushchin 
139584092dbcSRoman Gushchin 	/*
139684092dbcSRoman Gushchin 	 * Check that memory controller is available:
139784092dbcSRoman Gushchin 	 * memory is listed in cgroup.controllers
139884092dbcSRoman Gushchin 	 */
139984092dbcSRoman Gushchin 	if (cg_read_strstr(root, "cgroup.controllers", "memory"))
140084092dbcSRoman Gushchin 		ksft_exit_skip("memory controller isn't available\n");
140184092dbcSRoman Gushchin 
1402f6131f28SAlex Shi 	if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
1403f6131f28SAlex Shi 		if (cg_write(root, "cgroup.subtree_control", "+memory"))
1404f6131f28SAlex Shi 			ksft_exit_skip("Failed to set memory controller\n");
1405f6131f28SAlex Shi 
1406*cdc69458SDavid Vernet 	proc_status = proc_mount_contains("memory_recursiveprot");
1407*cdc69458SDavid Vernet 	if (proc_status < 0)
1408*cdc69458SDavid Vernet 		ksft_exit_skip("Failed to query cgroup mount option\n");
1409*cdc69458SDavid Vernet 	has_recursiveprot = proc_status;
1410*cdc69458SDavid Vernet 
141184092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
141284092dbcSRoman Gushchin 		switch (tests[i].fn(root)) {
141384092dbcSRoman Gushchin 		case KSFT_PASS:
141484092dbcSRoman Gushchin 			ksft_test_result_pass("%s\n", tests[i].name);
141584092dbcSRoman Gushchin 			break;
141684092dbcSRoman Gushchin 		case KSFT_SKIP:
141784092dbcSRoman Gushchin 			ksft_test_result_skip("%s\n", tests[i].name);
141884092dbcSRoman Gushchin 			break;
141984092dbcSRoman Gushchin 		default:
142084092dbcSRoman Gushchin 			ret = EXIT_FAILURE;
142184092dbcSRoman Gushchin 			ksft_test_result_fail("%s\n", tests[i].name);
142284092dbcSRoman Gushchin 			break;
142384092dbcSRoman Gushchin 		}
142484092dbcSRoman Gushchin 	}
142584092dbcSRoman Gushchin 
142684092dbcSRoman Gushchin 	return ret;
142784092dbcSRoman Gushchin }
1428