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 
2484092dbcSRoman Gushchin /*
2584092dbcSRoman Gushchin  * This test creates two nested cgroups with and without enabling
2684092dbcSRoman Gushchin  * the memory controller.
2784092dbcSRoman Gushchin  */
2884092dbcSRoman Gushchin static int test_memcg_subtree_control(const char *root)
2984092dbcSRoman Gushchin {
30e14d314cSRoman Gushchin 	char *parent, *child, *parent2 = NULL, *child2 = NULL;
3184092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
3284092dbcSRoman Gushchin 	char buf[PAGE_SIZE];
3384092dbcSRoman Gushchin 
3484092dbcSRoman Gushchin 	/* Create two nested cgroups with the memory controller enabled */
3584092dbcSRoman Gushchin 	parent = cg_name(root, "memcg_test_0");
3684092dbcSRoman Gushchin 	child = cg_name(root, "memcg_test_0/memcg_test_1");
3784092dbcSRoman Gushchin 	if (!parent || !child)
38e14d314cSRoman Gushchin 		goto cleanup_free;
3984092dbcSRoman Gushchin 
4084092dbcSRoman Gushchin 	if (cg_create(parent))
41e14d314cSRoman Gushchin 		goto cleanup_free;
4284092dbcSRoman Gushchin 
4384092dbcSRoman Gushchin 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
44e14d314cSRoman Gushchin 		goto cleanup_parent;
4584092dbcSRoman Gushchin 
4684092dbcSRoman Gushchin 	if (cg_create(child))
47e14d314cSRoman Gushchin 		goto cleanup_parent;
4884092dbcSRoman Gushchin 
4984092dbcSRoman Gushchin 	if (cg_read_strstr(child, "cgroup.controllers", "memory"))
50e14d314cSRoman Gushchin 		goto cleanup_child;
5184092dbcSRoman Gushchin 
5284092dbcSRoman Gushchin 	/* Create two nested cgroups without enabling memory controller */
5384092dbcSRoman Gushchin 	parent2 = cg_name(root, "memcg_test_1");
5484092dbcSRoman Gushchin 	child2 = cg_name(root, "memcg_test_1/memcg_test_1");
5584092dbcSRoman Gushchin 	if (!parent2 || !child2)
56e14d314cSRoman Gushchin 		goto cleanup_free2;
5784092dbcSRoman Gushchin 
5884092dbcSRoman Gushchin 	if (cg_create(parent2))
59e14d314cSRoman Gushchin 		goto cleanup_free2;
6084092dbcSRoman Gushchin 
6184092dbcSRoman Gushchin 	if (cg_create(child2))
62e14d314cSRoman Gushchin 		goto cleanup_parent2;
6384092dbcSRoman Gushchin 
6484092dbcSRoman Gushchin 	if (cg_read(child2, "cgroup.controllers", buf, sizeof(buf)))
65e14d314cSRoman Gushchin 		goto cleanup_all;
6684092dbcSRoman Gushchin 
6784092dbcSRoman Gushchin 	if (!cg_read_strstr(child2, "cgroup.controllers", "memory"))
68e14d314cSRoman Gushchin 		goto cleanup_all;
6984092dbcSRoman Gushchin 
7084092dbcSRoman Gushchin 	ret = KSFT_PASS;
7184092dbcSRoman Gushchin 
72e14d314cSRoman Gushchin cleanup_all:
7384092dbcSRoman Gushchin 	cg_destroy(child2);
74e14d314cSRoman Gushchin cleanup_parent2:
7584092dbcSRoman Gushchin 	cg_destroy(parent2);
76e14d314cSRoman Gushchin cleanup_free2:
7784092dbcSRoman Gushchin 	free(parent2);
7884092dbcSRoman Gushchin 	free(child2);
79e14d314cSRoman Gushchin cleanup_child:
80e14d314cSRoman Gushchin 	cg_destroy(child);
81e14d314cSRoman Gushchin cleanup_parent:
82e14d314cSRoman Gushchin 	cg_destroy(parent);
83e14d314cSRoman Gushchin cleanup_free:
84e14d314cSRoman Gushchin 	free(parent);
85e14d314cSRoman Gushchin 	free(child);
8684092dbcSRoman Gushchin 
8784092dbcSRoman Gushchin 	return ret;
8884092dbcSRoman Gushchin }
8984092dbcSRoman Gushchin 
9084092dbcSRoman Gushchin static int alloc_anon_50M_check(const char *cgroup, void *arg)
9184092dbcSRoman Gushchin {
9284092dbcSRoman Gushchin 	size_t size = MB(50);
9384092dbcSRoman Gushchin 	char *buf, *ptr;
9484092dbcSRoman Gushchin 	long anon, current;
9584092dbcSRoman Gushchin 	int ret = -1;
9684092dbcSRoman Gushchin 
9784092dbcSRoman Gushchin 	buf = malloc(size);
9884092dbcSRoman Gushchin 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
9984092dbcSRoman Gushchin 		*ptr = 0;
10084092dbcSRoman Gushchin 
10184092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
10284092dbcSRoman Gushchin 	if (current < size)
10384092dbcSRoman Gushchin 		goto cleanup;
10484092dbcSRoman Gushchin 
10584092dbcSRoman Gushchin 	if (!values_close(size, current, 3))
10684092dbcSRoman Gushchin 		goto cleanup;
10784092dbcSRoman Gushchin 
10884092dbcSRoman Gushchin 	anon = cg_read_key_long(cgroup, "memory.stat", "anon ");
10984092dbcSRoman Gushchin 	if (anon < 0)
11084092dbcSRoman Gushchin 		goto cleanup;
11184092dbcSRoman Gushchin 
11284092dbcSRoman Gushchin 	if (!values_close(anon, current, 3))
11384092dbcSRoman Gushchin 		goto cleanup;
11484092dbcSRoman Gushchin 
11584092dbcSRoman Gushchin 	ret = 0;
11684092dbcSRoman Gushchin cleanup:
11784092dbcSRoman Gushchin 	free(buf);
11884092dbcSRoman Gushchin 	return ret;
11984092dbcSRoman Gushchin }
12084092dbcSRoman Gushchin 
12184092dbcSRoman Gushchin static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
12284092dbcSRoman Gushchin {
12384092dbcSRoman Gushchin 	size_t size = MB(50);
12484092dbcSRoman Gushchin 	int ret = -1;
12584092dbcSRoman Gushchin 	long current, file;
12684092dbcSRoman Gushchin 	int fd;
12784092dbcSRoman Gushchin 
12884092dbcSRoman Gushchin 	fd = get_temp_fd();
12984092dbcSRoman Gushchin 	if (fd < 0)
13084092dbcSRoman Gushchin 		return -1;
13184092dbcSRoman Gushchin 
13284092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
13384092dbcSRoman Gushchin 		goto cleanup;
13484092dbcSRoman Gushchin 
13584092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
13684092dbcSRoman Gushchin 	if (current < size)
13784092dbcSRoman Gushchin 		goto cleanup;
13884092dbcSRoman Gushchin 
13984092dbcSRoman Gushchin 	file = cg_read_key_long(cgroup, "memory.stat", "file ");
14084092dbcSRoman Gushchin 	if (file < 0)
14184092dbcSRoman Gushchin 		goto cleanup;
14284092dbcSRoman Gushchin 
14384092dbcSRoman Gushchin 	if (!values_close(file, current, 10))
14484092dbcSRoman Gushchin 		goto cleanup;
14584092dbcSRoman Gushchin 
14684092dbcSRoman Gushchin 	ret = 0;
14784092dbcSRoman Gushchin 
14884092dbcSRoman Gushchin cleanup:
14984092dbcSRoman Gushchin 	close(fd);
15084092dbcSRoman Gushchin 	return ret;
15184092dbcSRoman Gushchin }
15284092dbcSRoman Gushchin 
15384092dbcSRoman Gushchin /*
15484092dbcSRoman Gushchin  * This test create a memory cgroup, allocates
15584092dbcSRoman Gushchin  * some anonymous memory and some pagecache
15684092dbcSRoman Gushchin  * and check memory.current and some memory.stat values.
15784092dbcSRoman Gushchin  */
15884092dbcSRoman Gushchin static int test_memcg_current(const char *root)
15984092dbcSRoman Gushchin {
16084092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
16184092dbcSRoman Gushchin 	long current;
16284092dbcSRoman Gushchin 	char *memcg;
16384092dbcSRoman Gushchin 
16484092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
16584092dbcSRoman Gushchin 	if (!memcg)
16684092dbcSRoman Gushchin 		goto cleanup;
16784092dbcSRoman Gushchin 
16884092dbcSRoman Gushchin 	if (cg_create(memcg))
16984092dbcSRoman Gushchin 		goto cleanup;
17084092dbcSRoman Gushchin 
17184092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
17284092dbcSRoman Gushchin 	if (current != 0)
17384092dbcSRoman Gushchin 		goto cleanup;
17484092dbcSRoman Gushchin 
17584092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_anon_50M_check, NULL))
17684092dbcSRoman Gushchin 		goto cleanup;
17784092dbcSRoman Gushchin 
17884092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_50M_check, NULL))
17984092dbcSRoman Gushchin 		goto cleanup;
18084092dbcSRoman Gushchin 
18184092dbcSRoman Gushchin 	ret = KSFT_PASS;
18284092dbcSRoman Gushchin 
18384092dbcSRoman Gushchin cleanup:
18484092dbcSRoman Gushchin 	cg_destroy(memcg);
18584092dbcSRoman Gushchin 	free(memcg);
18684092dbcSRoman Gushchin 
18784092dbcSRoman Gushchin 	return ret;
18884092dbcSRoman Gushchin }
18984092dbcSRoman Gushchin 
19084092dbcSRoman Gushchin static int alloc_pagecache_50M(const char *cgroup, void *arg)
19184092dbcSRoman Gushchin {
19284092dbcSRoman Gushchin 	int fd = (long)arg;
19384092dbcSRoman Gushchin 
19484092dbcSRoman Gushchin 	return alloc_pagecache(fd, MB(50));
19584092dbcSRoman Gushchin }
19684092dbcSRoman Gushchin 
19784092dbcSRoman Gushchin static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg)
19884092dbcSRoman Gushchin {
19984092dbcSRoman Gushchin 	int fd = (long)arg;
20084092dbcSRoman Gushchin 	int ppid = getppid();
20184092dbcSRoman Gushchin 
20284092dbcSRoman Gushchin 	if (alloc_pagecache(fd, MB(50)))
20384092dbcSRoman Gushchin 		return -1;
20484092dbcSRoman Gushchin 
20584092dbcSRoman Gushchin 	while (getppid() == ppid)
20684092dbcSRoman Gushchin 		sleep(1);
20784092dbcSRoman Gushchin 
20884092dbcSRoman Gushchin 	return 0;
20984092dbcSRoman Gushchin }
21084092dbcSRoman Gushchin 
211a987785dSJay Kamat static int alloc_anon_noexit(const char *cgroup, void *arg)
212a987785dSJay Kamat {
213a987785dSJay Kamat 	int ppid = getppid();
214a987785dSJay Kamat 
215a987785dSJay Kamat 	if (alloc_anon(cgroup, arg))
216a987785dSJay Kamat 		return -1;
217a987785dSJay Kamat 
218a987785dSJay Kamat 	while (getppid() == ppid)
219a987785dSJay Kamat 		sleep(1);
220a987785dSJay Kamat 
221a987785dSJay Kamat 	return 0;
222a987785dSJay Kamat }
223a987785dSJay Kamat 
224a987785dSJay Kamat /*
225a987785dSJay Kamat  * Wait until processes are killed asynchronously by the OOM killer
226a987785dSJay Kamat  * If we exceed a timeout, fail.
227a987785dSJay Kamat  */
228a987785dSJay Kamat static int cg_test_proc_killed(const char *cgroup)
229a987785dSJay Kamat {
230a987785dSJay Kamat 	int limit;
231a987785dSJay Kamat 
232a987785dSJay Kamat 	for (limit = 10; limit > 0; limit--) {
233a987785dSJay Kamat 		if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
234a987785dSJay Kamat 			return 0;
235a987785dSJay Kamat 
236a987785dSJay Kamat 		usleep(100000);
237a987785dSJay Kamat 	}
238a987785dSJay Kamat 	return -1;
239a987785dSJay Kamat }
240a987785dSJay Kamat 
24184092dbcSRoman Gushchin /*
24284092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
24384092dbcSRoman Gushchin  * A       memory.min = 50M,  memory.max = 200M
24484092dbcSRoman Gushchin  * A/B     memory.min = 50M,  memory.current = 50M
24584092dbcSRoman Gushchin  * A/B/C   memory.min = 75M,  memory.current = 50M
24684092dbcSRoman Gushchin  * A/B/D   memory.min = 25M,  memory.current = 50M
24784092dbcSRoman Gushchin  * A/B/E   memory.min = 500M, memory.current = 0
24884092dbcSRoman Gushchin  * A/B/F   memory.min = 0,    memory.current = 50M
24984092dbcSRoman Gushchin  *
25084092dbcSRoman Gushchin  * Usages are pagecache, but the test keeps a running
25184092dbcSRoman Gushchin  * process in every leaf cgroup.
25284092dbcSRoman Gushchin  * Then it creates A/G and creates a significant
25384092dbcSRoman Gushchin  * memory pressure in it.
25484092dbcSRoman Gushchin  *
25584092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
25684092dbcSRoman Gushchin  * A/B/C  memory.current ~= 33M
25784092dbcSRoman Gushchin  * A/B/D  memory.current ~= 17M
25884092dbcSRoman Gushchin  * A/B/E  memory.current ~= 0
25984092dbcSRoman Gushchin  *
26084092dbcSRoman Gushchin  * After that it tries to allocate more than there is
26184092dbcSRoman Gushchin  * unprotected memory in A available, and checks
26284092dbcSRoman Gushchin  * checks that memory.min protects pagecache even
26384092dbcSRoman Gushchin  * in this case.
26484092dbcSRoman Gushchin  */
26584092dbcSRoman Gushchin static int test_memcg_min(const char *root)
26684092dbcSRoman Gushchin {
26784092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
26884092dbcSRoman Gushchin 	char *parent[3] = {NULL};
26984092dbcSRoman Gushchin 	char *children[4] = {NULL};
27084092dbcSRoman Gushchin 	long c[4];
27184092dbcSRoman Gushchin 	int i, attempts;
27284092dbcSRoman Gushchin 	int fd;
27384092dbcSRoman Gushchin 
27484092dbcSRoman Gushchin 	fd = get_temp_fd();
27584092dbcSRoman Gushchin 	if (fd < 0)
27684092dbcSRoman Gushchin 		goto cleanup;
27784092dbcSRoman Gushchin 
27884092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
27984092dbcSRoman Gushchin 	if (!parent[0])
28084092dbcSRoman Gushchin 		goto cleanup;
28184092dbcSRoman Gushchin 
28284092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
28384092dbcSRoman Gushchin 	if (!parent[1])
28484092dbcSRoman Gushchin 		goto cleanup;
28584092dbcSRoman Gushchin 
28684092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
28784092dbcSRoman Gushchin 	if (!parent[2])
28884092dbcSRoman Gushchin 		goto cleanup;
28984092dbcSRoman Gushchin 
29084092dbcSRoman Gushchin 	if (cg_create(parent[0]))
29184092dbcSRoman Gushchin 		goto cleanup;
29284092dbcSRoman Gushchin 
29384092dbcSRoman Gushchin 	if (cg_read_long(parent[0], "memory.min")) {
29484092dbcSRoman Gushchin 		ret = KSFT_SKIP;
29584092dbcSRoman Gushchin 		goto cleanup;
29684092dbcSRoman Gushchin 	}
29784092dbcSRoman Gushchin 
29884092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
29984092dbcSRoman Gushchin 		goto cleanup;
30084092dbcSRoman Gushchin 
30184092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
30284092dbcSRoman Gushchin 		goto cleanup;
30384092dbcSRoman Gushchin 
30484092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
30584092dbcSRoman Gushchin 		goto cleanup;
30684092dbcSRoman Gushchin 
30784092dbcSRoman Gushchin 	if (cg_create(parent[1]))
30884092dbcSRoman Gushchin 		goto cleanup;
30984092dbcSRoman Gushchin 
31084092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
31184092dbcSRoman Gushchin 		goto cleanup;
31284092dbcSRoman Gushchin 
31384092dbcSRoman Gushchin 	if (cg_create(parent[2]))
31484092dbcSRoman Gushchin 		goto cleanup;
31584092dbcSRoman Gushchin 
31684092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
31784092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
31884092dbcSRoman Gushchin 		if (!children[i])
31984092dbcSRoman Gushchin 			goto cleanup;
32084092dbcSRoman Gushchin 
32184092dbcSRoman Gushchin 		if (cg_create(children[i]))
32284092dbcSRoman Gushchin 			goto cleanup;
32384092dbcSRoman Gushchin 
32484092dbcSRoman Gushchin 		if (i == 2)
32584092dbcSRoman Gushchin 			continue;
32684092dbcSRoman Gushchin 
32784092dbcSRoman Gushchin 		cg_run_nowait(children[i], alloc_pagecache_50M_noexit,
32884092dbcSRoman Gushchin 			      (void *)(long)fd);
32984092dbcSRoman Gushchin 	}
33084092dbcSRoman Gushchin 
33184092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.min", "50M"))
33284092dbcSRoman Gushchin 		goto cleanup;
33384092dbcSRoman Gushchin 	if (cg_write(parent[1], "memory.min", "50M"))
33484092dbcSRoman Gushchin 		goto cleanup;
33584092dbcSRoman Gushchin 	if (cg_write(children[0], "memory.min", "75M"))
33684092dbcSRoman Gushchin 		goto cleanup;
33784092dbcSRoman Gushchin 	if (cg_write(children[1], "memory.min", "25M"))
33884092dbcSRoman Gushchin 		goto cleanup;
33984092dbcSRoman Gushchin 	if (cg_write(children[2], "memory.min", "500M"))
34084092dbcSRoman Gushchin 		goto cleanup;
34184092dbcSRoman Gushchin 	if (cg_write(children[3], "memory.min", "0"))
34284092dbcSRoman Gushchin 		goto cleanup;
34384092dbcSRoman Gushchin 
34484092dbcSRoman Gushchin 	attempts = 0;
34584092dbcSRoman Gushchin 	while (!values_close(cg_read_long(parent[1], "memory.current"),
34684092dbcSRoman Gushchin 			     MB(150), 3)) {
34784092dbcSRoman Gushchin 		if (attempts++ > 5)
34884092dbcSRoman Gushchin 			break;
34984092dbcSRoman Gushchin 		sleep(1);
35084092dbcSRoman Gushchin 	}
35184092dbcSRoman Gushchin 
35284092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
35384092dbcSRoman Gushchin 		goto cleanup;
35484092dbcSRoman Gushchin 
35584092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
35684092dbcSRoman Gushchin 		goto cleanup;
35784092dbcSRoman Gushchin 
35884092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
35984092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
36084092dbcSRoman Gushchin 
36184092dbcSRoman Gushchin 	if (!values_close(c[0], MB(33), 10))
36284092dbcSRoman Gushchin 		goto cleanup;
36384092dbcSRoman Gushchin 
36484092dbcSRoman Gushchin 	if (!values_close(c[1], MB(17), 10))
36584092dbcSRoman Gushchin 		goto cleanup;
36684092dbcSRoman Gushchin 
36784092dbcSRoman Gushchin 	if (!values_close(c[2], 0, 1))
36884092dbcSRoman Gushchin 		goto cleanup;
36984092dbcSRoman Gushchin 
37084092dbcSRoman Gushchin 	if (!cg_run(parent[2], alloc_anon, (void *)MB(170)))
37184092dbcSRoman Gushchin 		goto cleanup;
37284092dbcSRoman Gushchin 
37384092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
37484092dbcSRoman Gushchin 		goto cleanup;
37584092dbcSRoman Gushchin 
37684092dbcSRoman Gushchin 	ret = KSFT_PASS;
37784092dbcSRoman Gushchin 
37884092dbcSRoman Gushchin cleanup:
37984092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
38084092dbcSRoman Gushchin 		if (!children[i])
38184092dbcSRoman Gushchin 			continue;
38284092dbcSRoman Gushchin 
38384092dbcSRoman Gushchin 		cg_destroy(children[i]);
38484092dbcSRoman Gushchin 		free(children[i]);
38584092dbcSRoman Gushchin 	}
38684092dbcSRoman Gushchin 
38784092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
38884092dbcSRoman Gushchin 		if (!parent[i])
38984092dbcSRoman Gushchin 			continue;
39084092dbcSRoman Gushchin 
39184092dbcSRoman Gushchin 		cg_destroy(parent[i]);
39284092dbcSRoman Gushchin 		free(parent[i]);
39384092dbcSRoman Gushchin 	}
39484092dbcSRoman Gushchin 	close(fd);
39584092dbcSRoman Gushchin 	return ret;
39684092dbcSRoman Gushchin }
39784092dbcSRoman Gushchin 
39884092dbcSRoman Gushchin /*
39984092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
40084092dbcSRoman Gushchin  * A       memory.low = 50M,  memory.max = 200M
40184092dbcSRoman Gushchin  * A/B     memory.low = 50M,  memory.current = 50M
40284092dbcSRoman Gushchin  * A/B/C   memory.low = 75M,  memory.current = 50M
40384092dbcSRoman Gushchin  * A/B/D   memory.low = 25M,  memory.current = 50M
40484092dbcSRoman Gushchin  * A/B/E   memory.low = 500M, memory.current = 0
40584092dbcSRoman Gushchin  * A/B/F   memory.low = 0,    memory.current = 50M
40684092dbcSRoman Gushchin  *
40784092dbcSRoman Gushchin  * Usages are pagecache.
40884092dbcSRoman Gushchin  * Then it creates A/G an creates a significant
40984092dbcSRoman Gushchin  * memory pressure in it.
41084092dbcSRoman Gushchin  *
41184092dbcSRoman Gushchin  * Then it checks actual memory usages and expects that:
41284092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
41384092dbcSRoman Gushchin  * A/B/   memory.current ~= 33M
41484092dbcSRoman Gushchin  * A/B/D  memory.current ~= 17M
41584092dbcSRoman Gushchin  * A/B/E  memory.current ~= 0
41684092dbcSRoman Gushchin  *
41784092dbcSRoman Gushchin  * After that it tries to allocate more than there is
41884092dbcSRoman Gushchin  * unprotected memory in A available,
41984092dbcSRoman Gushchin  * and checks low and oom events in memory.events.
42084092dbcSRoman Gushchin  */
42184092dbcSRoman Gushchin static int test_memcg_low(const char *root)
42284092dbcSRoman Gushchin {
42384092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
42484092dbcSRoman Gushchin 	char *parent[3] = {NULL};
42584092dbcSRoman Gushchin 	char *children[4] = {NULL};
42684092dbcSRoman Gushchin 	long low, oom;
42784092dbcSRoman Gushchin 	long c[4];
42884092dbcSRoman Gushchin 	int i;
42984092dbcSRoman Gushchin 	int fd;
43084092dbcSRoman Gushchin 
43184092dbcSRoman Gushchin 	fd = get_temp_fd();
43284092dbcSRoman Gushchin 	if (fd < 0)
43384092dbcSRoman Gushchin 		goto cleanup;
43484092dbcSRoman Gushchin 
43584092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
43684092dbcSRoman Gushchin 	if (!parent[0])
43784092dbcSRoman Gushchin 		goto cleanup;
43884092dbcSRoman Gushchin 
43984092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
44084092dbcSRoman Gushchin 	if (!parent[1])
44184092dbcSRoman Gushchin 		goto cleanup;
44284092dbcSRoman Gushchin 
44384092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
44484092dbcSRoman Gushchin 	if (!parent[2])
44584092dbcSRoman Gushchin 		goto cleanup;
44684092dbcSRoman Gushchin 
44784092dbcSRoman Gushchin 	if (cg_create(parent[0]))
44884092dbcSRoman Gushchin 		goto cleanup;
44984092dbcSRoman Gushchin 
45084092dbcSRoman Gushchin 	if (cg_read_long(parent[0], "memory.low"))
45184092dbcSRoman Gushchin 		goto cleanup;
45284092dbcSRoman Gushchin 
45384092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
45484092dbcSRoman Gushchin 		goto cleanup;
45584092dbcSRoman Gushchin 
45684092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
45784092dbcSRoman Gushchin 		goto cleanup;
45884092dbcSRoman Gushchin 
45984092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
46084092dbcSRoman Gushchin 		goto cleanup;
46184092dbcSRoman Gushchin 
46284092dbcSRoman Gushchin 	if (cg_create(parent[1]))
46384092dbcSRoman Gushchin 		goto cleanup;
46484092dbcSRoman Gushchin 
46584092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
46684092dbcSRoman Gushchin 		goto cleanup;
46784092dbcSRoman Gushchin 
46884092dbcSRoman Gushchin 	if (cg_create(parent[2]))
46984092dbcSRoman Gushchin 		goto cleanup;
47084092dbcSRoman Gushchin 
47184092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
47284092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
47384092dbcSRoman Gushchin 		if (!children[i])
47484092dbcSRoman Gushchin 			goto cleanup;
47584092dbcSRoman Gushchin 
47684092dbcSRoman Gushchin 		if (cg_create(children[i]))
47784092dbcSRoman Gushchin 			goto cleanup;
47884092dbcSRoman Gushchin 
47984092dbcSRoman Gushchin 		if (i == 2)
48084092dbcSRoman Gushchin 			continue;
48184092dbcSRoman Gushchin 
48284092dbcSRoman Gushchin 		if (cg_run(children[i], alloc_pagecache_50M, (void *)(long)fd))
48384092dbcSRoman Gushchin 			goto cleanup;
48484092dbcSRoman Gushchin 	}
48584092dbcSRoman Gushchin 
48684092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.low", "50M"))
48784092dbcSRoman Gushchin 		goto cleanup;
48884092dbcSRoman Gushchin 	if (cg_write(parent[1], "memory.low", "50M"))
48984092dbcSRoman Gushchin 		goto cleanup;
49084092dbcSRoman Gushchin 	if (cg_write(children[0], "memory.low", "75M"))
49184092dbcSRoman Gushchin 		goto cleanup;
49284092dbcSRoman Gushchin 	if (cg_write(children[1], "memory.low", "25M"))
49384092dbcSRoman Gushchin 		goto cleanup;
49484092dbcSRoman Gushchin 	if (cg_write(children[2], "memory.low", "500M"))
49584092dbcSRoman Gushchin 		goto cleanup;
49684092dbcSRoman Gushchin 	if (cg_write(children[3], "memory.low", "0"))
49784092dbcSRoman Gushchin 		goto cleanup;
49884092dbcSRoman Gushchin 
49984092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
50084092dbcSRoman Gushchin 		goto cleanup;
50184092dbcSRoman Gushchin 
50284092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
50384092dbcSRoman Gushchin 		goto cleanup;
50484092dbcSRoman Gushchin 
50584092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
50684092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
50784092dbcSRoman Gushchin 
50884092dbcSRoman Gushchin 	if (!values_close(c[0], MB(33), 10))
50984092dbcSRoman Gushchin 		goto cleanup;
51084092dbcSRoman Gushchin 
51184092dbcSRoman Gushchin 	if (!values_close(c[1], MB(17), 10))
51284092dbcSRoman Gushchin 		goto cleanup;
51384092dbcSRoman Gushchin 
51484092dbcSRoman Gushchin 	if (!values_close(c[2], 0, 1))
51584092dbcSRoman Gushchin 		goto cleanup;
51684092dbcSRoman Gushchin 
51784092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(166))) {
51884092dbcSRoman Gushchin 		fprintf(stderr,
51984092dbcSRoman Gushchin 			"memory.low prevents from allocating anon memory\n");
52084092dbcSRoman Gushchin 		goto cleanup;
52184092dbcSRoman Gushchin 	}
52284092dbcSRoman Gushchin 
52384092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
52484092dbcSRoman Gushchin 		oom = cg_read_key_long(children[i], "memory.events", "oom ");
52584092dbcSRoman Gushchin 		low = cg_read_key_long(children[i], "memory.events", "low ");
52684092dbcSRoman Gushchin 
52784092dbcSRoman Gushchin 		if (oom)
52884092dbcSRoman Gushchin 			goto cleanup;
52984092dbcSRoman Gushchin 		if (i < 2 && low <= 0)
53084092dbcSRoman Gushchin 			goto cleanup;
53184092dbcSRoman Gushchin 		if (i >= 2 && low)
53284092dbcSRoman Gushchin 			goto cleanup;
53384092dbcSRoman Gushchin 	}
53484092dbcSRoman Gushchin 
53584092dbcSRoman Gushchin 	ret = KSFT_PASS;
53684092dbcSRoman Gushchin 
53784092dbcSRoman Gushchin cleanup:
53884092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
53984092dbcSRoman Gushchin 		if (!children[i])
54084092dbcSRoman Gushchin 			continue;
54184092dbcSRoman Gushchin 
54284092dbcSRoman Gushchin 		cg_destroy(children[i]);
54384092dbcSRoman Gushchin 		free(children[i]);
54484092dbcSRoman Gushchin 	}
54584092dbcSRoman Gushchin 
54684092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
54784092dbcSRoman Gushchin 		if (!parent[i])
54884092dbcSRoman Gushchin 			continue;
54984092dbcSRoman Gushchin 
55084092dbcSRoman Gushchin 		cg_destroy(parent[i]);
55184092dbcSRoman Gushchin 		free(parent[i]);
55284092dbcSRoman Gushchin 	}
55384092dbcSRoman Gushchin 	close(fd);
55484092dbcSRoman Gushchin 	return ret;
55584092dbcSRoman Gushchin }
55684092dbcSRoman Gushchin 
55784092dbcSRoman Gushchin static int alloc_pagecache_max_30M(const char *cgroup, void *arg)
55884092dbcSRoman Gushchin {
55984092dbcSRoman Gushchin 	size_t size = MB(50);
56084092dbcSRoman Gushchin 	int ret = -1;
56184092dbcSRoman Gushchin 	long current;
56284092dbcSRoman Gushchin 	int fd;
56384092dbcSRoman Gushchin 
56484092dbcSRoman Gushchin 	fd = get_temp_fd();
56584092dbcSRoman Gushchin 	if (fd < 0)
56684092dbcSRoman Gushchin 		return -1;
56784092dbcSRoman Gushchin 
56884092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
56984092dbcSRoman Gushchin 		goto cleanup;
57084092dbcSRoman Gushchin 
57184092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
57284092dbcSRoman Gushchin 	if (current <= MB(29) || current > MB(30))
57384092dbcSRoman Gushchin 		goto cleanup;
57484092dbcSRoman Gushchin 
57584092dbcSRoman Gushchin 	ret = 0;
57684092dbcSRoman Gushchin 
57784092dbcSRoman Gushchin cleanup:
57884092dbcSRoman Gushchin 	close(fd);
57984092dbcSRoman Gushchin 	return ret;
58084092dbcSRoman Gushchin 
58184092dbcSRoman Gushchin }
58284092dbcSRoman Gushchin 
58384092dbcSRoman Gushchin /*
58484092dbcSRoman Gushchin  * This test checks that memory.high limits the amount of
58584092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
58684092dbcSRoman Gushchin  * or pagecache.
58784092dbcSRoman Gushchin  */
58884092dbcSRoman Gushchin static int test_memcg_high(const char *root)
58984092dbcSRoman Gushchin {
59084092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
59184092dbcSRoman Gushchin 	char *memcg;
59284092dbcSRoman Gushchin 	long high;
59384092dbcSRoman Gushchin 
59484092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
59584092dbcSRoman Gushchin 	if (!memcg)
59684092dbcSRoman Gushchin 		goto cleanup;
59784092dbcSRoman Gushchin 
59884092dbcSRoman Gushchin 	if (cg_create(memcg))
59984092dbcSRoman Gushchin 		goto cleanup;
60084092dbcSRoman Gushchin 
60184092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.high", "max\n"))
60284092dbcSRoman Gushchin 		goto cleanup;
60384092dbcSRoman Gushchin 
60484092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
60584092dbcSRoman Gushchin 		goto cleanup;
60684092dbcSRoman Gushchin 
60784092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.high", "30M"))
60884092dbcSRoman Gushchin 		goto cleanup;
60984092dbcSRoman Gushchin 
610*be74553fSRoman Gushchin 	if (cg_run(memcg, alloc_anon, (void *)MB(31)))
61184092dbcSRoman Gushchin 		goto cleanup;
61284092dbcSRoman Gushchin 
61384092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_pagecache_50M_check, NULL))
61484092dbcSRoman Gushchin 		goto cleanup;
61584092dbcSRoman Gushchin 
61684092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
61784092dbcSRoman Gushchin 		goto cleanup;
61884092dbcSRoman Gushchin 
61984092dbcSRoman Gushchin 	high = cg_read_key_long(memcg, "memory.events", "high ");
62084092dbcSRoman Gushchin 	if (high <= 0)
62184092dbcSRoman Gushchin 		goto cleanup;
62284092dbcSRoman Gushchin 
62384092dbcSRoman Gushchin 	ret = KSFT_PASS;
62484092dbcSRoman Gushchin 
62584092dbcSRoman Gushchin cleanup:
62684092dbcSRoman Gushchin 	cg_destroy(memcg);
62784092dbcSRoman Gushchin 	free(memcg);
62884092dbcSRoman Gushchin 
62984092dbcSRoman Gushchin 	return ret;
63084092dbcSRoman Gushchin }
63184092dbcSRoman Gushchin 
6326323ec54SShakeel Butt static int alloc_anon_mlock(const char *cgroup, void *arg)
6336323ec54SShakeel Butt {
6346323ec54SShakeel Butt 	size_t size = (size_t)arg;
6356323ec54SShakeel Butt 	void *buf;
6366323ec54SShakeel Butt 
6376323ec54SShakeel Butt 	buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
6386323ec54SShakeel Butt 		   0, 0);
6396323ec54SShakeel Butt 	if (buf == MAP_FAILED)
6406323ec54SShakeel Butt 		return -1;
6416323ec54SShakeel Butt 
6426323ec54SShakeel Butt 	mlock(buf, size);
6436323ec54SShakeel Butt 	munmap(buf, size);
6446323ec54SShakeel Butt 	return 0;
6456323ec54SShakeel Butt }
6466323ec54SShakeel Butt 
6476323ec54SShakeel Butt /*
6486323ec54SShakeel Butt  * This test checks that memory.high is able to throttle big single shot
6496323ec54SShakeel Butt  * allocation i.e. large allocation within one kernel entry.
6506323ec54SShakeel Butt  */
6516323ec54SShakeel Butt static int test_memcg_high_sync(const char *root)
6526323ec54SShakeel Butt {
6536323ec54SShakeel Butt 	int ret = KSFT_FAIL, pid, fd = -1;
6546323ec54SShakeel Butt 	char *memcg;
6556323ec54SShakeel Butt 	long pre_high, pre_max;
6566323ec54SShakeel Butt 	long post_high, post_max;
6576323ec54SShakeel Butt 
6586323ec54SShakeel Butt 	memcg = cg_name(root, "memcg_test");
6596323ec54SShakeel Butt 	if (!memcg)
6606323ec54SShakeel Butt 		goto cleanup;
6616323ec54SShakeel Butt 
6626323ec54SShakeel Butt 	if (cg_create(memcg))
6636323ec54SShakeel Butt 		goto cleanup;
6646323ec54SShakeel Butt 
6656323ec54SShakeel Butt 	pre_high = cg_read_key_long(memcg, "memory.events", "high ");
6666323ec54SShakeel Butt 	pre_max = cg_read_key_long(memcg, "memory.events", "max ");
6676323ec54SShakeel Butt 	if (pre_high < 0 || pre_max < 0)
6686323ec54SShakeel Butt 		goto cleanup;
6696323ec54SShakeel Butt 
6706323ec54SShakeel Butt 	if (cg_write(memcg, "memory.swap.max", "0"))
6716323ec54SShakeel Butt 		goto cleanup;
6726323ec54SShakeel Butt 
6736323ec54SShakeel Butt 	if (cg_write(memcg, "memory.high", "30M"))
6746323ec54SShakeel Butt 		goto cleanup;
6756323ec54SShakeel Butt 
6766323ec54SShakeel Butt 	if (cg_write(memcg, "memory.max", "140M"))
6776323ec54SShakeel Butt 		goto cleanup;
6786323ec54SShakeel Butt 
6796323ec54SShakeel Butt 	fd = memcg_prepare_for_wait(memcg);
6806323ec54SShakeel Butt 	if (fd < 0)
6816323ec54SShakeel Butt 		goto cleanup;
6826323ec54SShakeel Butt 
6836323ec54SShakeel Butt 	pid = cg_run_nowait(memcg, alloc_anon_mlock, (void *)MB(200));
6846323ec54SShakeel Butt 	if (pid < 0)
6856323ec54SShakeel Butt 		goto cleanup;
6866323ec54SShakeel Butt 
6876323ec54SShakeel Butt 	cg_wait_for(fd);
6886323ec54SShakeel Butt 
6896323ec54SShakeel Butt 	post_high = cg_read_key_long(memcg, "memory.events", "high ");
6906323ec54SShakeel Butt 	post_max = cg_read_key_long(memcg, "memory.events", "max ");
6916323ec54SShakeel Butt 	if (post_high < 0 || post_max < 0)
6926323ec54SShakeel Butt 		goto cleanup;
6936323ec54SShakeel Butt 
6946323ec54SShakeel Butt 	if (pre_high == post_high || pre_max != post_max)
6956323ec54SShakeel Butt 		goto cleanup;
6966323ec54SShakeel Butt 
6976323ec54SShakeel Butt 	ret = KSFT_PASS;
6986323ec54SShakeel Butt 
6996323ec54SShakeel Butt cleanup:
7006323ec54SShakeel Butt 	if (fd >= 0)
7016323ec54SShakeel Butt 		close(fd);
7026323ec54SShakeel Butt 	cg_destroy(memcg);
7036323ec54SShakeel Butt 	free(memcg);
7046323ec54SShakeel Butt 
7056323ec54SShakeel Butt 	return ret;
7066323ec54SShakeel Butt }
7076323ec54SShakeel Butt 
70884092dbcSRoman Gushchin /*
70984092dbcSRoman Gushchin  * This test checks that memory.max limits the amount of
71084092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
71184092dbcSRoman Gushchin  * or pagecache.
71284092dbcSRoman Gushchin  */
71384092dbcSRoman Gushchin static int test_memcg_max(const char *root)
71484092dbcSRoman Gushchin {
71584092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
71684092dbcSRoman Gushchin 	char *memcg;
71784092dbcSRoman Gushchin 	long current, max;
71884092dbcSRoman Gushchin 
71984092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
72084092dbcSRoman Gushchin 	if (!memcg)
72184092dbcSRoman Gushchin 		goto cleanup;
72284092dbcSRoman Gushchin 
72384092dbcSRoman Gushchin 	if (cg_create(memcg))
72484092dbcSRoman Gushchin 		goto cleanup;
72584092dbcSRoman Gushchin 
72684092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
72784092dbcSRoman Gushchin 		goto cleanup;
72884092dbcSRoman Gushchin 
72984092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
73084092dbcSRoman Gushchin 		goto cleanup;
73184092dbcSRoman Gushchin 
73284092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
73384092dbcSRoman Gushchin 		goto cleanup;
73484092dbcSRoman Gushchin 
73584092dbcSRoman Gushchin 	/* Should be killed by OOM killer */
73684092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
73784092dbcSRoman Gushchin 		goto cleanup;
73884092dbcSRoman Gushchin 
73984092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
74084092dbcSRoman Gushchin 		goto cleanup;
74184092dbcSRoman Gushchin 
74284092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
74384092dbcSRoman Gushchin 	if (current > MB(30) || !current)
74484092dbcSRoman Gushchin 		goto cleanup;
74584092dbcSRoman Gushchin 
74684092dbcSRoman Gushchin 	max = cg_read_key_long(memcg, "memory.events", "max ");
74784092dbcSRoman Gushchin 	if (max <= 0)
74884092dbcSRoman Gushchin 		goto cleanup;
74984092dbcSRoman Gushchin 
75084092dbcSRoman Gushchin 	ret = KSFT_PASS;
75184092dbcSRoman Gushchin 
75284092dbcSRoman Gushchin cleanup:
75384092dbcSRoman Gushchin 	cg_destroy(memcg);
75484092dbcSRoman Gushchin 	free(memcg);
75584092dbcSRoman Gushchin 
75684092dbcSRoman Gushchin 	return ret;
75784092dbcSRoman Gushchin }
75884092dbcSRoman Gushchin 
759478b2784SMike Rapoport static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
760478b2784SMike Rapoport {
761478b2784SMike Rapoport 	long mem_max = (long)arg;
762478b2784SMike Rapoport 	size_t size = MB(50);
763478b2784SMike Rapoport 	char *buf, *ptr;
764478b2784SMike Rapoport 	long mem_current, swap_current;
765478b2784SMike Rapoport 	int ret = -1;
766478b2784SMike Rapoport 
767478b2784SMike Rapoport 	buf = malloc(size);
768478b2784SMike Rapoport 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
769478b2784SMike Rapoport 		*ptr = 0;
770478b2784SMike Rapoport 
771478b2784SMike Rapoport 	mem_current = cg_read_long(cgroup, "memory.current");
772478b2784SMike Rapoport 	if (!mem_current || !values_close(mem_current, mem_max, 3))
773478b2784SMike Rapoport 		goto cleanup;
774478b2784SMike Rapoport 
775478b2784SMike Rapoport 	swap_current = cg_read_long(cgroup, "memory.swap.current");
776478b2784SMike Rapoport 	if (!swap_current ||
777478b2784SMike Rapoport 	    !values_close(mem_current + swap_current, size, 3))
778478b2784SMike Rapoport 		goto cleanup;
779478b2784SMike Rapoport 
780478b2784SMike Rapoport 	ret = 0;
781478b2784SMike Rapoport cleanup:
782478b2784SMike Rapoport 	free(buf);
783478b2784SMike Rapoport 	return ret;
784478b2784SMike Rapoport }
785478b2784SMike Rapoport 
786478b2784SMike Rapoport /*
787478b2784SMike Rapoport  * This test checks that memory.swap.max limits the amount of
788478b2784SMike Rapoport  * anonymous memory which can be swapped out.
789478b2784SMike Rapoport  */
790478b2784SMike Rapoport static int test_memcg_swap_max(const char *root)
791478b2784SMike Rapoport {
792478b2784SMike Rapoport 	int ret = KSFT_FAIL;
793478b2784SMike Rapoport 	char *memcg;
794478b2784SMike Rapoport 	long max;
795478b2784SMike Rapoport 
796478b2784SMike Rapoport 	if (!is_swap_enabled())
797478b2784SMike Rapoport 		return KSFT_SKIP;
798478b2784SMike Rapoport 
799478b2784SMike Rapoport 	memcg = cg_name(root, "memcg_test");
800478b2784SMike Rapoport 	if (!memcg)
801478b2784SMike Rapoport 		goto cleanup;
802478b2784SMike Rapoport 
803478b2784SMike Rapoport 	if (cg_create(memcg))
804478b2784SMike Rapoport 		goto cleanup;
805478b2784SMike Rapoport 
806478b2784SMike Rapoport 	if (cg_read_long(memcg, "memory.swap.current")) {
807478b2784SMike Rapoport 		ret = KSFT_SKIP;
808478b2784SMike Rapoport 		goto cleanup;
809478b2784SMike Rapoport 	}
810478b2784SMike Rapoport 
811478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
812478b2784SMike Rapoport 		goto cleanup;
813478b2784SMike Rapoport 
814478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.swap.max", "max\n"))
815478b2784SMike Rapoport 		goto cleanup;
816478b2784SMike Rapoport 
817478b2784SMike Rapoport 	if (cg_write(memcg, "memory.swap.max", "30M"))
818478b2784SMike Rapoport 		goto cleanup;
819478b2784SMike Rapoport 
820478b2784SMike Rapoport 	if (cg_write(memcg, "memory.max", "30M"))
821478b2784SMike Rapoport 		goto cleanup;
822478b2784SMike Rapoport 
823478b2784SMike Rapoport 	/* Should be killed by OOM killer */
824478b2784SMike Rapoport 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
825478b2784SMike Rapoport 		goto cleanup;
826478b2784SMike Rapoport 
827478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
828478b2784SMike Rapoport 		goto cleanup;
829478b2784SMike Rapoport 
830478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
831478b2784SMike Rapoport 		goto cleanup;
832478b2784SMike Rapoport 
833478b2784SMike Rapoport 	if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30)))
834478b2784SMike Rapoport 		goto cleanup;
835478b2784SMike Rapoport 
836478b2784SMike Rapoport 	max = cg_read_key_long(memcg, "memory.events", "max ");
837478b2784SMike Rapoport 	if (max <= 0)
838478b2784SMike Rapoport 		goto cleanup;
839478b2784SMike Rapoport 
840478b2784SMike Rapoport 	ret = KSFT_PASS;
841478b2784SMike Rapoport 
842478b2784SMike Rapoport cleanup:
843478b2784SMike Rapoport 	cg_destroy(memcg);
844478b2784SMike Rapoport 	free(memcg);
845478b2784SMike Rapoport 
846478b2784SMike Rapoport 	return ret;
847478b2784SMike Rapoport }
848478b2784SMike Rapoport 
84984092dbcSRoman Gushchin /*
85084092dbcSRoman Gushchin  * This test disables swapping and tries to allocate anonymous memory
85184092dbcSRoman Gushchin  * up to OOM. Then it checks for oom and oom_kill events in
85284092dbcSRoman Gushchin  * memory.events.
85384092dbcSRoman Gushchin  */
85484092dbcSRoman Gushchin static int test_memcg_oom_events(const char *root)
85584092dbcSRoman Gushchin {
85684092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
85784092dbcSRoman Gushchin 	char *memcg;
85884092dbcSRoman Gushchin 
85984092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
86084092dbcSRoman Gushchin 	if (!memcg)
86184092dbcSRoman Gushchin 		goto cleanup;
86284092dbcSRoman Gushchin 
86384092dbcSRoman Gushchin 	if (cg_create(memcg))
86484092dbcSRoman Gushchin 		goto cleanup;
86584092dbcSRoman Gushchin 
86684092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
86784092dbcSRoman Gushchin 		goto cleanup;
86884092dbcSRoman Gushchin 
86984092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
87084092dbcSRoman Gushchin 		goto cleanup;
87184092dbcSRoman Gushchin 
87284092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
87384092dbcSRoman Gushchin 		goto cleanup;
87484092dbcSRoman Gushchin 
87584092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "cgroup.procs", ""))
87684092dbcSRoman Gushchin 		goto cleanup;
87784092dbcSRoman Gushchin 
87884092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
87984092dbcSRoman Gushchin 		goto cleanup;
88084092dbcSRoman Gushchin 
88184092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
88284092dbcSRoman Gushchin 		goto cleanup;
88384092dbcSRoman Gushchin 
88484092dbcSRoman Gushchin 	ret = KSFT_PASS;
88584092dbcSRoman Gushchin 
88684092dbcSRoman Gushchin cleanup:
88784092dbcSRoman Gushchin 	cg_destroy(memcg);
88884092dbcSRoman Gushchin 	free(memcg);
88984092dbcSRoman Gushchin 
89084092dbcSRoman Gushchin 	return ret;
89184092dbcSRoman Gushchin }
89284092dbcSRoman Gushchin 
8935f8f0193SMike Rapoport struct tcp_server_args {
8945f8f0193SMike Rapoport 	unsigned short port;
8955f8f0193SMike Rapoport 	int ctl[2];
8965f8f0193SMike Rapoport };
8975f8f0193SMike Rapoport 
8985f8f0193SMike Rapoport static int tcp_server(const char *cgroup, void *arg)
8995f8f0193SMike Rapoport {
9005f8f0193SMike Rapoport 	struct tcp_server_args *srv_args = arg;
9015f8f0193SMike Rapoport 	struct sockaddr_in6 saddr = { 0 };
9025f8f0193SMike Rapoport 	socklen_t slen = sizeof(saddr);
9035f8f0193SMike Rapoport 	int sk, client_sk, ctl_fd, yes = 1, ret = -1;
9045f8f0193SMike Rapoport 
9055f8f0193SMike Rapoport 	close(srv_args->ctl[0]);
9065f8f0193SMike Rapoport 	ctl_fd = srv_args->ctl[1];
9075f8f0193SMike Rapoport 
9085f8f0193SMike Rapoport 	saddr.sin6_family = AF_INET6;
9095f8f0193SMike Rapoport 	saddr.sin6_addr = in6addr_any;
9105f8f0193SMike Rapoport 	saddr.sin6_port = htons(srv_args->port);
9115f8f0193SMike Rapoport 
9125f8f0193SMike Rapoport 	sk = socket(AF_INET6, SOCK_STREAM, 0);
9135f8f0193SMike Rapoport 	if (sk < 0)
9145f8f0193SMike Rapoport 		return ret;
9155f8f0193SMike Rapoport 
9165f8f0193SMike Rapoport 	if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
9175f8f0193SMike Rapoport 		goto cleanup;
9185f8f0193SMike Rapoport 
9195f8f0193SMike Rapoport 	if (bind(sk, (struct sockaddr *)&saddr, slen)) {
9205f8f0193SMike Rapoport 		write(ctl_fd, &errno, sizeof(errno));
9215f8f0193SMike Rapoport 		goto cleanup;
9225f8f0193SMike Rapoport 	}
9235f8f0193SMike Rapoport 
9245f8f0193SMike Rapoport 	if (listen(sk, 1))
9255f8f0193SMike Rapoport 		goto cleanup;
9265f8f0193SMike Rapoport 
9275f8f0193SMike Rapoport 	ret = 0;
9285f8f0193SMike Rapoport 	if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) {
9295f8f0193SMike Rapoport 		ret = -1;
9305f8f0193SMike Rapoport 		goto cleanup;
9315f8f0193SMike Rapoport 	}
9325f8f0193SMike Rapoport 
9335f8f0193SMike Rapoport 	client_sk = accept(sk, NULL, NULL);
9345f8f0193SMike Rapoport 	if (client_sk < 0)
9355f8f0193SMike Rapoport 		goto cleanup;
9365f8f0193SMike Rapoport 
9375f8f0193SMike Rapoport 	ret = -1;
9385f8f0193SMike Rapoport 	for (;;) {
9395f8f0193SMike Rapoport 		uint8_t buf[0x100000];
9405f8f0193SMike Rapoport 
9415f8f0193SMike Rapoport 		if (write(client_sk, buf, sizeof(buf)) <= 0) {
9425f8f0193SMike Rapoport 			if (errno == ECONNRESET)
9435f8f0193SMike Rapoport 				ret = 0;
9445f8f0193SMike Rapoport 			break;
9455f8f0193SMike Rapoport 		}
9465f8f0193SMike Rapoport 	}
9475f8f0193SMike Rapoport 
9485f8f0193SMike Rapoport 	close(client_sk);
9495f8f0193SMike Rapoport 
9505f8f0193SMike Rapoport cleanup:
9515f8f0193SMike Rapoport 	close(sk);
9525f8f0193SMike Rapoport 	return ret;
9535f8f0193SMike Rapoport }
9545f8f0193SMike Rapoport 
9555f8f0193SMike Rapoport static int tcp_client(const char *cgroup, unsigned short port)
9565f8f0193SMike Rapoport {
9575f8f0193SMike Rapoport 	const char server[] = "localhost";
9585f8f0193SMike Rapoport 	struct addrinfo *ai;
9595f8f0193SMike Rapoport 	char servport[6];
9605f8f0193SMike Rapoport 	int retries = 0x10; /* nice round number */
9615f8f0193SMike Rapoport 	int sk, ret;
9625f8f0193SMike Rapoport 
9635f8f0193SMike Rapoport 	snprintf(servport, sizeof(servport), "%hd", port);
9645f8f0193SMike Rapoport 	ret = getaddrinfo(server, servport, NULL, &ai);
9655f8f0193SMike Rapoport 	if (ret)
9665f8f0193SMike Rapoport 		return ret;
9675f8f0193SMike Rapoport 
9685f8f0193SMike Rapoport 	sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
9695f8f0193SMike Rapoport 	if (sk < 0)
9705f8f0193SMike Rapoport 		goto free_ainfo;
9715f8f0193SMike Rapoport 
9725f8f0193SMike Rapoport 	ret = connect(sk, ai->ai_addr, ai->ai_addrlen);
9735f8f0193SMike Rapoport 	if (ret < 0)
9745f8f0193SMike Rapoport 		goto close_sk;
9755f8f0193SMike Rapoport 
9765f8f0193SMike Rapoport 	ret = KSFT_FAIL;
9775f8f0193SMike Rapoport 	while (retries--) {
9785f8f0193SMike Rapoport 		uint8_t buf[0x100000];
9795f8f0193SMike Rapoport 		long current, sock;
9805f8f0193SMike Rapoport 
9815f8f0193SMike Rapoport 		if (read(sk, buf, sizeof(buf)) <= 0)
9825f8f0193SMike Rapoport 			goto close_sk;
9835f8f0193SMike Rapoport 
9845f8f0193SMike Rapoport 		current = cg_read_long(cgroup, "memory.current");
9855f8f0193SMike Rapoport 		sock = cg_read_key_long(cgroup, "memory.stat", "sock ");
9865f8f0193SMike Rapoport 
9875f8f0193SMike Rapoport 		if (current < 0 || sock < 0)
9885f8f0193SMike Rapoport 			goto close_sk;
9895f8f0193SMike Rapoport 
9905f8f0193SMike Rapoport 		if (current < sock)
9915f8f0193SMike Rapoport 			goto close_sk;
9925f8f0193SMike Rapoport 
9935f8f0193SMike Rapoport 		if (values_close(current, sock, 10)) {
9945f8f0193SMike Rapoport 			ret = KSFT_PASS;
9955f8f0193SMike Rapoport 			break;
9965f8f0193SMike Rapoport 		}
9975f8f0193SMike Rapoport 	}
9985f8f0193SMike Rapoport 
9995f8f0193SMike Rapoport close_sk:
10005f8f0193SMike Rapoport 	close(sk);
10015f8f0193SMike Rapoport free_ainfo:
10025f8f0193SMike Rapoport 	freeaddrinfo(ai);
10035f8f0193SMike Rapoport 	return ret;
10045f8f0193SMike Rapoport }
10055f8f0193SMike Rapoport 
10065f8f0193SMike Rapoport /*
10075f8f0193SMike Rapoport  * This test checks socket memory accounting.
10085f8f0193SMike Rapoport  * The test forks a TCP server listens on a random port between 1000
10095f8f0193SMike Rapoport  * and 61000. Once it gets a client connection, it starts writing to
10105f8f0193SMike Rapoport  * its socket.
10115f8f0193SMike Rapoport  * The TCP client interleaves reads from the socket with check whether
10125f8f0193SMike Rapoport  * memory.current and memory.stat.sock are similar.
10135f8f0193SMike Rapoport  */
10145f8f0193SMike Rapoport static int test_memcg_sock(const char *root)
10155f8f0193SMike Rapoport {
10165f8f0193SMike Rapoport 	int bind_retries = 5, ret = KSFT_FAIL, pid, err;
10175f8f0193SMike Rapoport 	unsigned short port;
10185f8f0193SMike Rapoport 	char *memcg;
10195f8f0193SMike Rapoport 
10205f8f0193SMike Rapoport 	memcg = cg_name(root, "memcg_test");
10215f8f0193SMike Rapoport 	if (!memcg)
10225f8f0193SMike Rapoport 		goto cleanup;
10235f8f0193SMike Rapoport 
10245f8f0193SMike Rapoport 	if (cg_create(memcg))
10255f8f0193SMike Rapoport 		goto cleanup;
10265f8f0193SMike Rapoport 
10275f8f0193SMike Rapoport 	while (bind_retries--) {
10285f8f0193SMike Rapoport 		struct tcp_server_args args;
10295f8f0193SMike Rapoport 
10305f8f0193SMike Rapoport 		if (pipe(args.ctl))
10315f8f0193SMike Rapoport 			goto cleanup;
10325f8f0193SMike Rapoport 
10335f8f0193SMike Rapoport 		port = args.port = 1000 + rand() % 60000;
10345f8f0193SMike Rapoport 
10355f8f0193SMike Rapoport 		pid = cg_run_nowait(memcg, tcp_server, &args);
10365f8f0193SMike Rapoport 		if (pid < 0)
10375f8f0193SMike Rapoport 			goto cleanup;
10385f8f0193SMike Rapoport 
10395f8f0193SMike Rapoport 		close(args.ctl[1]);
10405f8f0193SMike Rapoport 		if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err))
10415f8f0193SMike Rapoport 			goto cleanup;
10425f8f0193SMike Rapoport 		close(args.ctl[0]);
10435f8f0193SMike Rapoport 
10445f8f0193SMike Rapoport 		if (!err)
10455f8f0193SMike Rapoport 			break;
10465f8f0193SMike Rapoport 		if (err != EADDRINUSE)
10475f8f0193SMike Rapoport 			goto cleanup;
10485f8f0193SMike Rapoport 
10495f8f0193SMike Rapoport 		waitpid(pid, NULL, 0);
10505f8f0193SMike Rapoport 	}
10515f8f0193SMike Rapoport 
10525f8f0193SMike Rapoport 	if (err == EADDRINUSE) {
10535f8f0193SMike Rapoport 		ret = KSFT_SKIP;
10545f8f0193SMike Rapoport 		goto cleanup;
10555f8f0193SMike Rapoport 	}
10565f8f0193SMike Rapoport 
10575f8f0193SMike Rapoport 	if (tcp_client(memcg, port) != KSFT_PASS)
10585f8f0193SMike Rapoport 		goto cleanup;
10595f8f0193SMike Rapoport 
10605f8f0193SMike Rapoport 	waitpid(pid, &err, 0);
10615f8f0193SMike Rapoport 	if (WEXITSTATUS(err))
10625f8f0193SMike Rapoport 		goto cleanup;
10635f8f0193SMike Rapoport 
10645f8f0193SMike Rapoport 	if (cg_read_long(memcg, "memory.current") < 0)
10655f8f0193SMike Rapoport 		goto cleanup;
10665f8f0193SMike Rapoport 
10675f8f0193SMike Rapoport 	if (cg_read_key_long(memcg, "memory.stat", "sock "))
10685f8f0193SMike Rapoport 		goto cleanup;
10695f8f0193SMike Rapoport 
10705f8f0193SMike Rapoport 	ret = KSFT_PASS;
10715f8f0193SMike Rapoport 
10725f8f0193SMike Rapoport cleanup:
10735f8f0193SMike Rapoport 	cg_destroy(memcg);
10745f8f0193SMike Rapoport 	free(memcg);
10755f8f0193SMike Rapoport 
10765f8f0193SMike Rapoport 	return ret;
10775f8f0193SMike Rapoport }
10785f8f0193SMike Rapoport 
1079a987785dSJay Kamat /*
1080a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1081a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1082c85bcc91SRoman Gushchin  * processes in the leaf were killed. It also checks that oom_events
1083c85bcc91SRoman Gushchin  * were propagated to the parent level.
1084a987785dSJay Kamat  */
1085a987785dSJay Kamat static int test_memcg_oom_group_leaf_events(const char *root)
1086a987785dSJay Kamat {
1087a987785dSJay Kamat 	int ret = KSFT_FAIL;
1088a987785dSJay Kamat 	char *parent, *child;
1089a987785dSJay Kamat 
1090a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1091a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1092a987785dSJay Kamat 
1093a987785dSJay Kamat 	if (!parent || !child)
1094a987785dSJay Kamat 		goto cleanup;
1095a987785dSJay Kamat 
1096a987785dSJay Kamat 	if (cg_create(parent))
1097a987785dSJay Kamat 		goto cleanup;
1098a987785dSJay Kamat 
1099a987785dSJay Kamat 	if (cg_create(child))
1100a987785dSJay Kamat 		goto cleanup;
1101a987785dSJay Kamat 
1102a987785dSJay Kamat 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
1103a987785dSJay Kamat 		goto cleanup;
1104a987785dSJay Kamat 
1105a987785dSJay Kamat 	if (cg_write(child, "memory.max", "50M"))
1106a987785dSJay Kamat 		goto cleanup;
1107a987785dSJay Kamat 
1108a987785dSJay Kamat 	if (cg_write(child, "memory.swap.max", "0"))
1109a987785dSJay Kamat 		goto cleanup;
1110a987785dSJay Kamat 
1111a987785dSJay Kamat 	if (cg_write(child, "memory.oom.group", "1"))
1112a987785dSJay Kamat 		goto cleanup;
1113a987785dSJay Kamat 
1114a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1115a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1116a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1117a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1118a987785dSJay Kamat 		goto cleanup;
1119a987785dSJay Kamat 
1120a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1121a987785dSJay Kamat 		goto cleanup;
1122a987785dSJay Kamat 
1123a987785dSJay Kamat 	if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
1124a987785dSJay Kamat 		goto cleanup;
1125a987785dSJay Kamat 
1126c85bcc91SRoman Gushchin 	if (cg_read_key_long(parent, "memory.events", "oom_kill ") <= 0)
1127a987785dSJay Kamat 		goto cleanup;
1128a987785dSJay Kamat 
1129a987785dSJay Kamat 	ret = KSFT_PASS;
1130a987785dSJay Kamat 
1131a987785dSJay Kamat cleanup:
1132a987785dSJay Kamat 	if (child)
1133a987785dSJay Kamat 		cg_destroy(child);
1134a987785dSJay Kamat 	if (parent)
1135a987785dSJay Kamat 		cg_destroy(parent);
1136a987785dSJay Kamat 	free(child);
1137a987785dSJay Kamat 	free(parent);
1138a987785dSJay Kamat 
1139a987785dSJay Kamat 	return ret;
1140a987785dSJay Kamat }
1141a987785dSJay Kamat 
1142a987785dSJay Kamat /*
1143a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1144a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1145a987785dSJay Kamat  * processes in the parent and leaf were killed.
1146a987785dSJay Kamat  */
1147a987785dSJay Kamat static int test_memcg_oom_group_parent_events(const char *root)
1148a987785dSJay Kamat {
1149a987785dSJay Kamat 	int ret = KSFT_FAIL;
1150a987785dSJay Kamat 	char *parent, *child;
1151a987785dSJay Kamat 
1152a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1153a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1154a987785dSJay Kamat 
1155a987785dSJay Kamat 	if (!parent || !child)
1156a987785dSJay Kamat 		goto cleanup;
1157a987785dSJay Kamat 
1158a987785dSJay Kamat 	if (cg_create(parent))
1159a987785dSJay Kamat 		goto cleanup;
1160a987785dSJay Kamat 
1161a987785dSJay Kamat 	if (cg_create(child))
1162a987785dSJay Kamat 		goto cleanup;
1163a987785dSJay Kamat 
1164a987785dSJay Kamat 	if (cg_write(parent, "memory.max", "80M"))
1165a987785dSJay Kamat 		goto cleanup;
1166a987785dSJay Kamat 
1167a987785dSJay Kamat 	if (cg_write(parent, "memory.swap.max", "0"))
1168a987785dSJay Kamat 		goto cleanup;
1169a987785dSJay Kamat 
1170a987785dSJay Kamat 	if (cg_write(parent, "memory.oom.group", "1"))
1171a987785dSJay Kamat 		goto cleanup;
1172a987785dSJay Kamat 
1173a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1174a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1175a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1176a987785dSJay Kamat 
1177a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1178a987785dSJay Kamat 		goto cleanup;
1179a987785dSJay Kamat 
1180a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1181a987785dSJay Kamat 		goto cleanup;
1182a987785dSJay Kamat 	if (cg_test_proc_killed(parent))
1183a987785dSJay Kamat 		goto cleanup;
1184a987785dSJay Kamat 
1185a987785dSJay Kamat 	ret = KSFT_PASS;
1186a987785dSJay Kamat 
1187a987785dSJay Kamat cleanup:
1188a987785dSJay Kamat 	if (child)
1189a987785dSJay Kamat 		cg_destroy(child);
1190a987785dSJay Kamat 	if (parent)
1191a987785dSJay Kamat 		cg_destroy(parent);
1192a987785dSJay Kamat 	free(child);
1193a987785dSJay Kamat 	free(parent);
1194a987785dSJay Kamat 
1195a987785dSJay Kamat 	return ret;
1196a987785dSJay Kamat }
1197a987785dSJay Kamat 
1198a987785dSJay Kamat /*
1199a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1200a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1201a987785dSJay Kamat  * processes were killed except those set with OOM_SCORE_ADJ_MIN
1202a987785dSJay Kamat  */
1203a987785dSJay Kamat static int test_memcg_oom_group_score_events(const char *root)
1204a987785dSJay Kamat {
1205a987785dSJay Kamat 	int ret = KSFT_FAIL;
1206a987785dSJay Kamat 	char *memcg;
1207a987785dSJay Kamat 	int safe_pid;
1208a987785dSJay Kamat 
1209a987785dSJay Kamat 	memcg = cg_name(root, "memcg_test_0");
1210a987785dSJay Kamat 
1211a987785dSJay Kamat 	if (!memcg)
1212a987785dSJay Kamat 		goto cleanup;
1213a987785dSJay Kamat 
1214a987785dSJay Kamat 	if (cg_create(memcg))
1215a987785dSJay Kamat 		goto cleanup;
1216a987785dSJay Kamat 
1217a987785dSJay Kamat 	if (cg_write(memcg, "memory.max", "50M"))
1218a987785dSJay Kamat 		goto cleanup;
1219a987785dSJay Kamat 
1220a987785dSJay Kamat 	if (cg_write(memcg, "memory.swap.max", "0"))
1221a987785dSJay Kamat 		goto cleanup;
1222a987785dSJay Kamat 
1223a987785dSJay Kamat 	if (cg_write(memcg, "memory.oom.group", "1"))
1224a987785dSJay Kamat 		goto cleanup;
1225a987785dSJay Kamat 
1226a987785dSJay Kamat 	safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1227a987785dSJay Kamat 	if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN))
1228a987785dSJay Kamat 		goto cleanup;
1229a987785dSJay Kamat 
1230a987785dSJay Kamat 	cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1231a987785dSJay Kamat 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
1232a987785dSJay Kamat 		goto cleanup;
1233a987785dSJay Kamat 
1234a987785dSJay Kamat 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3)
1235a987785dSJay Kamat 		goto cleanup;
1236a987785dSJay Kamat 
1237a987785dSJay Kamat 	if (kill(safe_pid, SIGKILL))
1238a987785dSJay Kamat 		goto cleanup;
1239a987785dSJay Kamat 
1240a987785dSJay Kamat 	ret = KSFT_PASS;
1241a987785dSJay Kamat 
1242a987785dSJay Kamat cleanup:
1243a987785dSJay Kamat 	if (memcg)
1244a987785dSJay Kamat 		cg_destroy(memcg);
1245a987785dSJay Kamat 	free(memcg);
1246a987785dSJay Kamat 
1247a987785dSJay Kamat 	return ret;
1248a987785dSJay Kamat }
1249a987785dSJay Kamat 
1250a987785dSJay Kamat 
125184092dbcSRoman Gushchin #define T(x) { x, #x }
125284092dbcSRoman Gushchin struct memcg_test {
125384092dbcSRoman Gushchin 	int (*fn)(const char *root);
125484092dbcSRoman Gushchin 	const char *name;
125584092dbcSRoman Gushchin } tests[] = {
125684092dbcSRoman Gushchin 	T(test_memcg_subtree_control),
125784092dbcSRoman Gushchin 	T(test_memcg_current),
125884092dbcSRoman Gushchin 	T(test_memcg_min),
125984092dbcSRoman Gushchin 	T(test_memcg_low),
126084092dbcSRoman Gushchin 	T(test_memcg_high),
12616323ec54SShakeel Butt 	T(test_memcg_high_sync),
126284092dbcSRoman Gushchin 	T(test_memcg_max),
126384092dbcSRoman Gushchin 	T(test_memcg_oom_events),
1264478b2784SMike Rapoport 	T(test_memcg_swap_max),
12655f8f0193SMike Rapoport 	T(test_memcg_sock),
1266a987785dSJay Kamat 	T(test_memcg_oom_group_leaf_events),
1267a987785dSJay Kamat 	T(test_memcg_oom_group_parent_events),
1268a987785dSJay Kamat 	T(test_memcg_oom_group_score_events),
126984092dbcSRoman Gushchin };
127084092dbcSRoman Gushchin #undef T
127184092dbcSRoman Gushchin 
127284092dbcSRoman Gushchin int main(int argc, char **argv)
127384092dbcSRoman Gushchin {
127484092dbcSRoman Gushchin 	char root[PATH_MAX];
127584092dbcSRoman Gushchin 	int i, ret = EXIT_SUCCESS;
127684092dbcSRoman Gushchin 
127784092dbcSRoman Gushchin 	if (cg_find_unified_root(root, sizeof(root)))
127884092dbcSRoman Gushchin 		ksft_exit_skip("cgroup v2 isn't mounted\n");
127984092dbcSRoman Gushchin 
128084092dbcSRoman Gushchin 	/*
128184092dbcSRoman Gushchin 	 * Check that memory controller is available:
128284092dbcSRoman Gushchin 	 * memory is listed in cgroup.controllers
128384092dbcSRoman Gushchin 	 */
128484092dbcSRoman Gushchin 	if (cg_read_strstr(root, "cgroup.controllers", "memory"))
128584092dbcSRoman Gushchin 		ksft_exit_skip("memory controller isn't available\n");
128684092dbcSRoman Gushchin 
1287f6131f28SAlex Shi 	if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
1288f6131f28SAlex Shi 		if (cg_write(root, "cgroup.subtree_control", "+memory"))
1289f6131f28SAlex Shi 			ksft_exit_skip("Failed to set memory controller\n");
1290f6131f28SAlex Shi 
129184092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
129284092dbcSRoman Gushchin 		switch (tests[i].fn(root)) {
129384092dbcSRoman Gushchin 		case KSFT_PASS:
129484092dbcSRoman Gushchin 			ksft_test_result_pass("%s\n", tests[i].name);
129584092dbcSRoman Gushchin 			break;
129684092dbcSRoman Gushchin 		case KSFT_SKIP:
129784092dbcSRoman Gushchin 			ksft_test_result_skip("%s\n", tests[i].name);
129884092dbcSRoman Gushchin 			break;
129984092dbcSRoman Gushchin 		default:
130084092dbcSRoman Gushchin 			ret = EXIT_FAILURE;
130184092dbcSRoman Gushchin 			ksft_test_result_fail("%s\n", tests[i].name);
130284092dbcSRoman Gushchin 			break;
130384092dbcSRoman Gushchin 		}
130484092dbcSRoman Gushchin 	}
130584092dbcSRoman Gushchin 
130684092dbcSRoman Gushchin 	return ret;
130784092dbcSRoman Gushchin }
1308