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  */
test_memcg_subtree_control(const char * root)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 
alloc_anon_50M_check(const char * cgroup,void * arg)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);
101c83f320eSIvan Orlov 	if (buf == NULL) {
102c83f320eSIvan Orlov 		fprintf(stderr, "malloc() failed\n");
103c83f320eSIvan Orlov 		return -1;
104c83f320eSIvan Orlov 	}
105c83f320eSIvan Orlov 
10684092dbcSRoman Gushchin 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
10784092dbcSRoman Gushchin 		*ptr = 0;
10884092dbcSRoman Gushchin 
10984092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
11084092dbcSRoman Gushchin 	if (current < size)
11184092dbcSRoman Gushchin 		goto cleanup;
11284092dbcSRoman Gushchin 
11384092dbcSRoman Gushchin 	if (!values_close(size, current, 3))
11484092dbcSRoman Gushchin 		goto cleanup;
11584092dbcSRoman Gushchin 
11684092dbcSRoman Gushchin 	anon = cg_read_key_long(cgroup, "memory.stat", "anon ");
11784092dbcSRoman Gushchin 	if (anon < 0)
11884092dbcSRoman Gushchin 		goto cleanup;
11984092dbcSRoman Gushchin 
12084092dbcSRoman Gushchin 	if (!values_close(anon, current, 3))
12184092dbcSRoman Gushchin 		goto cleanup;
12284092dbcSRoman Gushchin 
12384092dbcSRoman Gushchin 	ret = 0;
12484092dbcSRoman Gushchin cleanup:
12584092dbcSRoman Gushchin 	free(buf);
12684092dbcSRoman Gushchin 	return ret;
12784092dbcSRoman Gushchin }
12884092dbcSRoman Gushchin 
alloc_pagecache_50M_check(const char * cgroup,void * arg)12984092dbcSRoman Gushchin static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
13084092dbcSRoman Gushchin {
13184092dbcSRoman Gushchin 	size_t size = MB(50);
13284092dbcSRoman Gushchin 	int ret = -1;
13384092dbcSRoman Gushchin 	long current, file;
13484092dbcSRoman Gushchin 	int fd;
13584092dbcSRoman Gushchin 
13684092dbcSRoman Gushchin 	fd = get_temp_fd();
13784092dbcSRoman Gushchin 	if (fd < 0)
13884092dbcSRoman Gushchin 		return -1;
13984092dbcSRoman Gushchin 
14084092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
14184092dbcSRoman Gushchin 		goto cleanup;
14284092dbcSRoman Gushchin 
14384092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
14484092dbcSRoman Gushchin 	if (current < size)
14584092dbcSRoman Gushchin 		goto cleanup;
14684092dbcSRoman Gushchin 
14784092dbcSRoman Gushchin 	file = cg_read_key_long(cgroup, "memory.stat", "file ");
14884092dbcSRoman Gushchin 	if (file < 0)
14984092dbcSRoman Gushchin 		goto cleanup;
15084092dbcSRoman Gushchin 
15184092dbcSRoman Gushchin 	if (!values_close(file, current, 10))
15284092dbcSRoman Gushchin 		goto cleanup;
15384092dbcSRoman Gushchin 
15484092dbcSRoman Gushchin 	ret = 0;
15584092dbcSRoman Gushchin 
15684092dbcSRoman Gushchin cleanup:
15784092dbcSRoman Gushchin 	close(fd);
15884092dbcSRoman Gushchin 	return ret;
15984092dbcSRoman Gushchin }
16084092dbcSRoman Gushchin 
16184092dbcSRoman Gushchin /*
16284092dbcSRoman Gushchin  * This test create a memory cgroup, allocates
16384092dbcSRoman Gushchin  * some anonymous memory and some pagecache
16484092dbcSRoman Gushchin  * and check memory.current and some memory.stat values.
16584092dbcSRoman Gushchin  */
test_memcg_current(const char * root)16684092dbcSRoman Gushchin static int test_memcg_current(const char *root)
16784092dbcSRoman Gushchin {
16884092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
16984092dbcSRoman Gushchin 	long current;
17084092dbcSRoman Gushchin 	char *memcg;
17184092dbcSRoman Gushchin 
17284092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
17384092dbcSRoman Gushchin 	if (!memcg)
17484092dbcSRoman Gushchin 		goto cleanup;
17584092dbcSRoman Gushchin 
17684092dbcSRoman Gushchin 	if (cg_create(memcg))
17784092dbcSRoman Gushchin 		goto cleanup;
17884092dbcSRoman Gushchin 
17984092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
18084092dbcSRoman Gushchin 	if (current != 0)
18184092dbcSRoman Gushchin 		goto cleanup;
18284092dbcSRoman Gushchin 
18384092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_anon_50M_check, NULL))
18484092dbcSRoman Gushchin 		goto cleanup;
18584092dbcSRoman Gushchin 
18684092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_50M_check, NULL))
18784092dbcSRoman Gushchin 		goto cleanup;
18884092dbcSRoman Gushchin 
18984092dbcSRoman Gushchin 	ret = KSFT_PASS;
19084092dbcSRoman Gushchin 
19184092dbcSRoman Gushchin cleanup:
19284092dbcSRoman Gushchin 	cg_destroy(memcg);
19384092dbcSRoman Gushchin 	free(memcg);
19484092dbcSRoman Gushchin 
19584092dbcSRoman Gushchin 	return ret;
19684092dbcSRoman Gushchin }
19784092dbcSRoman Gushchin 
alloc_pagecache_50M_noexit(const char * cgroup,void * arg)19884092dbcSRoman Gushchin static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg)
19984092dbcSRoman Gushchin {
20084092dbcSRoman Gushchin 	int fd = (long)arg;
20184092dbcSRoman Gushchin 	int ppid = getppid();
20284092dbcSRoman Gushchin 
20384092dbcSRoman Gushchin 	if (alloc_pagecache(fd, MB(50)))
20484092dbcSRoman Gushchin 		return -1;
20584092dbcSRoman Gushchin 
20684092dbcSRoman Gushchin 	while (getppid() == ppid)
20784092dbcSRoman Gushchin 		sleep(1);
20884092dbcSRoman Gushchin 
20984092dbcSRoman Gushchin 	return 0;
21084092dbcSRoman Gushchin }
21184092dbcSRoman Gushchin 
alloc_anon_noexit(const char * cgroup,void * arg)212a987785dSJay Kamat static int alloc_anon_noexit(const char *cgroup, void *arg)
213a987785dSJay Kamat {
214a987785dSJay Kamat 	int ppid = getppid();
215a3622a53SYosry Ahmed 	size_t size = (unsigned long)arg;
216a3622a53SYosry Ahmed 	char *buf, *ptr;
217a987785dSJay Kamat 
218a3622a53SYosry Ahmed 	buf = malloc(size);
219c83f320eSIvan Orlov 	if (buf == NULL) {
220c83f320eSIvan Orlov 		fprintf(stderr, "malloc() failed\n");
221c83f320eSIvan Orlov 		return -1;
222c83f320eSIvan Orlov 	}
223c83f320eSIvan Orlov 
224a3622a53SYosry Ahmed 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
225a3622a53SYosry Ahmed 		*ptr = 0;
226a987785dSJay Kamat 
227a987785dSJay Kamat 	while (getppid() == ppid)
228a987785dSJay Kamat 		sleep(1);
229a987785dSJay Kamat 
230a3622a53SYosry Ahmed 	free(buf);
231a987785dSJay Kamat 	return 0;
232a987785dSJay Kamat }
233a987785dSJay Kamat 
234a987785dSJay Kamat /*
235a987785dSJay Kamat  * Wait until processes are killed asynchronously by the OOM killer
236a987785dSJay Kamat  * If we exceed a timeout, fail.
237a987785dSJay Kamat  */
cg_test_proc_killed(const char * cgroup)238a987785dSJay Kamat static int cg_test_proc_killed(const char *cgroup)
239a987785dSJay Kamat {
240a987785dSJay Kamat 	int limit;
241a987785dSJay Kamat 
242a987785dSJay Kamat 	for (limit = 10; limit > 0; limit--) {
243a987785dSJay Kamat 		if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0)
244a987785dSJay Kamat 			return 0;
245a987785dSJay Kamat 
246a987785dSJay Kamat 		usleep(100000);
247a987785dSJay Kamat 	}
248a987785dSJay Kamat 	return -1;
249a987785dSJay Kamat }
250a987785dSJay Kamat 
2511c746977SYosry Ahmed static bool reclaim_until(const char *memcg, long goal);
2521c746977SYosry Ahmed 
25384092dbcSRoman Gushchin /*
25484092dbcSRoman Gushchin  * First, this test creates the following hierarchy:
2556a359190SMichal Koutný  * A       memory.min = 0,    memory.max = 200M
256f10b6e9aSMichal Koutný  * A/B     memory.min = 50M
25784092dbcSRoman Gushchin  * A/B/C   memory.min = 75M,  memory.current = 50M
25884092dbcSRoman Gushchin  * A/B/D   memory.min = 25M,  memory.current = 50M
259f0cdaa56SDavid Vernet  * A/B/E   memory.min = 0,    memory.current = 50M
260f0cdaa56SDavid Vernet  * A/B/F   memory.min = 500M, memory.current = 0
26184092dbcSRoman Gushchin  *
262f079a020SMichal Koutný  * (or memory.low if we test soft protection)
263f079a020SMichal Koutný  *
264f079a020SMichal Koutný  * Usages are pagecache and the test keeps a running
26584092dbcSRoman Gushchin  * process in every leaf cgroup.
26684092dbcSRoman Gushchin  * Then it creates A/G and creates a significant
2676a359190SMichal Koutný  * memory pressure in A.
26884092dbcSRoman Gushchin  *
269f10b6e9aSMichal Koutný  * Then it checks actual memory usages and expects that:
27084092dbcSRoman Gushchin  * A/B    memory.current ~= 50M
271f10b6e9aSMichal Koutný  * A/B/C  memory.current ~= 29M
272f10b6e9aSMichal Koutný  * A/B/D  memory.current ~= 21M
273f10b6e9aSMichal Koutný  * A/B/E  memory.current ~= 0
274f10b6e9aSMichal Koutný  * A/B/F  memory.current  = 0
275f10b6e9aSMichal Koutný  * (for origin of the numbers, see model in memcg_protection.m.)
27684092dbcSRoman Gushchin  *
27784092dbcSRoman Gushchin  * After that it tries to allocate more than there is
278f079a020SMichal Koutný  * unprotected memory in A available, and checks that:
279f079a020SMichal Koutný  * a) memory.min protects pagecache even in this case,
280f079a020SMichal Koutný  * b) memory.low allows reclaiming page cache with low events.
2811c746977SYosry Ahmed  *
2821c746977SYosry Ahmed  * Then we try to reclaim from A/B/C using memory.reclaim until its
2831c746977SYosry Ahmed  * usage reaches 10M.
2841c746977SYosry Ahmed  * This makes sure that:
2851c746977SYosry Ahmed  * (a) We ignore the protection of the reclaim target memcg.
2861c746977SYosry Ahmed  * (b) The previously calculated emin value (~29M) should be dismissed.
28784092dbcSRoman Gushchin  */
test_memcg_protection(const char * root,bool min)288f079a020SMichal Koutný static int test_memcg_protection(const char *root, bool min)
28984092dbcSRoman Gushchin {
290f079a020SMichal Koutný 	int ret = KSFT_FAIL, rc;
29184092dbcSRoman Gushchin 	char *parent[3] = {NULL};
29284092dbcSRoman Gushchin 	char *children[4] = {NULL};
293f079a020SMichal Koutný 	const char *attribute = min ? "memory.min" : "memory.low";
29484092dbcSRoman Gushchin 	long c[4];
29519ab3657SHaifeng Xu 	long current;
29684092dbcSRoman Gushchin 	int i, attempts;
29784092dbcSRoman Gushchin 	int fd;
29884092dbcSRoman Gushchin 
29984092dbcSRoman Gushchin 	fd = get_temp_fd();
30084092dbcSRoman Gushchin 	if (fd < 0)
30184092dbcSRoman Gushchin 		goto cleanup;
30284092dbcSRoman Gushchin 
30384092dbcSRoman Gushchin 	parent[0] = cg_name(root, "memcg_test_0");
30484092dbcSRoman Gushchin 	if (!parent[0])
30584092dbcSRoman Gushchin 		goto cleanup;
30684092dbcSRoman Gushchin 
30784092dbcSRoman Gushchin 	parent[1] = cg_name(parent[0], "memcg_test_1");
30884092dbcSRoman Gushchin 	if (!parent[1])
30984092dbcSRoman Gushchin 		goto cleanup;
31084092dbcSRoman Gushchin 
31184092dbcSRoman Gushchin 	parent[2] = cg_name(parent[0], "memcg_test_2");
31284092dbcSRoman Gushchin 	if (!parent[2])
31384092dbcSRoman Gushchin 		goto cleanup;
31484092dbcSRoman Gushchin 
31584092dbcSRoman Gushchin 	if (cg_create(parent[0]))
31684092dbcSRoman Gushchin 		goto cleanup;
31784092dbcSRoman Gushchin 
318f079a020SMichal Koutný 	if (cg_read_long(parent[0], attribute)) {
319f079a020SMichal Koutný 		/* No memory.min on older kernels is fine */
320f079a020SMichal Koutný 		if (min)
32184092dbcSRoman Gushchin 			ret = KSFT_SKIP;
32284092dbcSRoman Gushchin 		goto cleanup;
32384092dbcSRoman Gushchin 	}
32484092dbcSRoman Gushchin 
32584092dbcSRoman Gushchin 	if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
32684092dbcSRoman Gushchin 		goto cleanup;
32784092dbcSRoman Gushchin 
32884092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.max", "200M"))
32984092dbcSRoman Gushchin 		goto cleanup;
33084092dbcSRoman Gushchin 
33184092dbcSRoman Gushchin 	if (cg_write(parent[0], "memory.swap.max", "0"))
33284092dbcSRoman Gushchin 		goto cleanup;
33384092dbcSRoman Gushchin 
33484092dbcSRoman Gushchin 	if (cg_create(parent[1]))
33584092dbcSRoman Gushchin 		goto cleanup;
33684092dbcSRoman Gushchin 
33784092dbcSRoman Gushchin 	if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
33884092dbcSRoman Gushchin 		goto cleanup;
33984092dbcSRoman Gushchin 
34084092dbcSRoman Gushchin 	if (cg_create(parent[2]))
34184092dbcSRoman Gushchin 		goto cleanup;
34284092dbcSRoman Gushchin 
34384092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
34484092dbcSRoman Gushchin 		children[i] = cg_name_indexed(parent[1], "child_memcg", i);
34584092dbcSRoman Gushchin 		if (!children[i])
34684092dbcSRoman Gushchin 			goto cleanup;
34784092dbcSRoman Gushchin 
34884092dbcSRoman Gushchin 		if (cg_create(children[i]))
34984092dbcSRoman Gushchin 			goto cleanup;
35084092dbcSRoman Gushchin 
351f0cdaa56SDavid Vernet 		if (i > 2)
35284092dbcSRoman Gushchin 			continue;
35384092dbcSRoman Gushchin 
35484092dbcSRoman Gushchin 		cg_run_nowait(children[i], alloc_pagecache_50M_noexit,
35584092dbcSRoman Gushchin 			      (void *)(long)fd);
35684092dbcSRoman Gushchin 	}
35784092dbcSRoman Gushchin 
358f079a020SMichal Koutný 	if (cg_write(parent[1],   attribute, "50M"))
35984092dbcSRoman Gushchin 		goto cleanup;
360f079a020SMichal Koutný 	if (cg_write(children[0], attribute, "75M"))
36184092dbcSRoman Gushchin 		goto cleanup;
362f079a020SMichal Koutný 	if (cg_write(children[1], attribute, "25M"))
36384092dbcSRoman Gushchin 		goto cleanup;
364f079a020SMichal Koutný 	if (cg_write(children[2], attribute, "0"))
36584092dbcSRoman Gushchin 		goto cleanup;
366f079a020SMichal Koutný 	if (cg_write(children[3], attribute, "500M"))
36784092dbcSRoman Gushchin 		goto cleanup;
36884092dbcSRoman Gushchin 
36984092dbcSRoman Gushchin 	attempts = 0;
37084092dbcSRoman Gushchin 	while (!values_close(cg_read_long(parent[1], "memory.current"),
37184092dbcSRoman Gushchin 			     MB(150), 3)) {
37284092dbcSRoman Gushchin 		if (attempts++ > 5)
37384092dbcSRoman Gushchin 			break;
37484092dbcSRoman Gushchin 		sleep(1);
37584092dbcSRoman Gushchin 	}
37684092dbcSRoman Gushchin 
37784092dbcSRoman Gushchin 	if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
37884092dbcSRoman Gushchin 		goto cleanup;
37984092dbcSRoman Gushchin 
38084092dbcSRoman Gushchin 	if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
38184092dbcSRoman Gushchin 		goto cleanup;
38284092dbcSRoman Gushchin 
38384092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++)
38484092dbcSRoman Gushchin 		c[i] = cg_read_long(children[i], "memory.current");
38584092dbcSRoman Gushchin 
386f10b6e9aSMichal Koutný 	if (!values_close(c[0], MB(29), 10))
38784092dbcSRoman Gushchin 		goto cleanup;
38884092dbcSRoman Gushchin 
389f10b6e9aSMichal Koutný 	if (!values_close(c[1], MB(21), 10))
39084092dbcSRoman Gushchin 		goto cleanup;
39184092dbcSRoman Gushchin 
392f0cdaa56SDavid Vernet 	if (c[3] != 0)
39384092dbcSRoman Gushchin 		goto cleanup;
39484092dbcSRoman Gushchin 
395f079a020SMichal Koutný 	rc = cg_run(parent[2], alloc_anon, (void *)MB(170));
396f079a020SMichal Koutný 	if (min && !rc)
39784092dbcSRoman Gushchin 		goto cleanup;
398f079a020SMichal Koutný 	else if (!min && rc) {
39984092dbcSRoman Gushchin 		fprintf(stderr,
40084092dbcSRoman Gushchin 			"memory.low prevents from allocating anon memory\n");
40184092dbcSRoman Gushchin 		goto cleanup;
40284092dbcSRoman Gushchin 	}
40384092dbcSRoman Gushchin 
40419ab3657SHaifeng Xu 	current = min ? MB(50) : MB(30);
40519ab3657SHaifeng Xu 	if (!values_close(cg_read_long(parent[1], "memory.current"), current, 3))
406f079a020SMichal Koutný 		goto cleanup;
407f079a020SMichal Koutný 
4081c746977SYosry Ahmed 	if (!reclaim_until(children[0], MB(10)))
4091c746977SYosry Ahmed 		goto cleanup;
4101c746977SYosry Ahmed 
411f079a020SMichal Koutný 	if (min) {
412f079a020SMichal Koutný 		ret = KSFT_PASS;
413f079a020SMichal Koutný 		goto cleanup;
414f079a020SMichal Koutný 	}
415f079a020SMichal Koutný 
41684092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(children); i++) {
4171d09069fSMichal Koutný 		int no_low_events_index = 1;
418f079a020SMichal Koutný 		long low, oom;
419cdc69458SDavid Vernet 
42084092dbcSRoman Gushchin 		oom = cg_read_key_long(children[i], "memory.events", "oom ");
42184092dbcSRoman Gushchin 		low = cg_read_key_long(children[i], "memory.events", "low ");
42284092dbcSRoman Gushchin 
42384092dbcSRoman Gushchin 		if (oom)
42484092dbcSRoman Gushchin 			goto cleanup;
425cdc69458SDavid Vernet 		if (i <= no_low_events_index && low <= 0)
42684092dbcSRoman Gushchin 			goto cleanup;
427cdc69458SDavid Vernet 		if (i > no_low_events_index && low)
42884092dbcSRoman Gushchin 			goto cleanup;
429cdc69458SDavid Vernet 
43084092dbcSRoman Gushchin 	}
43184092dbcSRoman Gushchin 
43284092dbcSRoman Gushchin 	ret = KSFT_PASS;
43384092dbcSRoman Gushchin 
43484092dbcSRoman Gushchin cleanup:
43584092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
43684092dbcSRoman Gushchin 		if (!children[i])
43784092dbcSRoman Gushchin 			continue;
43884092dbcSRoman Gushchin 
43984092dbcSRoman Gushchin 		cg_destroy(children[i]);
44084092dbcSRoman Gushchin 		free(children[i]);
44184092dbcSRoman Gushchin 	}
44284092dbcSRoman Gushchin 
44384092dbcSRoman Gushchin 	for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
44484092dbcSRoman Gushchin 		if (!parent[i])
44584092dbcSRoman Gushchin 			continue;
44684092dbcSRoman Gushchin 
44784092dbcSRoman Gushchin 		cg_destroy(parent[i]);
44884092dbcSRoman Gushchin 		free(parent[i]);
44984092dbcSRoman Gushchin 	}
45084092dbcSRoman Gushchin 	close(fd);
45184092dbcSRoman Gushchin 	return ret;
45284092dbcSRoman Gushchin }
45384092dbcSRoman Gushchin 
test_memcg_min(const char * root)454f079a020SMichal Koutný static int test_memcg_min(const char *root)
455f079a020SMichal Koutný {
456f079a020SMichal Koutný 	return test_memcg_protection(root, true);
457f079a020SMichal Koutný }
458f079a020SMichal Koutný 
test_memcg_low(const char * root)459f079a020SMichal Koutný static int test_memcg_low(const char *root)
460f079a020SMichal Koutný {
461f079a020SMichal Koutný 	return test_memcg_protection(root, false);
462f079a020SMichal Koutný }
463f079a020SMichal Koutný 
alloc_pagecache_max_30M(const char * cgroup,void * arg)46484092dbcSRoman Gushchin static int alloc_pagecache_max_30M(const char *cgroup, void *arg)
46584092dbcSRoman Gushchin {
46684092dbcSRoman Gushchin 	size_t size = MB(50);
46784092dbcSRoman Gushchin 	int ret = -1;
468c1a31a2fSDavid Vernet 	long current, high, max;
46984092dbcSRoman Gushchin 	int fd;
47084092dbcSRoman Gushchin 
471c1a31a2fSDavid Vernet 	high = cg_read_long(cgroup, "memory.high");
472c1a31a2fSDavid Vernet 	max = cg_read_long(cgroup, "memory.max");
473c1a31a2fSDavid Vernet 	if (high != MB(30) && max != MB(30))
4747fb63787SChristophe JAILLET 		return -1;
475c1a31a2fSDavid Vernet 
47684092dbcSRoman Gushchin 	fd = get_temp_fd();
47784092dbcSRoman Gushchin 	if (fd < 0)
47884092dbcSRoman Gushchin 		return -1;
47984092dbcSRoman Gushchin 
48084092dbcSRoman Gushchin 	if (alloc_pagecache(fd, size))
48184092dbcSRoman Gushchin 		goto cleanup;
48284092dbcSRoman Gushchin 
48384092dbcSRoman Gushchin 	current = cg_read_long(cgroup, "memory.current");
484c1a31a2fSDavid Vernet 	if (!values_close(current, MB(30), 5))
48584092dbcSRoman Gushchin 		goto cleanup;
48684092dbcSRoman Gushchin 
48784092dbcSRoman Gushchin 	ret = 0;
48884092dbcSRoman Gushchin 
48984092dbcSRoman Gushchin cleanup:
49084092dbcSRoman Gushchin 	close(fd);
49184092dbcSRoman Gushchin 	return ret;
49284092dbcSRoman Gushchin 
49384092dbcSRoman Gushchin }
49484092dbcSRoman Gushchin 
49584092dbcSRoman Gushchin /*
49684092dbcSRoman Gushchin  * This test checks that memory.high limits the amount of
49784092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
49884092dbcSRoman Gushchin  * or pagecache.
49984092dbcSRoman Gushchin  */
test_memcg_high(const char * root)50084092dbcSRoman Gushchin static int test_memcg_high(const char *root)
50184092dbcSRoman Gushchin {
50284092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
50384092dbcSRoman Gushchin 	char *memcg;
50484092dbcSRoman Gushchin 	long high;
50584092dbcSRoman Gushchin 
50684092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
50784092dbcSRoman Gushchin 	if (!memcg)
50884092dbcSRoman Gushchin 		goto cleanup;
50984092dbcSRoman Gushchin 
51084092dbcSRoman Gushchin 	if (cg_create(memcg))
51184092dbcSRoman Gushchin 		goto cleanup;
51284092dbcSRoman Gushchin 
51384092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.high", "max\n"))
51484092dbcSRoman Gushchin 		goto cleanup;
51584092dbcSRoman Gushchin 
51684092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
51784092dbcSRoman Gushchin 		goto cleanup;
51884092dbcSRoman Gushchin 
51984092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.high", "30M"))
52084092dbcSRoman Gushchin 		goto cleanup;
52184092dbcSRoman Gushchin 
522be74553fSRoman Gushchin 	if (cg_run(memcg, alloc_anon, (void *)MB(31)))
52384092dbcSRoman Gushchin 		goto cleanup;
52484092dbcSRoman Gushchin 
52584092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_pagecache_50M_check, NULL))
52684092dbcSRoman Gushchin 		goto cleanup;
52784092dbcSRoman Gushchin 
52884092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
52984092dbcSRoman Gushchin 		goto cleanup;
53084092dbcSRoman Gushchin 
53184092dbcSRoman Gushchin 	high = cg_read_key_long(memcg, "memory.events", "high ");
53284092dbcSRoman Gushchin 	if (high <= 0)
53384092dbcSRoman Gushchin 		goto cleanup;
53484092dbcSRoman Gushchin 
53584092dbcSRoman Gushchin 	ret = KSFT_PASS;
53684092dbcSRoman Gushchin 
53784092dbcSRoman Gushchin cleanup:
53884092dbcSRoman Gushchin 	cg_destroy(memcg);
53984092dbcSRoman Gushchin 	free(memcg);
54084092dbcSRoman Gushchin 
54184092dbcSRoman Gushchin 	return ret;
54284092dbcSRoman Gushchin }
54384092dbcSRoman Gushchin 
alloc_anon_mlock(const char * cgroup,void * arg)5446323ec54SShakeel Butt static int alloc_anon_mlock(const char *cgroup, void *arg)
5456323ec54SShakeel Butt {
5466323ec54SShakeel Butt 	size_t size = (size_t)arg;
5476323ec54SShakeel Butt 	void *buf;
5486323ec54SShakeel Butt 
5496323ec54SShakeel Butt 	buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
5506323ec54SShakeel Butt 		   0, 0);
5516323ec54SShakeel Butt 	if (buf == MAP_FAILED)
5526323ec54SShakeel Butt 		return -1;
5536323ec54SShakeel Butt 
5546323ec54SShakeel Butt 	mlock(buf, size);
5556323ec54SShakeel Butt 	munmap(buf, size);
5566323ec54SShakeel Butt 	return 0;
5576323ec54SShakeel Butt }
5586323ec54SShakeel Butt 
5596323ec54SShakeel Butt /*
5606323ec54SShakeel Butt  * This test checks that memory.high is able to throttle big single shot
5616323ec54SShakeel Butt  * allocation i.e. large allocation within one kernel entry.
5626323ec54SShakeel Butt  */
test_memcg_high_sync(const char * root)5636323ec54SShakeel Butt static int test_memcg_high_sync(const char *root)
5646323ec54SShakeel Butt {
5656323ec54SShakeel Butt 	int ret = KSFT_FAIL, pid, fd = -1;
5666323ec54SShakeel Butt 	char *memcg;
5676323ec54SShakeel Butt 	long pre_high, pre_max;
5686323ec54SShakeel Butt 	long post_high, post_max;
5696323ec54SShakeel Butt 
5706323ec54SShakeel Butt 	memcg = cg_name(root, "memcg_test");
5716323ec54SShakeel Butt 	if (!memcg)
5726323ec54SShakeel Butt 		goto cleanup;
5736323ec54SShakeel Butt 
5746323ec54SShakeel Butt 	if (cg_create(memcg))
5756323ec54SShakeel Butt 		goto cleanup;
5766323ec54SShakeel Butt 
5776323ec54SShakeel Butt 	pre_high = cg_read_key_long(memcg, "memory.events", "high ");
5786323ec54SShakeel Butt 	pre_max = cg_read_key_long(memcg, "memory.events", "max ");
5796323ec54SShakeel Butt 	if (pre_high < 0 || pre_max < 0)
5806323ec54SShakeel Butt 		goto cleanup;
5816323ec54SShakeel Butt 
5826323ec54SShakeel Butt 	if (cg_write(memcg, "memory.swap.max", "0"))
5836323ec54SShakeel Butt 		goto cleanup;
5846323ec54SShakeel Butt 
5856323ec54SShakeel Butt 	if (cg_write(memcg, "memory.high", "30M"))
5866323ec54SShakeel Butt 		goto cleanup;
5876323ec54SShakeel Butt 
5886323ec54SShakeel Butt 	if (cg_write(memcg, "memory.max", "140M"))
5896323ec54SShakeel Butt 		goto cleanup;
5906323ec54SShakeel Butt 
5916323ec54SShakeel Butt 	fd = memcg_prepare_for_wait(memcg);
5926323ec54SShakeel Butt 	if (fd < 0)
5936323ec54SShakeel Butt 		goto cleanup;
5946323ec54SShakeel Butt 
5956323ec54SShakeel Butt 	pid = cg_run_nowait(memcg, alloc_anon_mlock, (void *)MB(200));
5966323ec54SShakeel Butt 	if (pid < 0)
5976323ec54SShakeel Butt 		goto cleanup;
5986323ec54SShakeel Butt 
5996323ec54SShakeel Butt 	cg_wait_for(fd);
6006323ec54SShakeel Butt 
6016323ec54SShakeel Butt 	post_high = cg_read_key_long(memcg, "memory.events", "high ");
6026323ec54SShakeel Butt 	post_max = cg_read_key_long(memcg, "memory.events", "max ");
6036323ec54SShakeel Butt 	if (post_high < 0 || post_max < 0)
6046323ec54SShakeel Butt 		goto cleanup;
6056323ec54SShakeel Butt 
6066323ec54SShakeel Butt 	if (pre_high == post_high || pre_max != post_max)
6076323ec54SShakeel Butt 		goto cleanup;
6086323ec54SShakeel Butt 
6096323ec54SShakeel Butt 	ret = KSFT_PASS;
6106323ec54SShakeel Butt 
6116323ec54SShakeel Butt cleanup:
6126323ec54SShakeel Butt 	if (fd >= 0)
6136323ec54SShakeel Butt 		close(fd);
6146323ec54SShakeel Butt 	cg_destroy(memcg);
6156323ec54SShakeel Butt 	free(memcg);
6166323ec54SShakeel Butt 
6176323ec54SShakeel Butt 	return ret;
6186323ec54SShakeel Butt }
6196323ec54SShakeel Butt 
62084092dbcSRoman Gushchin /*
62184092dbcSRoman Gushchin  * This test checks that memory.max limits the amount of
62284092dbcSRoman Gushchin  * memory which can be consumed by either anonymous memory
62384092dbcSRoman Gushchin  * or pagecache.
62484092dbcSRoman Gushchin  */
test_memcg_max(const char * root)62584092dbcSRoman Gushchin static int test_memcg_max(const char *root)
62684092dbcSRoman Gushchin {
62784092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
62884092dbcSRoman Gushchin 	char *memcg;
62984092dbcSRoman Gushchin 	long current, max;
63084092dbcSRoman Gushchin 
63184092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
63284092dbcSRoman Gushchin 	if (!memcg)
63384092dbcSRoman Gushchin 		goto cleanup;
63484092dbcSRoman Gushchin 
63584092dbcSRoman Gushchin 	if (cg_create(memcg))
63684092dbcSRoman Gushchin 		goto cleanup;
63784092dbcSRoman Gushchin 
63884092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
63984092dbcSRoman Gushchin 		goto cleanup;
64084092dbcSRoman Gushchin 
64184092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
64284092dbcSRoman Gushchin 		goto cleanup;
64384092dbcSRoman Gushchin 
64484092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
64584092dbcSRoman Gushchin 		goto cleanup;
64684092dbcSRoman Gushchin 
64784092dbcSRoman Gushchin 	/* Should be killed by OOM killer */
64884092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
64984092dbcSRoman Gushchin 		goto cleanup;
65084092dbcSRoman Gushchin 
65184092dbcSRoman Gushchin 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
65284092dbcSRoman Gushchin 		goto cleanup;
65384092dbcSRoman Gushchin 
65484092dbcSRoman Gushchin 	current = cg_read_long(memcg, "memory.current");
65584092dbcSRoman Gushchin 	if (current > MB(30) || !current)
65684092dbcSRoman Gushchin 		goto cleanup;
65784092dbcSRoman Gushchin 
65884092dbcSRoman Gushchin 	max = cg_read_key_long(memcg, "memory.events", "max ");
65984092dbcSRoman Gushchin 	if (max <= 0)
66084092dbcSRoman Gushchin 		goto cleanup;
66184092dbcSRoman Gushchin 
66284092dbcSRoman Gushchin 	ret = KSFT_PASS;
66384092dbcSRoman Gushchin 
66484092dbcSRoman Gushchin cleanup:
66584092dbcSRoman Gushchin 	cg_destroy(memcg);
66684092dbcSRoman Gushchin 	free(memcg);
66784092dbcSRoman Gushchin 
66884092dbcSRoman Gushchin 	return ret;
66984092dbcSRoman Gushchin }
67084092dbcSRoman Gushchin 
671eae3cb2eSYosry Ahmed /*
672e5d64edaSYosry Ahmed  * Reclaim from @memcg until usage reaches @goal by writing to
673e5d64edaSYosry Ahmed  * memory.reclaim.
674e5d64edaSYosry Ahmed  *
675e5d64edaSYosry Ahmed  * This function will return false if the usage is already below the
676e5d64edaSYosry Ahmed  * goal.
677e5d64edaSYosry Ahmed  *
678e5d64edaSYosry Ahmed  * This function assumes that writing to memory.reclaim is the only
679e5d64edaSYosry Ahmed  * source of change in memory.current (no concurrent allocations or
680e5d64edaSYosry Ahmed  * reclaim).
681e5d64edaSYosry Ahmed  *
682e5d64edaSYosry Ahmed  * This function makes sure memory.reclaim is sane. It will return
683e5d64edaSYosry Ahmed  * false if memory.reclaim's error codes do not make sense, even if
684e5d64edaSYosry Ahmed  * the usage goal was satisfied.
685e5d64edaSYosry Ahmed  */
reclaim_until(const char * memcg,long goal)686e5d64edaSYosry Ahmed static bool reclaim_until(const char *memcg, long goal)
687e5d64edaSYosry Ahmed {
688e5d64edaSYosry Ahmed 	char buf[64];
689e5d64edaSYosry Ahmed 	int retries, err;
690e5d64edaSYosry Ahmed 	long current, to_reclaim;
691e5d64edaSYosry Ahmed 	bool reclaimed = false;
692e5d64edaSYosry Ahmed 
693e5d64edaSYosry Ahmed 	for (retries = 5; retries > 0; retries--) {
694e5d64edaSYosry Ahmed 		current = cg_read_long(memcg, "memory.current");
695e5d64edaSYosry Ahmed 
696e5d64edaSYosry Ahmed 		if (current < goal || values_close(current, goal, 3))
697e5d64edaSYosry Ahmed 			break;
698e5d64edaSYosry Ahmed 		/* Did memory.reclaim return 0 incorrectly? */
699e5d64edaSYosry Ahmed 		else if (reclaimed)
700e5d64edaSYosry Ahmed 			return false;
701e5d64edaSYosry Ahmed 
702e5d64edaSYosry Ahmed 		to_reclaim = current - goal;
703e5d64edaSYosry Ahmed 		snprintf(buf, sizeof(buf), "%ld", to_reclaim);
704e5d64edaSYosry Ahmed 		err = cg_write(memcg, "memory.reclaim", buf);
705e5d64edaSYosry Ahmed 		if (!err)
706e5d64edaSYosry Ahmed 			reclaimed = true;
707e5d64edaSYosry Ahmed 		else if (err != -EAGAIN)
708e5d64edaSYosry Ahmed 			return false;
709e5d64edaSYosry Ahmed 	}
710e5d64edaSYosry Ahmed 	return reclaimed;
711e5d64edaSYosry Ahmed }
712e5d64edaSYosry Ahmed 
713e5d64edaSYosry Ahmed /*
714eae3cb2eSYosry Ahmed  * This test checks that memory.reclaim reclaims the given
715eae3cb2eSYosry Ahmed  * amount of memory (from both anon and file, if possible).
716eae3cb2eSYosry Ahmed  */
test_memcg_reclaim(const char * root)717eae3cb2eSYosry Ahmed static int test_memcg_reclaim(const char *root)
718eae3cb2eSYosry Ahmed {
719eae3cb2eSYosry Ahmed 	int ret = KSFT_FAIL, fd, retries;
720eae3cb2eSYosry Ahmed 	char *memcg;
721e5d64edaSYosry Ahmed 	long current, expected_usage;
722eae3cb2eSYosry Ahmed 
723eae3cb2eSYosry Ahmed 	memcg = cg_name(root, "memcg_test");
724eae3cb2eSYosry Ahmed 	if (!memcg)
725eae3cb2eSYosry Ahmed 		goto cleanup;
726eae3cb2eSYosry Ahmed 
727eae3cb2eSYosry Ahmed 	if (cg_create(memcg))
728eae3cb2eSYosry Ahmed 		goto cleanup;
729eae3cb2eSYosry Ahmed 
730eae3cb2eSYosry Ahmed 	current = cg_read_long(memcg, "memory.current");
731eae3cb2eSYosry Ahmed 	if (current != 0)
732eae3cb2eSYosry Ahmed 		goto cleanup;
733eae3cb2eSYosry Ahmed 
734eae3cb2eSYosry Ahmed 	fd = get_temp_fd();
735eae3cb2eSYosry Ahmed 	if (fd < 0)
736eae3cb2eSYosry Ahmed 		goto cleanup;
737eae3cb2eSYosry Ahmed 
738eae3cb2eSYosry Ahmed 	cg_run_nowait(memcg, alloc_pagecache_50M_noexit, (void *)(long)fd);
739eae3cb2eSYosry Ahmed 
740eae3cb2eSYosry Ahmed 	/*
741eae3cb2eSYosry Ahmed 	 * If swap is enabled, try to reclaim from both anon and file, else try
742eae3cb2eSYosry Ahmed 	 * to reclaim from file only.
743eae3cb2eSYosry Ahmed 	 */
744eae3cb2eSYosry Ahmed 	if (is_swap_enabled()) {
745eae3cb2eSYosry Ahmed 		cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(50));
746eae3cb2eSYosry Ahmed 		expected_usage = MB(100);
747eae3cb2eSYosry Ahmed 	} else
748eae3cb2eSYosry Ahmed 		expected_usage = MB(50);
749eae3cb2eSYosry Ahmed 
750eae3cb2eSYosry Ahmed 	/*
751eae3cb2eSYosry Ahmed 	 * Wait until current usage reaches the expected usage (or we run out of
752eae3cb2eSYosry Ahmed 	 * retries).
753eae3cb2eSYosry Ahmed 	 */
754eae3cb2eSYosry Ahmed 	retries = 5;
755eae3cb2eSYosry Ahmed 	while (!values_close(cg_read_long(memcg, "memory.current"),
756eae3cb2eSYosry Ahmed 			    expected_usage, 10)) {
757eae3cb2eSYosry Ahmed 		if (retries--) {
758eae3cb2eSYosry Ahmed 			sleep(1);
759eae3cb2eSYosry Ahmed 			continue;
760eae3cb2eSYosry Ahmed 		} else {
761eae3cb2eSYosry Ahmed 			fprintf(stderr,
762eae3cb2eSYosry Ahmed 				"failed to allocate %ld for memcg reclaim test\n",
763eae3cb2eSYosry Ahmed 				expected_usage);
764eae3cb2eSYosry Ahmed 			goto cleanup;
765eae3cb2eSYosry Ahmed 		}
766eae3cb2eSYosry Ahmed 	}
767eae3cb2eSYosry Ahmed 
768eae3cb2eSYosry Ahmed 	/*
769eae3cb2eSYosry Ahmed 	 * Reclaim until current reaches 30M, this makes sure we hit both anon
770eae3cb2eSYosry Ahmed 	 * and file if swap is enabled.
771eae3cb2eSYosry Ahmed 	 */
772e5d64edaSYosry Ahmed 	if (!reclaim_until(memcg, MB(30)))
773eae3cb2eSYosry Ahmed 		goto cleanup;
774eae3cb2eSYosry Ahmed 
775eae3cb2eSYosry Ahmed 	ret = KSFT_PASS;
776eae3cb2eSYosry Ahmed cleanup:
777eae3cb2eSYosry Ahmed 	cg_destroy(memcg);
778eae3cb2eSYosry Ahmed 	free(memcg);
779eae3cb2eSYosry Ahmed 	close(fd);
780eae3cb2eSYosry Ahmed 
781eae3cb2eSYosry Ahmed 	return ret;
782eae3cb2eSYosry Ahmed }
783eae3cb2eSYosry Ahmed 
alloc_anon_50M_check_swap(const char * cgroup,void * arg)784478b2784SMike Rapoport static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
785478b2784SMike Rapoport {
786478b2784SMike Rapoport 	long mem_max = (long)arg;
787478b2784SMike Rapoport 	size_t size = MB(50);
788478b2784SMike Rapoport 	char *buf, *ptr;
789478b2784SMike Rapoport 	long mem_current, swap_current;
790478b2784SMike Rapoport 	int ret = -1;
791478b2784SMike Rapoport 
792478b2784SMike Rapoport 	buf = malloc(size);
793c83f320eSIvan Orlov 	if (buf == NULL) {
794c83f320eSIvan Orlov 		fprintf(stderr, "malloc() failed\n");
795c83f320eSIvan Orlov 		return -1;
796c83f320eSIvan Orlov 	}
797c83f320eSIvan Orlov 
798478b2784SMike Rapoport 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
799478b2784SMike Rapoport 		*ptr = 0;
800478b2784SMike Rapoport 
801478b2784SMike Rapoport 	mem_current = cg_read_long(cgroup, "memory.current");
802478b2784SMike Rapoport 	if (!mem_current || !values_close(mem_current, mem_max, 3))
803478b2784SMike Rapoport 		goto cleanup;
804478b2784SMike Rapoport 
805478b2784SMike Rapoport 	swap_current = cg_read_long(cgroup, "memory.swap.current");
806478b2784SMike Rapoport 	if (!swap_current ||
807478b2784SMike Rapoport 	    !values_close(mem_current + swap_current, size, 3))
808478b2784SMike Rapoport 		goto cleanup;
809478b2784SMike Rapoport 
810478b2784SMike Rapoport 	ret = 0;
811478b2784SMike Rapoport cleanup:
812478b2784SMike Rapoport 	free(buf);
813478b2784SMike Rapoport 	return ret;
814478b2784SMike Rapoport }
815478b2784SMike Rapoport 
816478b2784SMike Rapoport /*
817478b2784SMike Rapoport  * This test checks that memory.swap.max limits the amount of
818478b2784SMike Rapoport  * anonymous memory which can be swapped out.
819478b2784SMike Rapoport  */
test_memcg_swap_max(const char * root)820478b2784SMike Rapoport static int test_memcg_swap_max(const char *root)
821478b2784SMike Rapoport {
822478b2784SMike Rapoport 	int ret = KSFT_FAIL;
823478b2784SMike Rapoport 	char *memcg;
824478b2784SMike Rapoport 	long max;
825478b2784SMike Rapoport 
826478b2784SMike Rapoport 	if (!is_swap_enabled())
827478b2784SMike Rapoport 		return KSFT_SKIP;
828478b2784SMike Rapoport 
829478b2784SMike Rapoport 	memcg = cg_name(root, "memcg_test");
830478b2784SMike Rapoport 	if (!memcg)
831478b2784SMike Rapoport 		goto cleanup;
832478b2784SMike Rapoport 
833478b2784SMike Rapoport 	if (cg_create(memcg))
834478b2784SMike Rapoport 		goto cleanup;
835478b2784SMike Rapoport 
836478b2784SMike Rapoport 	if (cg_read_long(memcg, "memory.swap.current")) {
837478b2784SMike Rapoport 		ret = KSFT_SKIP;
838478b2784SMike Rapoport 		goto cleanup;
839478b2784SMike Rapoport 	}
840478b2784SMike Rapoport 
841478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.max", "max\n"))
842478b2784SMike Rapoport 		goto cleanup;
843478b2784SMike Rapoport 
844478b2784SMike Rapoport 	if (cg_read_strcmp(memcg, "memory.swap.max", "max\n"))
845478b2784SMike Rapoport 		goto cleanup;
846478b2784SMike Rapoport 
847478b2784SMike Rapoport 	if (cg_write(memcg, "memory.swap.max", "30M"))
848478b2784SMike Rapoport 		goto cleanup;
849478b2784SMike Rapoport 
850478b2784SMike Rapoport 	if (cg_write(memcg, "memory.max", "30M"))
851478b2784SMike Rapoport 		goto cleanup;
852478b2784SMike Rapoport 
853478b2784SMike Rapoport 	/* Should be killed by OOM killer */
854478b2784SMike Rapoport 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
855478b2784SMike Rapoport 		goto cleanup;
856478b2784SMike Rapoport 
857478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
858478b2784SMike Rapoport 		goto cleanup;
859478b2784SMike Rapoport 
860478b2784SMike Rapoport 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
861478b2784SMike Rapoport 		goto cleanup;
862478b2784SMike Rapoport 
863478b2784SMike Rapoport 	if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30)))
864478b2784SMike Rapoport 		goto cleanup;
865478b2784SMike Rapoport 
866478b2784SMike Rapoport 	max = cg_read_key_long(memcg, "memory.events", "max ");
867478b2784SMike Rapoport 	if (max <= 0)
868478b2784SMike Rapoport 		goto cleanup;
869478b2784SMike Rapoport 
870478b2784SMike Rapoport 	ret = KSFT_PASS;
871478b2784SMike Rapoport 
872478b2784SMike Rapoport cleanup:
873478b2784SMike Rapoport 	cg_destroy(memcg);
874478b2784SMike Rapoport 	free(memcg);
875478b2784SMike Rapoport 
876478b2784SMike Rapoport 	return ret;
877478b2784SMike Rapoport }
878478b2784SMike Rapoport 
87984092dbcSRoman Gushchin /*
88084092dbcSRoman Gushchin  * This test disables swapping and tries to allocate anonymous memory
88184092dbcSRoman Gushchin  * up to OOM. Then it checks for oom and oom_kill events in
88284092dbcSRoman Gushchin  * memory.events.
88384092dbcSRoman Gushchin  */
test_memcg_oom_events(const char * root)88484092dbcSRoman Gushchin static int test_memcg_oom_events(const char *root)
88584092dbcSRoman Gushchin {
88684092dbcSRoman Gushchin 	int ret = KSFT_FAIL;
88784092dbcSRoman Gushchin 	char *memcg;
88884092dbcSRoman Gushchin 
88984092dbcSRoman Gushchin 	memcg = cg_name(root, "memcg_test");
89084092dbcSRoman Gushchin 	if (!memcg)
89184092dbcSRoman Gushchin 		goto cleanup;
89284092dbcSRoman Gushchin 
89384092dbcSRoman Gushchin 	if (cg_create(memcg))
89484092dbcSRoman Gushchin 		goto cleanup;
89584092dbcSRoman Gushchin 
89684092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.max", "30M"))
89784092dbcSRoman Gushchin 		goto cleanup;
89884092dbcSRoman Gushchin 
89984092dbcSRoman Gushchin 	if (cg_write(memcg, "memory.swap.max", "0"))
90084092dbcSRoman Gushchin 		goto cleanup;
90184092dbcSRoman Gushchin 
90284092dbcSRoman Gushchin 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
90384092dbcSRoman Gushchin 		goto cleanup;
90484092dbcSRoman Gushchin 
90584092dbcSRoman Gushchin 	if (cg_read_strcmp(memcg, "cgroup.procs", ""))
90684092dbcSRoman Gushchin 		goto cleanup;
90784092dbcSRoman Gushchin 
90884092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
90984092dbcSRoman Gushchin 		goto cleanup;
91084092dbcSRoman Gushchin 
91184092dbcSRoman Gushchin 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
91284092dbcSRoman Gushchin 		goto cleanup;
91384092dbcSRoman Gushchin 
91484092dbcSRoman Gushchin 	ret = KSFT_PASS;
91584092dbcSRoman Gushchin 
91684092dbcSRoman Gushchin cleanup:
91784092dbcSRoman Gushchin 	cg_destroy(memcg);
91884092dbcSRoman Gushchin 	free(memcg);
91984092dbcSRoman Gushchin 
92084092dbcSRoman Gushchin 	return ret;
92184092dbcSRoman Gushchin }
92284092dbcSRoman Gushchin 
9235f8f0193SMike Rapoport struct tcp_server_args {
9245f8f0193SMike Rapoport 	unsigned short port;
9255f8f0193SMike Rapoport 	int ctl[2];
9265f8f0193SMike Rapoport };
9275f8f0193SMike Rapoport 
tcp_server(const char * cgroup,void * arg)9285f8f0193SMike Rapoport static int tcp_server(const char *cgroup, void *arg)
9295f8f0193SMike Rapoport {
9305f8f0193SMike Rapoport 	struct tcp_server_args *srv_args = arg;
9315f8f0193SMike Rapoport 	struct sockaddr_in6 saddr = { 0 };
9325f8f0193SMike Rapoport 	socklen_t slen = sizeof(saddr);
9335f8f0193SMike Rapoport 	int sk, client_sk, ctl_fd, yes = 1, ret = -1;
9345f8f0193SMike Rapoport 
9355f8f0193SMike Rapoport 	close(srv_args->ctl[0]);
9365f8f0193SMike Rapoport 	ctl_fd = srv_args->ctl[1];
9375f8f0193SMike Rapoport 
9385f8f0193SMike Rapoport 	saddr.sin6_family = AF_INET6;
9395f8f0193SMike Rapoport 	saddr.sin6_addr = in6addr_any;
9405f8f0193SMike Rapoport 	saddr.sin6_port = htons(srv_args->port);
9415f8f0193SMike Rapoport 
9425f8f0193SMike Rapoport 	sk = socket(AF_INET6, SOCK_STREAM, 0);
9435f8f0193SMike Rapoport 	if (sk < 0)
9445f8f0193SMike Rapoport 		return ret;
9455f8f0193SMike Rapoport 
9465f8f0193SMike Rapoport 	if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
9475f8f0193SMike Rapoport 		goto cleanup;
9485f8f0193SMike Rapoport 
9495f8f0193SMike Rapoport 	if (bind(sk, (struct sockaddr *)&saddr, slen)) {
9505f8f0193SMike Rapoport 		write(ctl_fd, &errno, sizeof(errno));
9515f8f0193SMike Rapoport 		goto cleanup;
9525f8f0193SMike Rapoport 	}
9535f8f0193SMike Rapoport 
9545f8f0193SMike Rapoport 	if (listen(sk, 1))
9555f8f0193SMike Rapoport 		goto cleanup;
9565f8f0193SMike Rapoport 
9575f8f0193SMike Rapoport 	ret = 0;
9585f8f0193SMike Rapoport 	if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) {
9595f8f0193SMike Rapoport 		ret = -1;
9605f8f0193SMike Rapoport 		goto cleanup;
9615f8f0193SMike Rapoport 	}
9625f8f0193SMike Rapoport 
9635f8f0193SMike Rapoport 	client_sk = accept(sk, NULL, NULL);
9645f8f0193SMike Rapoport 	if (client_sk < 0)
9655f8f0193SMike Rapoport 		goto cleanup;
9665f8f0193SMike Rapoport 
9675f8f0193SMike Rapoport 	ret = -1;
9685f8f0193SMike Rapoport 	for (;;) {
9695f8f0193SMike Rapoport 		uint8_t buf[0x100000];
9705f8f0193SMike Rapoport 
9715f8f0193SMike Rapoport 		if (write(client_sk, buf, sizeof(buf)) <= 0) {
9725f8f0193SMike Rapoport 			if (errno == ECONNRESET)
9735f8f0193SMike Rapoport 				ret = 0;
9745f8f0193SMike Rapoport 			break;
9755f8f0193SMike Rapoport 		}
9765f8f0193SMike Rapoport 	}
9775f8f0193SMike Rapoport 
9785f8f0193SMike Rapoport 	close(client_sk);
9795f8f0193SMike Rapoport 
9805f8f0193SMike Rapoport cleanup:
9815f8f0193SMike Rapoport 	close(sk);
9825f8f0193SMike Rapoport 	return ret;
9835f8f0193SMike Rapoport }
9845f8f0193SMike Rapoport 
tcp_client(const char * cgroup,unsigned short port)9855f8f0193SMike Rapoport static int tcp_client(const char *cgroup, unsigned short port)
9865f8f0193SMike Rapoport {
9875f8f0193SMike Rapoport 	const char server[] = "localhost";
9885f8f0193SMike Rapoport 	struct addrinfo *ai;
9895f8f0193SMike Rapoport 	char servport[6];
9905f8f0193SMike Rapoport 	int retries = 0x10; /* nice round number */
9915f8f0193SMike Rapoport 	int sk, ret;
992*3360cd30SHaifeng Xu 	long allocated;
9935f8f0193SMike Rapoport 
994*3360cd30SHaifeng Xu 	allocated = cg_read_long(cgroup, "memory.current");
9955f8f0193SMike Rapoport 	snprintf(servport, sizeof(servport), "%hd", port);
9965f8f0193SMike Rapoport 	ret = getaddrinfo(server, servport, NULL, &ai);
9975f8f0193SMike Rapoport 	if (ret)
9985f8f0193SMike Rapoport 		return ret;
9995f8f0193SMike Rapoport 
10005f8f0193SMike Rapoport 	sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
10015f8f0193SMike Rapoport 	if (sk < 0)
10025f8f0193SMike Rapoport 		goto free_ainfo;
10035f8f0193SMike Rapoport 
10045f8f0193SMike Rapoport 	ret = connect(sk, ai->ai_addr, ai->ai_addrlen);
10055f8f0193SMike Rapoport 	if (ret < 0)
10065f8f0193SMike Rapoport 		goto close_sk;
10075f8f0193SMike Rapoport 
10085f8f0193SMike Rapoport 	ret = KSFT_FAIL;
10095f8f0193SMike Rapoport 	while (retries--) {
10105f8f0193SMike Rapoport 		uint8_t buf[0x100000];
10115f8f0193SMike Rapoport 		long current, sock;
10125f8f0193SMike Rapoport 
10135f8f0193SMike Rapoport 		if (read(sk, buf, sizeof(buf)) <= 0)
10145f8f0193SMike Rapoport 			goto close_sk;
10155f8f0193SMike Rapoport 
10165f8f0193SMike Rapoport 		current = cg_read_long(cgroup, "memory.current");
10175f8f0193SMike Rapoport 		sock = cg_read_key_long(cgroup, "memory.stat", "sock ");
10185f8f0193SMike Rapoport 
10195f8f0193SMike Rapoport 		if (current < 0 || sock < 0)
10205f8f0193SMike Rapoport 			goto close_sk;
10215f8f0193SMike Rapoport 
1022*3360cd30SHaifeng Xu 		/* exclude the memory not related to socket connection */
1023*3360cd30SHaifeng Xu 		if (values_close(current - allocated, sock, 10)) {
10245f8f0193SMike Rapoport 			ret = KSFT_PASS;
10255f8f0193SMike Rapoport 			break;
10265f8f0193SMike Rapoport 		}
10275f8f0193SMike Rapoport 	}
10285f8f0193SMike Rapoport 
10295f8f0193SMike Rapoport close_sk:
10305f8f0193SMike Rapoport 	close(sk);
10315f8f0193SMike Rapoport free_ainfo:
10325f8f0193SMike Rapoport 	freeaddrinfo(ai);
10335f8f0193SMike Rapoport 	return ret;
10345f8f0193SMike Rapoport }
10355f8f0193SMike Rapoport 
10365f8f0193SMike Rapoport /*
10375f8f0193SMike Rapoport  * This test checks socket memory accounting.
10385f8f0193SMike Rapoport  * The test forks a TCP server listens on a random port between 1000
10395f8f0193SMike Rapoport  * and 61000. Once it gets a client connection, it starts writing to
10405f8f0193SMike Rapoport  * its socket.
10415f8f0193SMike Rapoport  * The TCP client interleaves reads from the socket with check whether
10425f8f0193SMike Rapoport  * memory.current and memory.stat.sock are similar.
10435f8f0193SMike Rapoport  */
test_memcg_sock(const char * root)10445f8f0193SMike Rapoport static int test_memcg_sock(const char *root)
10455f8f0193SMike Rapoport {
10465f8f0193SMike Rapoport 	int bind_retries = 5, ret = KSFT_FAIL, pid, err;
10475f8f0193SMike Rapoport 	unsigned short port;
10485f8f0193SMike Rapoport 	char *memcg;
10495f8f0193SMike Rapoport 
10505f8f0193SMike Rapoport 	memcg = cg_name(root, "memcg_test");
10515f8f0193SMike Rapoport 	if (!memcg)
10525f8f0193SMike Rapoport 		goto cleanup;
10535f8f0193SMike Rapoport 
10545f8f0193SMike Rapoport 	if (cg_create(memcg))
10555f8f0193SMike Rapoport 		goto cleanup;
10565f8f0193SMike Rapoport 
10575f8f0193SMike Rapoport 	while (bind_retries--) {
10585f8f0193SMike Rapoport 		struct tcp_server_args args;
10595f8f0193SMike Rapoport 
10605f8f0193SMike Rapoport 		if (pipe(args.ctl))
10615f8f0193SMike Rapoport 			goto cleanup;
10625f8f0193SMike Rapoport 
10635f8f0193SMike Rapoport 		port = args.port = 1000 + rand() % 60000;
10645f8f0193SMike Rapoport 
10655f8f0193SMike Rapoport 		pid = cg_run_nowait(memcg, tcp_server, &args);
10665f8f0193SMike Rapoport 		if (pid < 0)
10675f8f0193SMike Rapoport 			goto cleanup;
10685f8f0193SMike Rapoport 
10695f8f0193SMike Rapoport 		close(args.ctl[1]);
10705f8f0193SMike Rapoport 		if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err))
10715f8f0193SMike Rapoport 			goto cleanup;
10725f8f0193SMike Rapoport 		close(args.ctl[0]);
10735f8f0193SMike Rapoport 
10745f8f0193SMike Rapoport 		if (!err)
10755f8f0193SMike Rapoport 			break;
10765f8f0193SMike Rapoport 		if (err != EADDRINUSE)
10775f8f0193SMike Rapoport 			goto cleanup;
10785f8f0193SMike Rapoport 
10795f8f0193SMike Rapoport 		waitpid(pid, NULL, 0);
10805f8f0193SMike Rapoport 	}
10815f8f0193SMike Rapoport 
10825f8f0193SMike Rapoport 	if (err == EADDRINUSE) {
10835f8f0193SMike Rapoport 		ret = KSFT_SKIP;
10845f8f0193SMike Rapoport 		goto cleanup;
10855f8f0193SMike Rapoport 	}
10865f8f0193SMike Rapoport 
10875f8f0193SMike Rapoport 	if (tcp_client(memcg, port) != KSFT_PASS)
10885f8f0193SMike Rapoport 		goto cleanup;
10895f8f0193SMike Rapoport 
10905f8f0193SMike Rapoport 	waitpid(pid, &err, 0);
10915f8f0193SMike Rapoport 	if (WEXITSTATUS(err))
10925f8f0193SMike Rapoport 		goto cleanup;
10935f8f0193SMike Rapoport 
10945f8f0193SMike Rapoport 	if (cg_read_long(memcg, "memory.current") < 0)
10955f8f0193SMike Rapoport 		goto cleanup;
10965f8f0193SMike Rapoport 
10975f8f0193SMike Rapoport 	if (cg_read_key_long(memcg, "memory.stat", "sock "))
10985f8f0193SMike Rapoport 		goto cleanup;
10995f8f0193SMike Rapoport 
11005f8f0193SMike Rapoport 	ret = KSFT_PASS;
11015f8f0193SMike Rapoport 
11025f8f0193SMike Rapoport cleanup:
11035f8f0193SMike Rapoport 	cg_destroy(memcg);
11045f8f0193SMike Rapoport 	free(memcg);
11055f8f0193SMike Rapoport 
11065f8f0193SMike Rapoport 	return ret;
11075f8f0193SMike Rapoport }
11085f8f0193SMike Rapoport 
1109a987785dSJay Kamat /*
1110a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1111a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1112c85bcc91SRoman Gushchin  * processes in the leaf were killed. It also checks that oom_events
1113c85bcc91SRoman Gushchin  * were propagated to the parent level.
1114a987785dSJay Kamat  */
test_memcg_oom_group_leaf_events(const char * root)1115a987785dSJay Kamat static int test_memcg_oom_group_leaf_events(const char *root)
1116a987785dSJay Kamat {
1117a987785dSJay Kamat 	int ret = KSFT_FAIL;
1118a987785dSJay Kamat 	char *parent, *child;
111972b1e03aSDavid Vernet 	long parent_oom_events;
1120a987785dSJay Kamat 
1121a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1122a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1123a987785dSJay Kamat 
1124a987785dSJay Kamat 	if (!parent || !child)
1125a987785dSJay Kamat 		goto cleanup;
1126a987785dSJay Kamat 
1127a987785dSJay Kamat 	if (cg_create(parent))
1128a987785dSJay Kamat 		goto cleanup;
1129a987785dSJay Kamat 
1130a987785dSJay Kamat 	if (cg_create(child))
1131a987785dSJay Kamat 		goto cleanup;
1132a987785dSJay Kamat 
1133a987785dSJay Kamat 	if (cg_write(parent, "cgroup.subtree_control", "+memory"))
1134a987785dSJay Kamat 		goto cleanup;
1135a987785dSJay Kamat 
1136a987785dSJay Kamat 	if (cg_write(child, "memory.max", "50M"))
1137a987785dSJay Kamat 		goto cleanup;
1138a987785dSJay Kamat 
1139a987785dSJay Kamat 	if (cg_write(child, "memory.swap.max", "0"))
1140a987785dSJay Kamat 		goto cleanup;
1141a987785dSJay Kamat 
1142a987785dSJay Kamat 	if (cg_write(child, "memory.oom.group", "1"))
1143a987785dSJay Kamat 		goto cleanup;
1144a987785dSJay Kamat 
1145a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1146a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1147a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1148a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1149a987785dSJay Kamat 		goto cleanup;
1150a987785dSJay Kamat 
1151a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1152a987785dSJay Kamat 		goto cleanup;
1153a987785dSJay Kamat 
1154a987785dSJay Kamat 	if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
1155a987785dSJay Kamat 		goto cleanup;
1156a987785dSJay Kamat 
1157ff3b72a5SMichal Koutný 	parent_oom_events = cg_read_key_long(
1158ff3b72a5SMichal Koutný 			parent, "memory.events", "oom_kill ");
1159ff3b72a5SMichal Koutný 	/*
1160ff3b72a5SMichal Koutný 	 * If memory_localevents is not enabled (the default), the parent should
1161ff3b72a5SMichal Koutný 	 * count OOM events in its children groups. Otherwise, it should not
1162ff3b72a5SMichal Koutný 	 * have observed any events.
1163ff3b72a5SMichal Koutný 	 */
1164ff3b72a5SMichal Koutný 	if (has_localevents && parent_oom_events != 0)
1165ff3b72a5SMichal Koutný 		goto cleanup;
1166ff3b72a5SMichal Koutný 	else if (!has_localevents && parent_oom_events <= 0)
1167a987785dSJay Kamat 		goto cleanup;
1168a987785dSJay Kamat 
1169a987785dSJay Kamat 	ret = KSFT_PASS;
1170a987785dSJay Kamat 
1171a987785dSJay Kamat cleanup:
1172a987785dSJay Kamat 	if (child)
1173a987785dSJay Kamat 		cg_destroy(child);
1174a987785dSJay Kamat 	if (parent)
1175a987785dSJay Kamat 		cg_destroy(parent);
1176a987785dSJay Kamat 	free(child);
1177a987785dSJay Kamat 	free(parent);
1178a987785dSJay Kamat 
1179a987785dSJay Kamat 	return ret;
1180a987785dSJay Kamat }
1181a987785dSJay Kamat 
1182a987785dSJay Kamat /*
1183a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1184a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1185a987785dSJay Kamat  * processes in the parent and leaf were killed.
1186a987785dSJay Kamat  */
test_memcg_oom_group_parent_events(const char * root)1187a987785dSJay Kamat static int test_memcg_oom_group_parent_events(const char *root)
1188a987785dSJay Kamat {
1189a987785dSJay Kamat 	int ret = KSFT_FAIL;
1190a987785dSJay Kamat 	char *parent, *child;
1191a987785dSJay Kamat 
1192a987785dSJay Kamat 	parent = cg_name(root, "memcg_test_0");
1193a987785dSJay Kamat 	child = cg_name(root, "memcg_test_0/memcg_test_1");
1194a987785dSJay Kamat 
1195a987785dSJay Kamat 	if (!parent || !child)
1196a987785dSJay Kamat 		goto cleanup;
1197a987785dSJay Kamat 
1198a987785dSJay Kamat 	if (cg_create(parent))
1199a987785dSJay Kamat 		goto cleanup;
1200a987785dSJay Kamat 
1201a987785dSJay Kamat 	if (cg_create(child))
1202a987785dSJay Kamat 		goto cleanup;
1203a987785dSJay Kamat 
1204a987785dSJay Kamat 	if (cg_write(parent, "memory.max", "80M"))
1205a987785dSJay Kamat 		goto cleanup;
1206a987785dSJay Kamat 
1207a987785dSJay Kamat 	if (cg_write(parent, "memory.swap.max", "0"))
1208a987785dSJay Kamat 		goto cleanup;
1209a987785dSJay Kamat 
1210a987785dSJay Kamat 	if (cg_write(parent, "memory.oom.group", "1"))
1211a987785dSJay Kamat 		goto cleanup;
1212a987785dSJay Kamat 
1213a987785dSJay Kamat 	cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60));
1214a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1215a987785dSJay Kamat 	cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1));
1216a987785dSJay Kamat 
1217a987785dSJay Kamat 	if (!cg_run(child, alloc_anon, (void *)MB(100)))
1218a987785dSJay Kamat 		goto cleanup;
1219a987785dSJay Kamat 
1220a987785dSJay Kamat 	if (cg_test_proc_killed(child))
1221a987785dSJay Kamat 		goto cleanup;
1222a987785dSJay Kamat 	if (cg_test_proc_killed(parent))
1223a987785dSJay Kamat 		goto cleanup;
1224a987785dSJay Kamat 
1225a987785dSJay Kamat 	ret = KSFT_PASS;
1226a987785dSJay Kamat 
1227a987785dSJay Kamat cleanup:
1228a987785dSJay Kamat 	if (child)
1229a987785dSJay Kamat 		cg_destroy(child);
1230a987785dSJay Kamat 	if (parent)
1231a987785dSJay Kamat 		cg_destroy(parent);
1232a987785dSJay Kamat 	free(child);
1233a987785dSJay Kamat 	free(parent);
1234a987785dSJay Kamat 
1235a987785dSJay Kamat 	return ret;
1236a987785dSJay Kamat }
1237a987785dSJay Kamat 
1238a987785dSJay Kamat /*
1239a987785dSJay Kamat  * This test disables swapping and tries to allocate anonymous memory
1240a987785dSJay Kamat  * up to OOM with memory.group.oom set. Then it checks that all
1241a987785dSJay Kamat  * processes were killed except those set with OOM_SCORE_ADJ_MIN
1242a987785dSJay Kamat  */
test_memcg_oom_group_score_events(const char * root)1243a987785dSJay Kamat static int test_memcg_oom_group_score_events(const char *root)
1244a987785dSJay Kamat {
1245a987785dSJay Kamat 	int ret = KSFT_FAIL;
1246a987785dSJay Kamat 	char *memcg;
1247a987785dSJay Kamat 	int safe_pid;
1248a987785dSJay Kamat 
1249a987785dSJay Kamat 	memcg = cg_name(root, "memcg_test_0");
1250a987785dSJay Kamat 
1251a987785dSJay Kamat 	if (!memcg)
1252a987785dSJay Kamat 		goto cleanup;
1253a987785dSJay Kamat 
1254a987785dSJay Kamat 	if (cg_create(memcg))
1255a987785dSJay Kamat 		goto cleanup;
1256a987785dSJay Kamat 
1257a987785dSJay Kamat 	if (cg_write(memcg, "memory.max", "50M"))
1258a987785dSJay Kamat 		goto cleanup;
1259a987785dSJay Kamat 
1260a987785dSJay Kamat 	if (cg_write(memcg, "memory.swap.max", "0"))
1261a987785dSJay Kamat 		goto cleanup;
1262a987785dSJay Kamat 
1263a987785dSJay Kamat 	if (cg_write(memcg, "memory.oom.group", "1"))
1264a987785dSJay Kamat 		goto cleanup;
1265a987785dSJay Kamat 
1266a987785dSJay Kamat 	safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1267a987785dSJay Kamat 	if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN))
1268a987785dSJay Kamat 		goto cleanup;
1269a987785dSJay Kamat 
1270a987785dSJay Kamat 	cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1));
1271a987785dSJay Kamat 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
1272a987785dSJay Kamat 		goto cleanup;
1273a987785dSJay Kamat 
1274ff3b72a5SMichal Koutný 	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3)
1275ff3b72a5SMichal Koutný 		goto cleanup;
1276a987785dSJay Kamat 
1277a987785dSJay Kamat 	if (kill(safe_pid, SIGKILL))
1278a987785dSJay Kamat 		goto cleanup;
1279a987785dSJay Kamat 
1280ff3b72a5SMichal Koutný 	ret = KSFT_PASS;
1281ff3b72a5SMichal Koutný 
1282a987785dSJay Kamat cleanup:
1283a987785dSJay Kamat 	if (memcg)
1284a987785dSJay Kamat 		cg_destroy(memcg);
1285a987785dSJay Kamat 	free(memcg);
1286a987785dSJay Kamat 
1287a987785dSJay Kamat 	return ret;
1288a987785dSJay Kamat }
1289a987785dSJay Kamat 
129084092dbcSRoman Gushchin #define T(x) { x, #x }
129184092dbcSRoman Gushchin struct memcg_test {
129284092dbcSRoman Gushchin 	int (*fn)(const char *root);
129384092dbcSRoman Gushchin 	const char *name;
129484092dbcSRoman Gushchin } tests[] = {
129584092dbcSRoman Gushchin 	T(test_memcg_subtree_control),
129684092dbcSRoman Gushchin 	T(test_memcg_current),
129784092dbcSRoman Gushchin 	T(test_memcg_min),
129884092dbcSRoman Gushchin 	T(test_memcg_low),
129984092dbcSRoman Gushchin 	T(test_memcg_high),
13006323ec54SShakeel Butt 	T(test_memcg_high_sync),
130184092dbcSRoman Gushchin 	T(test_memcg_max),
1302eae3cb2eSYosry Ahmed 	T(test_memcg_reclaim),
130384092dbcSRoman Gushchin 	T(test_memcg_oom_events),
1304478b2784SMike Rapoport 	T(test_memcg_swap_max),
13055f8f0193SMike Rapoport 	T(test_memcg_sock),
1306a987785dSJay Kamat 	T(test_memcg_oom_group_leaf_events),
1307a987785dSJay Kamat 	T(test_memcg_oom_group_parent_events),
1308a987785dSJay Kamat 	T(test_memcg_oom_group_score_events),
130984092dbcSRoman Gushchin };
131084092dbcSRoman Gushchin #undef T
131184092dbcSRoman Gushchin 
main(int argc,char ** argv)131284092dbcSRoman Gushchin int main(int argc, char **argv)
131384092dbcSRoman Gushchin {
131484092dbcSRoman Gushchin 	char root[PATH_MAX];
1315cdc69458SDavid Vernet 	int i, proc_status, ret = EXIT_SUCCESS;
131684092dbcSRoman Gushchin 
131784092dbcSRoman Gushchin 	if (cg_find_unified_root(root, sizeof(root)))
131884092dbcSRoman Gushchin 		ksft_exit_skip("cgroup v2 isn't mounted\n");
131984092dbcSRoman Gushchin 
132084092dbcSRoman Gushchin 	/*
132184092dbcSRoman Gushchin 	 * Check that memory controller is available:
132284092dbcSRoman Gushchin 	 * memory is listed in cgroup.controllers
132384092dbcSRoman Gushchin 	 */
132484092dbcSRoman Gushchin 	if (cg_read_strstr(root, "cgroup.controllers", "memory"))
132584092dbcSRoman Gushchin 		ksft_exit_skip("memory controller isn't available\n");
132684092dbcSRoman Gushchin 
1327f6131f28SAlex Shi 	if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
1328f6131f28SAlex Shi 		if (cg_write(root, "cgroup.subtree_control", "+memory"))
1329f6131f28SAlex Shi 			ksft_exit_skip("Failed to set memory controller\n");
1330f6131f28SAlex Shi 
1331cdc69458SDavid Vernet 	proc_status = proc_mount_contains("memory_recursiveprot");
1332cdc69458SDavid Vernet 	if (proc_status < 0)
1333cdc69458SDavid Vernet 		ksft_exit_skip("Failed to query cgroup mount option\n");
1334cdc69458SDavid Vernet 	has_recursiveprot = proc_status;
1335cdc69458SDavid Vernet 
133672b1e03aSDavid Vernet 	proc_status = proc_mount_contains("memory_localevents");
133772b1e03aSDavid Vernet 	if (proc_status < 0)
133872b1e03aSDavid Vernet 		ksft_exit_skip("Failed to query cgroup mount option\n");
133972b1e03aSDavid Vernet 	has_localevents = proc_status;
134072b1e03aSDavid Vernet 
134184092dbcSRoman Gushchin 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
134284092dbcSRoman Gushchin 		switch (tests[i].fn(root)) {
134384092dbcSRoman Gushchin 		case KSFT_PASS:
134484092dbcSRoman Gushchin 			ksft_test_result_pass("%s\n", tests[i].name);
134584092dbcSRoman Gushchin 			break;
134684092dbcSRoman Gushchin 		case KSFT_SKIP:
134784092dbcSRoman Gushchin 			ksft_test_result_skip("%s\n", tests[i].name);
134884092dbcSRoman Gushchin 			break;
134984092dbcSRoman Gushchin 		default:
135084092dbcSRoman Gushchin 			ret = EXIT_FAILURE;
135184092dbcSRoman Gushchin 			ksft_test_result_fail("%s\n", tests[i].name);
135284092dbcSRoman Gushchin 			break;
135384092dbcSRoman Gushchin 		}
135484092dbcSRoman Gushchin 	}
135584092dbcSRoman Gushchin 
135684092dbcSRoman Gushchin 	return ret;
135784092dbcSRoman Gushchin }
1358