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