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