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