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