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