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: 250*6a359190SMichal Koutný * A memory.min = 0, memory.max = 200M 251f10b6e9aSMichal Koutný * A/B memory.min = 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 260*6a359190SMichal Koutný * memory pressure in A. 26184092dbcSRoman Gushchin * 262f10b6e9aSMichal Koutný * Then it checks actual memory usages and expects that: 26384092dbcSRoman Gushchin * A/B memory.current ~= 50M 264f10b6e9aSMichal Koutný * A/B/C memory.current ~= 29M 265f10b6e9aSMichal Koutný * A/B/D memory.current ~= 21M 266f10b6e9aSMichal Koutný * A/B/E memory.current ~= 0 267f10b6e9aSMichal Koutný * A/B/F memory.current = 0 268f10b6e9aSMichal Koutný * (for origin of the numbers, see model in memcg_protection.m.) 26984092dbcSRoman Gushchin * 27084092dbcSRoman Gushchin * After that it tries to allocate more than there is 27184092dbcSRoman Gushchin * unprotected memory in A available, and checks 27284092dbcSRoman Gushchin * checks that memory.min protects pagecache even 27384092dbcSRoman Gushchin * in this case. 27484092dbcSRoman Gushchin */ 27584092dbcSRoman Gushchin static int test_memcg_min(const char *root) 27684092dbcSRoman Gushchin { 27784092dbcSRoman Gushchin int ret = KSFT_FAIL; 27884092dbcSRoman Gushchin char *parent[3] = {NULL}; 27984092dbcSRoman Gushchin char *children[4] = {NULL}; 28084092dbcSRoman Gushchin long c[4]; 28184092dbcSRoman Gushchin int i, attempts; 28284092dbcSRoman Gushchin int fd; 28384092dbcSRoman Gushchin 28484092dbcSRoman Gushchin fd = get_temp_fd(); 28584092dbcSRoman Gushchin if (fd < 0) 28684092dbcSRoman Gushchin goto cleanup; 28784092dbcSRoman Gushchin 28884092dbcSRoman Gushchin parent[0] = cg_name(root, "memcg_test_0"); 28984092dbcSRoman Gushchin if (!parent[0]) 29084092dbcSRoman Gushchin goto cleanup; 29184092dbcSRoman Gushchin 29284092dbcSRoman Gushchin parent[1] = cg_name(parent[0], "memcg_test_1"); 29384092dbcSRoman Gushchin if (!parent[1]) 29484092dbcSRoman Gushchin goto cleanup; 29584092dbcSRoman Gushchin 29684092dbcSRoman Gushchin parent[2] = cg_name(parent[0], "memcg_test_2"); 29784092dbcSRoman Gushchin if (!parent[2]) 29884092dbcSRoman Gushchin goto cleanup; 29984092dbcSRoman Gushchin 30084092dbcSRoman Gushchin if (cg_create(parent[0])) 30184092dbcSRoman Gushchin goto cleanup; 30284092dbcSRoman Gushchin 30384092dbcSRoman Gushchin if (cg_read_long(parent[0], "memory.min")) { 30484092dbcSRoman Gushchin ret = KSFT_SKIP; 30584092dbcSRoman Gushchin goto cleanup; 30684092dbcSRoman Gushchin } 30784092dbcSRoman Gushchin 30884092dbcSRoman Gushchin if (cg_write(parent[0], "cgroup.subtree_control", "+memory")) 30984092dbcSRoman Gushchin goto cleanup; 31084092dbcSRoman Gushchin 31184092dbcSRoman Gushchin if (cg_write(parent[0], "memory.max", "200M")) 31284092dbcSRoman Gushchin goto cleanup; 31384092dbcSRoman Gushchin 31484092dbcSRoman Gushchin if (cg_write(parent[0], "memory.swap.max", "0")) 31584092dbcSRoman Gushchin goto cleanup; 31684092dbcSRoman Gushchin 31784092dbcSRoman Gushchin if (cg_create(parent[1])) 31884092dbcSRoman Gushchin goto cleanup; 31984092dbcSRoman Gushchin 32084092dbcSRoman Gushchin if (cg_write(parent[1], "cgroup.subtree_control", "+memory")) 32184092dbcSRoman Gushchin goto cleanup; 32284092dbcSRoman Gushchin 32384092dbcSRoman Gushchin if (cg_create(parent[2])) 32484092dbcSRoman Gushchin goto cleanup; 32584092dbcSRoman Gushchin 32684092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) { 32784092dbcSRoman Gushchin children[i] = cg_name_indexed(parent[1], "child_memcg", i); 32884092dbcSRoman Gushchin if (!children[i]) 32984092dbcSRoman Gushchin goto cleanup; 33084092dbcSRoman Gushchin 33184092dbcSRoman Gushchin if (cg_create(children[i])) 33284092dbcSRoman Gushchin goto cleanup; 33384092dbcSRoman Gushchin 334f0cdaa56SDavid Vernet if (i > 2) 33584092dbcSRoman Gushchin continue; 33684092dbcSRoman Gushchin 33784092dbcSRoman Gushchin cg_run_nowait(children[i], alloc_pagecache_50M_noexit, 33884092dbcSRoman Gushchin (void *)(long)fd); 33984092dbcSRoman Gushchin } 34084092dbcSRoman Gushchin 34184092dbcSRoman Gushchin if (cg_write(parent[1], "memory.min", "50M")) 34284092dbcSRoman Gushchin goto cleanup; 34384092dbcSRoman Gushchin if (cg_write(children[0], "memory.min", "75M")) 34484092dbcSRoman Gushchin goto cleanup; 34584092dbcSRoman Gushchin if (cg_write(children[1], "memory.min", "25M")) 34684092dbcSRoman Gushchin goto cleanup; 347f0cdaa56SDavid Vernet if (cg_write(children[2], "memory.min", "0")) 34884092dbcSRoman Gushchin goto cleanup; 349f0cdaa56SDavid Vernet if (cg_write(children[3], "memory.min", "500M")) 35084092dbcSRoman Gushchin goto cleanup; 35184092dbcSRoman Gushchin 35284092dbcSRoman Gushchin attempts = 0; 35384092dbcSRoman Gushchin while (!values_close(cg_read_long(parent[1], "memory.current"), 35484092dbcSRoman Gushchin MB(150), 3)) { 35584092dbcSRoman Gushchin if (attempts++ > 5) 35684092dbcSRoman Gushchin break; 35784092dbcSRoman Gushchin sleep(1); 35884092dbcSRoman Gushchin } 35984092dbcSRoman Gushchin 36084092dbcSRoman Gushchin if (cg_run(parent[2], alloc_anon, (void *)MB(148))) 36184092dbcSRoman Gushchin goto cleanup; 36284092dbcSRoman Gushchin 36384092dbcSRoman Gushchin if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) 36484092dbcSRoman Gushchin goto cleanup; 36584092dbcSRoman Gushchin 36684092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) 36784092dbcSRoman Gushchin c[i] = cg_read_long(children[i], "memory.current"); 36884092dbcSRoman Gushchin 369f10b6e9aSMichal Koutný if (!values_close(c[0], MB(29), 10)) 37084092dbcSRoman Gushchin goto cleanup; 37184092dbcSRoman Gushchin 372f10b6e9aSMichal Koutný if (!values_close(c[1], MB(21), 10)) 37384092dbcSRoman Gushchin goto cleanup; 37484092dbcSRoman Gushchin 375f0cdaa56SDavid Vernet if (c[3] != 0) 37684092dbcSRoman Gushchin goto cleanup; 37784092dbcSRoman Gushchin 37884092dbcSRoman Gushchin if (!cg_run(parent[2], alloc_anon, (void *)MB(170))) 37984092dbcSRoman Gushchin goto cleanup; 38084092dbcSRoman Gushchin 38184092dbcSRoman Gushchin if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) 38284092dbcSRoman Gushchin goto cleanup; 38384092dbcSRoman Gushchin 38484092dbcSRoman Gushchin ret = KSFT_PASS; 38584092dbcSRoman Gushchin 38684092dbcSRoman Gushchin cleanup: 38784092dbcSRoman Gushchin for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) { 38884092dbcSRoman Gushchin if (!children[i]) 38984092dbcSRoman Gushchin continue; 39084092dbcSRoman Gushchin 39184092dbcSRoman Gushchin cg_destroy(children[i]); 39284092dbcSRoman Gushchin free(children[i]); 39384092dbcSRoman Gushchin } 39484092dbcSRoman Gushchin 39584092dbcSRoman Gushchin for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) { 39684092dbcSRoman Gushchin if (!parent[i]) 39784092dbcSRoman Gushchin continue; 39884092dbcSRoman Gushchin 39984092dbcSRoman Gushchin cg_destroy(parent[i]); 40084092dbcSRoman Gushchin free(parent[i]); 40184092dbcSRoman Gushchin } 40284092dbcSRoman Gushchin close(fd); 40384092dbcSRoman Gushchin return ret; 40484092dbcSRoman Gushchin } 40584092dbcSRoman Gushchin 40684092dbcSRoman Gushchin /* 40784092dbcSRoman Gushchin * First, this test creates the following hierarchy: 408*6a359190SMichal Koutný * A memory.low = 0, memory.max = 200M 409f10b6e9aSMichal Koutný * A/B memory.low = 50M 41084092dbcSRoman Gushchin * A/B/C memory.low = 75M, memory.current = 50M 41184092dbcSRoman Gushchin * A/B/D memory.low = 25M, memory.current = 50M 412f0cdaa56SDavid Vernet * A/B/E memory.low = 0, memory.current = 50M 413f0cdaa56SDavid Vernet * A/B/F memory.low = 500M, memory.current = 0 41484092dbcSRoman Gushchin * 41584092dbcSRoman Gushchin * Usages are pagecache. 41684092dbcSRoman Gushchin * Then it creates A/G an creates a significant 41784092dbcSRoman Gushchin * memory pressure in it. 41884092dbcSRoman Gushchin * 41984092dbcSRoman Gushchin * Then it checks actual memory usages and expects that: 42084092dbcSRoman Gushchin * A/B memory.current ~= 50M 421f10b6e9aSMichal Koutný * A/B/C memory.current ~= 29M 422f10b6e9aSMichal Koutný * A/B/D memory.current ~= 21M 423f10b6e9aSMichal Koutný * A/B/E memory.current ~= 0 424f10b6e9aSMichal Koutný * A/B/F memory.current = 0 425f10b6e9aSMichal Koutný * (for origin of the numbers, see model in memcg_protection.m.) 42684092dbcSRoman Gushchin * 42784092dbcSRoman Gushchin * After that it tries to allocate more than there is 42884092dbcSRoman Gushchin * unprotected memory in A available, 42984092dbcSRoman Gushchin * and checks low and oom events in memory.events. 43084092dbcSRoman Gushchin */ 43184092dbcSRoman Gushchin static int test_memcg_low(const char *root) 43284092dbcSRoman Gushchin { 43384092dbcSRoman Gushchin int ret = KSFT_FAIL; 43484092dbcSRoman Gushchin char *parent[3] = {NULL}; 43584092dbcSRoman Gushchin char *children[4] = {NULL}; 43684092dbcSRoman Gushchin long low, oom; 43784092dbcSRoman Gushchin long c[4]; 43884092dbcSRoman Gushchin int i; 43984092dbcSRoman Gushchin int fd; 44084092dbcSRoman Gushchin 44184092dbcSRoman Gushchin fd = get_temp_fd(); 44284092dbcSRoman Gushchin if (fd < 0) 44384092dbcSRoman Gushchin goto cleanup; 44484092dbcSRoman Gushchin 44584092dbcSRoman Gushchin parent[0] = cg_name(root, "memcg_test_0"); 44684092dbcSRoman Gushchin if (!parent[0]) 44784092dbcSRoman Gushchin goto cleanup; 44884092dbcSRoman Gushchin 44984092dbcSRoman Gushchin parent[1] = cg_name(parent[0], "memcg_test_1"); 45084092dbcSRoman Gushchin if (!parent[1]) 45184092dbcSRoman Gushchin goto cleanup; 45284092dbcSRoman Gushchin 45384092dbcSRoman Gushchin parent[2] = cg_name(parent[0], "memcg_test_2"); 45484092dbcSRoman Gushchin if (!parent[2]) 45584092dbcSRoman Gushchin goto cleanup; 45684092dbcSRoman Gushchin 45784092dbcSRoman Gushchin if (cg_create(parent[0])) 45884092dbcSRoman Gushchin goto cleanup; 45984092dbcSRoman Gushchin 46084092dbcSRoman Gushchin if (cg_read_long(parent[0], "memory.low")) 46184092dbcSRoman Gushchin goto cleanup; 46284092dbcSRoman Gushchin 46384092dbcSRoman Gushchin if (cg_write(parent[0], "cgroup.subtree_control", "+memory")) 46484092dbcSRoman Gushchin goto cleanup; 46584092dbcSRoman Gushchin 46684092dbcSRoman Gushchin if (cg_write(parent[0], "memory.max", "200M")) 46784092dbcSRoman Gushchin goto cleanup; 46884092dbcSRoman Gushchin 46984092dbcSRoman Gushchin if (cg_write(parent[0], "memory.swap.max", "0")) 47084092dbcSRoman Gushchin goto cleanup; 47184092dbcSRoman Gushchin 47284092dbcSRoman Gushchin if (cg_create(parent[1])) 47384092dbcSRoman Gushchin goto cleanup; 47484092dbcSRoman Gushchin 47584092dbcSRoman Gushchin if (cg_write(parent[1], "cgroup.subtree_control", "+memory")) 47684092dbcSRoman Gushchin goto cleanup; 47784092dbcSRoman Gushchin 47884092dbcSRoman Gushchin if (cg_create(parent[2])) 47984092dbcSRoman Gushchin goto cleanup; 48084092dbcSRoman Gushchin 48184092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) { 48284092dbcSRoman Gushchin children[i] = cg_name_indexed(parent[1], "child_memcg", i); 48384092dbcSRoman Gushchin if (!children[i]) 48484092dbcSRoman Gushchin goto cleanup; 48584092dbcSRoman Gushchin 48684092dbcSRoman Gushchin if (cg_create(children[i])) 48784092dbcSRoman Gushchin goto cleanup; 48884092dbcSRoman Gushchin 489f0cdaa56SDavid Vernet if (i > 2) 49084092dbcSRoman Gushchin continue; 49184092dbcSRoman Gushchin 49284092dbcSRoman Gushchin if (cg_run(children[i], alloc_pagecache_50M, (void *)(long)fd)) 49384092dbcSRoman Gushchin goto cleanup; 49484092dbcSRoman Gushchin } 49584092dbcSRoman Gushchin 49684092dbcSRoman Gushchin if (cg_write(parent[1], "memory.low", "50M")) 49784092dbcSRoman Gushchin goto cleanup; 49884092dbcSRoman Gushchin if (cg_write(children[0], "memory.low", "75M")) 49984092dbcSRoman Gushchin goto cleanup; 50084092dbcSRoman Gushchin if (cg_write(children[1], "memory.low", "25M")) 50184092dbcSRoman Gushchin goto cleanup; 502f0cdaa56SDavid Vernet if (cg_write(children[2], "memory.low", "0")) 50384092dbcSRoman Gushchin goto cleanup; 504f0cdaa56SDavid Vernet if (cg_write(children[3], "memory.low", "500M")) 50584092dbcSRoman Gushchin goto cleanup; 50684092dbcSRoman Gushchin 50784092dbcSRoman Gushchin if (cg_run(parent[2], alloc_anon, (void *)MB(148))) 50884092dbcSRoman Gushchin goto cleanup; 50984092dbcSRoman Gushchin 51084092dbcSRoman Gushchin if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) 51184092dbcSRoman Gushchin goto cleanup; 51284092dbcSRoman Gushchin 51384092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) 51484092dbcSRoman Gushchin c[i] = cg_read_long(children[i], "memory.current"); 51584092dbcSRoman Gushchin 516f10b6e9aSMichal Koutný if (!values_close(c[0], MB(29), 10)) 51784092dbcSRoman Gushchin goto cleanup; 51884092dbcSRoman Gushchin 519f10b6e9aSMichal Koutný if (!values_close(c[1], MB(21), 10)) 52084092dbcSRoman Gushchin goto cleanup; 52184092dbcSRoman Gushchin 522f0cdaa56SDavid Vernet if (c[3] != 0) 52384092dbcSRoman Gushchin goto cleanup; 52484092dbcSRoman Gushchin 52584092dbcSRoman Gushchin if (cg_run(parent[2], alloc_anon, (void *)MB(166))) { 52684092dbcSRoman Gushchin fprintf(stderr, 52784092dbcSRoman Gushchin "memory.low prevents from allocating anon memory\n"); 52884092dbcSRoman Gushchin goto cleanup; 52984092dbcSRoman Gushchin } 53084092dbcSRoman Gushchin 53184092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) { 5321d09069fSMichal Koutný int no_low_events_index = 1; 533cdc69458SDavid Vernet 53484092dbcSRoman Gushchin oom = cg_read_key_long(children[i], "memory.events", "oom "); 53584092dbcSRoman Gushchin low = cg_read_key_long(children[i], "memory.events", "low "); 53684092dbcSRoman Gushchin 53784092dbcSRoman Gushchin if (oom) 53884092dbcSRoman Gushchin goto cleanup; 539cdc69458SDavid Vernet if (i <= no_low_events_index && low <= 0) 54084092dbcSRoman Gushchin goto cleanup; 541cdc69458SDavid Vernet if (i > no_low_events_index && low) 54284092dbcSRoman Gushchin goto cleanup; 543cdc69458SDavid Vernet 54484092dbcSRoman Gushchin } 54584092dbcSRoman Gushchin 54684092dbcSRoman Gushchin ret = KSFT_PASS; 54784092dbcSRoman Gushchin 54884092dbcSRoman Gushchin cleanup: 54984092dbcSRoman Gushchin for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) { 55084092dbcSRoman Gushchin if (!children[i]) 55184092dbcSRoman Gushchin continue; 55284092dbcSRoman Gushchin 55384092dbcSRoman Gushchin cg_destroy(children[i]); 55484092dbcSRoman Gushchin free(children[i]); 55584092dbcSRoman Gushchin } 55684092dbcSRoman Gushchin 55784092dbcSRoman Gushchin for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) { 55884092dbcSRoman Gushchin if (!parent[i]) 55984092dbcSRoman Gushchin continue; 56084092dbcSRoman Gushchin 56184092dbcSRoman Gushchin cg_destroy(parent[i]); 56284092dbcSRoman Gushchin free(parent[i]); 56384092dbcSRoman Gushchin } 56484092dbcSRoman Gushchin close(fd); 56584092dbcSRoman Gushchin return ret; 56684092dbcSRoman Gushchin } 56784092dbcSRoman Gushchin 56884092dbcSRoman Gushchin static int alloc_pagecache_max_30M(const char *cgroup, void *arg) 56984092dbcSRoman Gushchin { 57084092dbcSRoman Gushchin size_t size = MB(50); 57184092dbcSRoman Gushchin int ret = -1; 572c1a31a2fSDavid Vernet long current, high, max; 57384092dbcSRoman Gushchin int fd; 57484092dbcSRoman Gushchin 575c1a31a2fSDavid Vernet high = cg_read_long(cgroup, "memory.high"); 576c1a31a2fSDavid Vernet max = cg_read_long(cgroup, "memory.max"); 577c1a31a2fSDavid Vernet if (high != MB(30) && max != MB(30)) 5787fb63787SChristophe JAILLET return -1; 579c1a31a2fSDavid Vernet 58084092dbcSRoman Gushchin fd = get_temp_fd(); 58184092dbcSRoman Gushchin if (fd < 0) 58284092dbcSRoman Gushchin return -1; 58384092dbcSRoman Gushchin 58484092dbcSRoman Gushchin if (alloc_pagecache(fd, size)) 58584092dbcSRoman Gushchin goto cleanup; 58684092dbcSRoman Gushchin 58784092dbcSRoman Gushchin current = cg_read_long(cgroup, "memory.current"); 588c1a31a2fSDavid Vernet if (!values_close(current, MB(30), 5)) 58984092dbcSRoman Gushchin goto cleanup; 59084092dbcSRoman Gushchin 59184092dbcSRoman Gushchin ret = 0; 59284092dbcSRoman Gushchin 59384092dbcSRoman Gushchin cleanup: 59484092dbcSRoman Gushchin close(fd); 59584092dbcSRoman Gushchin return ret; 59684092dbcSRoman Gushchin 59784092dbcSRoman Gushchin } 59884092dbcSRoman Gushchin 59984092dbcSRoman Gushchin /* 60084092dbcSRoman Gushchin * This test checks that memory.high limits the amount of 60184092dbcSRoman Gushchin * memory which can be consumed by either anonymous memory 60284092dbcSRoman Gushchin * or pagecache. 60384092dbcSRoman Gushchin */ 60484092dbcSRoman Gushchin static int test_memcg_high(const char *root) 60584092dbcSRoman Gushchin { 60684092dbcSRoman Gushchin int ret = KSFT_FAIL; 60784092dbcSRoman Gushchin char *memcg; 60884092dbcSRoman Gushchin long high; 60984092dbcSRoman Gushchin 61084092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 61184092dbcSRoman Gushchin if (!memcg) 61284092dbcSRoman Gushchin goto cleanup; 61384092dbcSRoman Gushchin 61484092dbcSRoman Gushchin if (cg_create(memcg)) 61584092dbcSRoman Gushchin goto cleanup; 61684092dbcSRoman Gushchin 61784092dbcSRoman Gushchin if (cg_read_strcmp(memcg, "memory.high", "max\n")) 61884092dbcSRoman Gushchin goto cleanup; 61984092dbcSRoman Gushchin 62084092dbcSRoman Gushchin if (cg_write(memcg, "memory.swap.max", "0")) 62184092dbcSRoman Gushchin goto cleanup; 62284092dbcSRoman Gushchin 62384092dbcSRoman Gushchin if (cg_write(memcg, "memory.high", "30M")) 62484092dbcSRoman Gushchin goto cleanup; 62584092dbcSRoman Gushchin 626be74553fSRoman Gushchin if (cg_run(memcg, alloc_anon, (void *)MB(31))) 62784092dbcSRoman Gushchin goto cleanup; 62884092dbcSRoman Gushchin 62984092dbcSRoman Gushchin if (!cg_run(memcg, alloc_pagecache_50M_check, NULL)) 63084092dbcSRoman Gushchin goto cleanup; 63184092dbcSRoman Gushchin 63284092dbcSRoman Gushchin if (cg_run(memcg, alloc_pagecache_max_30M, NULL)) 63384092dbcSRoman Gushchin goto cleanup; 63484092dbcSRoman Gushchin 63584092dbcSRoman Gushchin high = cg_read_key_long(memcg, "memory.events", "high "); 63684092dbcSRoman Gushchin if (high <= 0) 63784092dbcSRoman Gushchin goto cleanup; 63884092dbcSRoman Gushchin 63984092dbcSRoman Gushchin ret = KSFT_PASS; 64084092dbcSRoman Gushchin 64184092dbcSRoman Gushchin cleanup: 64284092dbcSRoman Gushchin cg_destroy(memcg); 64384092dbcSRoman Gushchin free(memcg); 64484092dbcSRoman Gushchin 64584092dbcSRoman Gushchin return ret; 64684092dbcSRoman Gushchin } 64784092dbcSRoman Gushchin 6486323ec54SShakeel Butt static int alloc_anon_mlock(const char *cgroup, void *arg) 6496323ec54SShakeel Butt { 6506323ec54SShakeel Butt size_t size = (size_t)arg; 6516323ec54SShakeel Butt void *buf; 6526323ec54SShakeel Butt 6536323ec54SShakeel Butt buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 6546323ec54SShakeel Butt 0, 0); 6556323ec54SShakeel Butt if (buf == MAP_FAILED) 6566323ec54SShakeel Butt return -1; 6576323ec54SShakeel Butt 6586323ec54SShakeel Butt mlock(buf, size); 6596323ec54SShakeel Butt munmap(buf, size); 6606323ec54SShakeel Butt return 0; 6616323ec54SShakeel Butt } 6626323ec54SShakeel Butt 6636323ec54SShakeel Butt /* 6646323ec54SShakeel Butt * This test checks that memory.high is able to throttle big single shot 6656323ec54SShakeel Butt * allocation i.e. large allocation within one kernel entry. 6666323ec54SShakeel Butt */ 6676323ec54SShakeel Butt static int test_memcg_high_sync(const char *root) 6686323ec54SShakeel Butt { 6696323ec54SShakeel Butt int ret = KSFT_FAIL, pid, fd = -1; 6706323ec54SShakeel Butt char *memcg; 6716323ec54SShakeel Butt long pre_high, pre_max; 6726323ec54SShakeel Butt long post_high, post_max; 6736323ec54SShakeel Butt 6746323ec54SShakeel Butt memcg = cg_name(root, "memcg_test"); 6756323ec54SShakeel Butt if (!memcg) 6766323ec54SShakeel Butt goto cleanup; 6776323ec54SShakeel Butt 6786323ec54SShakeel Butt if (cg_create(memcg)) 6796323ec54SShakeel Butt goto cleanup; 6806323ec54SShakeel Butt 6816323ec54SShakeel Butt pre_high = cg_read_key_long(memcg, "memory.events", "high "); 6826323ec54SShakeel Butt pre_max = cg_read_key_long(memcg, "memory.events", "max "); 6836323ec54SShakeel Butt if (pre_high < 0 || pre_max < 0) 6846323ec54SShakeel Butt goto cleanup; 6856323ec54SShakeel Butt 6866323ec54SShakeel Butt if (cg_write(memcg, "memory.swap.max", "0")) 6876323ec54SShakeel Butt goto cleanup; 6886323ec54SShakeel Butt 6896323ec54SShakeel Butt if (cg_write(memcg, "memory.high", "30M")) 6906323ec54SShakeel Butt goto cleanup; 6916323ec54SShakeel Butt 6926323ec54SShakeel Butt if (cg_write(memcg, "memory.max", "140M")) 6936323ec54SShakeel Butt goto cleanup; 6946323ec54SShakeel Butt 6956323ec54SShakeel Butt fd = memcg_prepare_for_wait(memcg); 6966323ec54SShakeel Butt if (fd < 0) 6976323ec54SShakeel Butt goto cleanup; 6986323ec54SShakeel Butt 6996323ec54SShakeel Butt pid = cg_run_nowait(memcg, alloc_anon_mlock, (void *)MB(200)); 7006323ec54SShakeel Butt if (pid < 0) 7016323ec54SShakeel Butt goto cleanup; 7026323ec54SShakeel Butt 7036323ec54SShakeel Butt cg_wait_for(fd); 7046323ec54SShakeel Butt 7056323ec54SShakeel Butt post_high = cg_read_key_long(memcg, "memory.events", "high "); 7066323ec54SShakeel Butt post_max = cg_read_key_long(memcg, "memory.events", "max "); 7076323ec54SShakeel Butt if (post_high < 0 || post_max < 0) 7086323ec54SShakeel Butt goto cleanup; 7096323ec54SShakeel Butt 7106323ec54SShakeel Butt if (pre_high == post_high || pre_max != post_max) 7116323ec54SShakeel Butt goto cleanup; 7126323ec54SShakeel Butt 7136323ec54SShakeel Butt ret = KSFT_PASS; 7146323ec54SShakeel Butt 7156323ec54SShakeel Butt cleanup: 7166323ec54SShakeel Butt if (fd >= 0) 7176323ec54SShakeel Butt close(fd); 7186323ec54SShakeel Butt cg_destroy(memcg); 7196323ec54SShakeel Butt free(memcg); 7206323ec54SShakeel Butt 7216323ec54SShakeel Butt return ret; 7226323ec54SShakeel Butt } 7236323ec54SShakeel Butt 72484092dbcSRoman Gushchin /* 72584092dbcSRoman Gushchin * This test checks that memory.max limits the amount of 72684092dbcSRoman Gushchin * memory which can be consumed by either anonymous memory 72784092dbcSRoman Gushchin * or pagecache. 72884092dbcSRoman Gushchin */ 72984092dbcSRoman Gushchin static int test_memcg_max(const char *root) 73084092dbcSRoman Gushchin { 73184092dbcSRoman Gushchin int ret = KSFT_FAIL; 73284092dbcSRoman Gushchin char *memcg; 73384092dbcSRoman Gushchin long current, max; 73484092dbcSRoman Gushchin 73584092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 73684092dbcSRoman Gushchin if (!memcg) 73784092dbcSRoman Gushchin goto cleanup; 73884092dbcSRoman Gushchin 73984092dbcSRoman Gushchin if (cg_create(memcg)) 74084092dbcSRoman Gushchin goto cleanup; 74184092dbcSRoman Gushchin 74284092dbcSRoman Gushchin if (cg_read_strcmp(memcg, "memory.max", "max\n")) 74384092dbcSRoman Gushchin goto cleanup; 74484092dbcSRoman Gushchin 74584092dbcSRoman Gushchin if (cg_write(memcg, "memory.swap.max", "0")) 74684092dbcSRoman Gushchin goto cleanup; 74784092dbcSRoman Gushchin 74884092dbcSRoman Gushchin if (cg_write(memcg, "memory.max", "30M")) 74984092dbcSRoman Gushchin goto cleanup; 75084092dbcSRoman Gushchin 75184092dbcSRoman Gushchin /* Should be killed by OOM killer */ 75284092dbcSRoman Gushchin if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 75384092dbcSRoman Gushchin goto cleanup; 75484092dbcSRoman Gushchin 75584092dbcSRoman Gushchin if (cg_run(memcg, alloc_pagecache_max_30M, NULL)) 75684092dbcSRoman Gushchin goto cleanup; 75784092dbcSRoman Gushchin 75884092dbcSRoman Gushchin current = cg_read_long(memcg, "memory.current"); 75984092dbcSRoman Gushchin if (current > MB(30) || !current) 76084092dbcSRoman Gushchin goto cleanup; 76184092dbcSRoman Gushchin 76284092dbcSRoman Gushchin max = cg_read_key_long(memcg, "memory.events", "max "); 76384092dbcSRoman Gushchin if (max <= 0) 76484092dbcSRoman Gushchin goto cleanup; 76584092dbcSRoman Gushchin 76684092dbcSRoman Gushchin ret = KSFT_PASS; 76784092dbcSRoman Gushchin 76884092dbcSRoman Gushchin cleanup: 76984092dbcSRoman Gushchin cg_destroy(memcg); 77084092dbcSRoman Gushchin free(memcg); 77184092dbcSRoman Gushchin 77284092dbcSRoman Gushchin return ret; 77384092dbcSRoman Gushchin } 77484092dbcSRoman Gushchin 775eae3cb2eSYosry Ahmed /* 776eae3cb2eSYosry Ahmed * This test checks that memory.reclaim reclaims the given 777eae3cb2eSYosry Ahmed * amount of memory (from both anon and file, if possible). 778eae3cb2eSYosry Ahmed */ 779eae3cb2eSYosry Ahmed static int test_memcg_reclaim(const char *root) 780eae3cb2eSYosry Ahmed { 781eae3cb2eSYosry Ahmed int ret = KSFT_FAIL, fd, retries; 782eae3cb2eSYosry Ahmed char *memcg; 783eae3cb2eSYosry Ahmed long current, expected_usage, to_reclaim; 784eae3cb2eSYosry Ahmed char buf[64]; 785eae3cb2eSYosry Ahmed 786eae3cb2eSYosry Ahmed memcg = cg_name(root, "memcg_test"); 787eae3cb2eSYosry Ahmed if (!memcg) 788eae3cb2eSYosry Ahmed goto cleanup; 789eae3cb2eSYosry Ahmed 790eae3cb2eSYosry Ahmed if (cg_create(memcg)) 791eae3cb2eSYosry Ahmed goto cleanup; 792eae3cb2eSYosry Ahmed 793eae3cb2eSYosry Ahmed current = cg_read_long(memcg, "memory.current"); 794eae3cb2eSYosry Ahmed if (current != 0) 795eae3cb2eSYosry Ahmed goto cleanup; 796eae3cb2eSYosry Ahmed 797eae3cb2eSYosry Ahmed fd = get_temp_fd(); 798eae3cb2eSYosry Ahmed if (fd < 0) 799eae3cb2eSYosry Ahmed goto cleanup; 800eae3cb2eSYosry Ahmed 801eae3cb2eSYosry Ahmed cg_run_nowait(memcg, alloc_pagecache_50M_noexit, (void *)(long)fd); 802eae3cb2eSYosry Ahmed 803eae3cb2eSYosry Ahmed /* 804eae3cb2eSYosry Ahmed * If swap is enabled, try to reclaim from both anon and file, else try 805eae3cb2eSYosry Ahmed * to reclaim from file only. 806eae3cb2eSYosry Ahmed */ 807eae3cb2eSYosry Ahmed if (is_swap_enabled()) { 808eae3cb2eSYosry Ahmed cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(50)); 809eae3cb2eSYosry Ahmed expected_usage = MB(100); 810eae3cb2eSYosry Ahmed } else 811eae3cb2eSYosry Ahmed expected_usage = MB(50); 812eae3cb2eSYosry Ahmed 813eae3cb2eSYosry Ahmed /* 814eae3cb2eSYosry Ahmed * Wait until current usage reaches the expected usage (or we run out of 815eae3cb2eSYosry Ahmed * retries). 816eae3cb2eSYosry Ahmed */ 817eae3cb2eSYosry Ahmed retries = 5; 818eae3cb2eSYosry Ahmed while (!values_close(cg_read_long(memcg, "memory.current"), 819eae3cb2eSYosry Ahmed expected_usage, 10)) { 820eae3cb2eSYosry Ahmed if (retries--) { 821eae3cb2eSYosry Ahmed sleep(1); 822eae3cb2eSYosry Ahmed continue; 823eae3cb2eSYosry Ahmed } else { 824eae3cb2eSYosry Ahmed fprintf(stderr, 825eae3cb2eSYosry Ahmed "failed to allocate %ld for memcg reclaim test\n", 826eae3cb2eSYosry Ahmed expected_usage); 827eae3cb2eSYosry Ahmed goto cleanup; 828eae3cb2eSYosry Ahmed } 829eae3cb2eSYosry Ahmed } 830eae3cb2eSYosry Ahmed 831eae3cb2eSYosry Ahmed /* 832eae3cb2eSYosry Ahmed * Reclaim until current reaches 30M, this makes sure we hit both anon 833eae3cb2eSYosry Ahmed * and file if swap is enabled. 834eae3cb2eSYosry Ahmed */ 835eae3cb2eSYosry Ahmed retries = 5; 836eae3cb2eSYosry Ahmed while (true) { 837eae3cb2eSYosry Ahmed int err; 838eae3cb2eSYosry Ahmed 839eae3cb2eSYosry Ahmed current = cg_read_long(memcg, "memory.current"); 840eae3cb2eSYosry Ahmed to_reclaim = current - MB(30); 841eae3cb2eSYosry Ahmed 842eae3cb2eSYosry Ahmed /* 843eae3cb2eSYosry Ahmed * We only keep looping if we get EAGAIN, which means we could 844eae3cb2eSYosry Ahmed * not reclaim the full amount. 845eae3cb2eSYosry Ahmed */ 846eae3cb2eSYosry Ahmed if (to_reclaim <= 0) 847eae3cb2eSYosry Ahmed goto cleanup; 848eae3cb2eSYosry Ahmed 849eae3cb2eSYosry Ahmed 850eae3cb2eSYosry Ahmed snprintf(buf, sizeof(buf), "%ld", to_reclaim); 851eae3cb2eSYosry Ahmed err = cg_write(memcg, "memory.reclaim", buf); 852eae3cb2eSYosry Ahmed if (!err) { 853eae3cb2eSYosry Ahmed /* 854eae3cb2eSYosry Ahmed * If writing succeeds, then the written amount should have been 855eae3cb2eSYosry Ahmed * fully reclaimed (and maybe more). 856eae3cb2eSYosry Ahmed */ 857eae3cb2eSYosry Ahmed current = cg_read_long(memcg, "memory.current"); 858eae3cb2eSYosry Ahmed if (!values_close(current, MB(30), 3) && current > MB(30)) 859eae3cb2eSYosry Ahmed goto cleanup; 860eae3cb2eSYosry Ahmed break; 861eae3cb2eSYosry Ahmed } 862eae3cb2eSYosry Ahmed 863eae3cb2eSYosry Ahmed /* The kernel could not reclaim the full amount, try again. */ 864eae3cb2eSYosry Ahmed if (err == -EAGAIN && retries--) 865eae3cb2eSYosry Ahmed continue; 866eae3cb2eSYosry Ahmed 867eae3cb2eSYosry Ahmed /* We got an unexpected error or ran out of retries. */ 868eae3cb2eSYosry Ahmed goto cleanup; 869eae3cb2eSYosry Ahmed } 870eae3cb2eSYosry Ahmed 871eae3cb2eSYosry Ahmed ret = KSFT_PASS; 872eae3cb2eSYosry Ahmed cleanup: 873eae3cb2eSYosry Ahmed cg_destroy(memcg); 874eae3cb2eSYosry Ahmed free(memcg); 875eae3cb2eSYosry Ahmed close(fd); 876eae3cb2eSYosry Ahmed 877eae3cb2eSYosry Ahmed return ret; 878eae3cb2eSYosry Ahmed } 879eae3cb2eSYosry Ahmed 880478b2784SMike Rapoport static int alloc_anon_50M_check_swap(const char *cgroup, void *arg) 881478b2784SMike Rapoport { 882478b2784SMike Rapoport long mem_max = (long)arg; 883478b2784SMike Rapoport size_t size = MB(50); 884478b2784SMike Rapoport char *buf, *ptr; 885478b2784SMike Rapoport long mem_current, swap_current; 886478b2784SMike Rapoport int ret = -1; 887478b2784SMike Rapoport 888478b2784SMike Rapoport buf = malloc(size); 889478b2784SMike Rapoport for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 890478b2784SMike Rapoport *ptr = 0; 891478b2784SMike Rapoport 892478b2784SMike Rapoport mem_current = cg_read_long(cgroup, "memory.current"); 893478b2784SMike Rapoport if (!mem_current || !values_close(mem_current, mem_max, 3)) 894478b2784SMike Rapoport goto cleanup; 895478b2784SMike Rapoport 896478b2784SMike Rapoport swap_current = cg_read_long(cgroup, "memory.swap.current"); 897478b2784SMike Rapoport if (!swap_current || 898478b2784SMike Rapoport !values_close(mem_current + swap_current, size, 3)) 899478b2784SMike Rapoport goto cleanup; 900478b2784SMike Rapoport 901478b2784SMike Rapoport ret = 0; 902478b2784SMike Rapoport cleanup: 903478b2784SMike Rapoport free(buf); 904478b2784SMike Rapoport return ret; 905478b2784SMike Rapoport } 906478b2784SMike Rapoport 907478b2784SMike Rapoport /* 908478b2784SMike Rapoport * This test checks that memory.swap.max limits the amount of 909478b2784SMike Rapoport * anonymous memory which can be swapped out. 910478b2784SMike Rapoport */ 911478b2784SMike Rapoport static int test_memcg_swap_max(const char *root) 912478b2784SMike Rapoport { 913478b2784SMike Rapoport int ret = KSFT_FAIL; 914478b2784SMike Rapoport char *memcg; 915478b2784SMike Rapoport long max; 916478b2784SMike Rapoport 917478b2784SMike Rapoport if (!is_swap_enabled()) 918478b2784SMike Rapoport return KSFT_SKIP; 919478b2784SMike Rapoport 920478b2784SMike Rapoport memcg = cg_name(root, "memcg_test"); 921478b2784SMike Rapoport if (!memcg) 922478b2784SMike Rapoport goto cleanup; 923478b2784SMike Rapoport 924478b2784SMike Rapoport if (cg_create(memcg)) 925478b2784SMike Rapoport goto cleanup; 926478b2784SMike Rapoport 927478b2784SMike Rapoport if (cg_read_long(memcg, "memory.swap.current")) { 928478b2784SMike Rapoport ret = KSFT_SKIP; 929478b2784SMike Rapoport goto cleanup; 930478b2784SMike Rapoport } 931478b2784SMike Rapoport 932478b2784SMike Rapoport if (cg_read_strcmp(memcg, "memory.max", "max\n")) 933478b2784SMike Rapoport goto cleanup; 934478b2784SMike Rapoport 935478b2784SMike Rapoport if (cg_read_strcmp(memcg, "memory.swap.max", "max\n")) 936478b2784SMike Rapoport goto cleanup; 937478b2784SMike Rapoport 938478b2784SMike Rapoport if (cg_write(memcg, "memory.swap.max", "30M")) 939478b2784SMike Rapoport goto cleanup; 940478b2784SMike Rapoport 941478b2784SMike Rapoport if (cg_write(memcg, "memory.max", "30M")) 942478b2784SMike Rapoport goto cleanup; 943478b2784SMike Rapoport 944478b2784SMike Rapoport /* Should be killed by OOM killer */ 945478b2784SMike Rapoport if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 946478b2784SMike Rapoport goto cleanup; 947478b2784SMike Rapoport 948478b2784SMike Rapoport if (cg_read_key_long(memcg, "memory.events", "oom ") != 1) 949478b2784SMike Rapoport goto cleanup; 950478b2784SMike Rapoport 951478b2784SMike Rapoport if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1) 952478b2784SMike Rapoport goto cleanup; 953478b2784SMike Rapoport 954478b2784SMike Rapoport if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30))) 955478b2784SMike Rapoport goto cleanup; 956478b2784SMike Rapoport 957478b2784SMike Rapoport max = cg_read_key_long(memcg, "memory.events", "max "); 958478b2784SMike Rapoport if (max <= 0) 959478b2784SMike Rapoport goto cleanup; 960478b2784SMike Rapoport 961478b2784SMike Rapoport ret = KSFT_PASS; 962478b2784SMike Rapoport 963478b2784SMike Rapoport cleanup: 964478b2784SMike Rapoport cg_destroy(memcg); 965478b2784SMike Rapoport free(memcg); 966478b2784SMike Rapoport 967478b2784SMike Rapoport return ret; 968478b2784SMike Rapoport } 969478b2784SMike Rapoport 97084092dbcSRoman Gushchin /* 97184092dbcSRoman Gushchin * This test disables swapping and tries to allocate anonymous memory 97284092dbcSRoman Gushchin * up to OOM. Then it checks for oom and oom_kill events in 97384092dbcSRoman Gushchin * memory.events. 97484092dbcSRoman Gushchin */ 97584092dbcSRoman Gushchin static int test_memcg_oom_events(const char *root) 97684092dbcSRoman Gushchin { 97784092dbcSRoman Gushchin int ret = KSFT_FAIL; 97884092dbcSRoman Gushchin char *memcg; 97984092dbcSRoman Gushchin 98084092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 98184092dbcSRoman Gushchin if (!memcg) 98284092dbcSRoman Gushchin goto cleanup; 98384092dbcSRoman Gushchin 98484092dbcSRoman Gushchin if (cg_create(memcg)) 98584092dbcSRoman Gushchin goto cleanup; 98684092dbcSRoman Gushchin 98784092dbcSRoman Gushchin if (cg_write(memcg, "memory.max", "30M")) 98884092dbcSRoman Gushchin goto cleanup; 98984092dbcSRoman Gushchin 99084092dbcSRoman Gushchin if (cg_write(memcg, "memory.swap.max", "0")) 99184092dbcSRoman Gushchin goto cleanup; 99284092dbcSRoman Gushchin 99384092dbcSRoman Gushchin if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 99484092dbcSRoman Gushchin goto cleanup; 99584092dbcSRoman Gushchin 99684092dbcSRoman Gushchin if (cg_read_strcmp(memcg, "cgroup.procs", "")) 99784092dbcSRoman Gushchin goto cleanup; 99884092dbcSRoman Gushchin 99984092dbcSRoman Gushchin if (cg_read_key_long(memcg, "memory.events", "oom ") != 1) 100084092dbcSRoman Gushchin goto cleanup; 100184092dbcSRoman Gushchin 100284092dbcSRoman Gushchin if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1) 100384092dbcSRoman Gushchin goto cleanup; 100484092dbcSRoman Gushchin 100584092dbcSRoman Gushchin ret = KSFT_PASS; 100684092dbcSRoman Gushchin 100784092dbcSRoman Gushchin cleanup: 100884092dbcSRoman Gushchin cg_destroy(memcg); 100984092dbcSRoman Gushchin free(memcg); 101084092dbcSRoman Gushchin 101184092dbcSRoman Gushchin return ret; 101284092dbcSRoman Gushchin } 101384092dbcSRoman Gushchin 10145f8f0193SMike Rapoport struct tcp_server_args { 10155f8f0193SMike Rapoport unsigned short port; 10165f8f0193SMike Rapoport int ctl[2]; 10175f8f0193SMike Rapoport }; 10185f8f0193SMike Rapoport 10195f8f0193SMike Rapoport static int tcp_server(const char *cgroup, void *arg) 10205f8f0193SMike Rapoport { 10215f8f0193SMike Rapoport struct tcp_server_args *srv_args = arg; 10225f8f0193SMike Rapoport struct sockaddr_in6 saddr = { 0 }; 10235f8f0193SMike Rapoport socklen_t slen = sizeof(saddr); 10245f8f0193SMike Rapoport int sk, client_sk, ctl_fd, yes = 1, ret = -1; 10255f8f0193SMike Rapoport 10265f8f0193SMike Rapoport close(srv_args->ctl[0]); 10275f8f0193SMike Rapoport ctl_fd = srv_args->ctl[1]; 10285f8f0193SMike Rapoport 10295f8f0193SMike Rapoport saddr.sin6_family = AF_INET6; 10305f8f0193SMike Rapoport saddr.sin6_addr = in6addr_any; 10315f8f0193SMike Rapoport saddr.sin6_port = htons(srv_args->port); 10325f8f0193SMike Rapoport 10335f8f0193SMike Rapoport sk = socket(AF_INET6, SOCK_STREAM, 0); 10345f8f0193SMike Rapoport if (sk < 0) 10355f8f0193SMike Rapoport return ret; 10365f8f0193SMike Rapoport 10375f8f0193SMike Rapoport if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) 10385f8f0193SMike Rapoport goto cleanup; 10395f8f0193SMike Rapoport 10405f8f0193SMike Rapoport if (bind(sk, (struct sockaddr *)&saddr, slen)) { 10415f8f0193SMike Rapoport write(ctl_fd, &errno, sizeof(errno)); 10425f8f0193SMike Rapoport goto cleanup; 10435f8f0193SMike Rapoport } 10445f8f0193SMike Rapoport 10455f8f0193SMike Rapoport if (listen(sk, 1)) 10465f8f0193SMike Rapoport goto cleanup; 10475f8f0193SMike Rapoport 10485f8f0193SMike Rapoport ret = 0; 10495f8f0193SMike Rapoport if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) { 10505f8f0193SMike Rapoport ret = -1; 10515f8f0193SMike Rapoport goto cleanup; 10525f8f0193SMike Rapoport } 10535f8f0193SMike Rapoport 10545f8f0193SMike Rapoport client_sk = accept(sk, NULL, NULL); 10555f8f0193SMike Rapoport if (client_sk < 0) 10565f8f0193SMike Rapoport goto cleanup; 10575f8f0193SMike Rapoport 10585f8f0193SMike Rapoport ret = -1; 10595f8f0193SMike Rapoport for (;;) { 10605f8f0193SMike Rapoport uint8_t buf[0x100000]; 10615f8f0193SMike Rapoport 10625f8f0193SMike Rapoport if (write(client_sk, buf, sizeof(buf)) <= 0) { 10635f8f0193SMike Rapoport if (errno == ECONNRESET) 10645f8f0193SMike Rapoport ret = 0; 10655f8f0193SMike Rapoport break; 10665f8f0193SMike Rapoport } 10675f8f0193SMike Rapoport } 10685f8f0193SMike Rapoport 10695f8f0193SMike Rapoport close(client_sk); 10705f8f0193SMike Rapoport 10715f8f0193SMike Rapoport cleanup: 10725f8f0193SMike Rapoport close(sk); 10735f8f0193SMike Rapoport return ret; 10745f8f0193SMike Rapoport } 10755f8f0193SMike Rapoport 10765f8f0193SMike Rapoport static int tcp_client(const char *cgroup, unsigned short port) 10775f8f0193SMike Rapoport { 10785f8f0193SMike Rapoport const char server[] = "localhost"; 10795f8f0193SMike Rapoport struct addrinfo *ai; 10805f8f0193SMike Rapoport char servport[6]; 10815f8f0193SMike Rapoport int retries = 0x10; /* nice round number */ 10825f8f0193SMike Rapoport int sk, ret; 10835f8f0193SMike Rapoport 10845f8f0193SMike Rapoport snprintf(servport, sizeof(servport), "%hd", port); 10855f8f0193SMike Rapoport ret = getaddrinfo(server, servport, NULL, &ai); 10865f8f0193SMike Rapoport if (ret) 10875f8f0193SMike Rapoport return ret; 10885f8f0193SMike Rapoport 10895f8f0193SMike Rapoport sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 10905f8f0193SMike Rapoport if (sk < 0) 10915f8f0193SMike Rapoport goto free_ainfo; 10925f8f0193SMike Rapoport 10935f8f0193SMike Rapoport ret = connect(sk, ai->ai_addr, ai->ai_addrlen); 10945f8f0193SMike Rapoport if (ret < 0) 10955f8f0193SMike Rapoport goto close_sk; 10965f8f0193SMike Rapoport 10975f8f0193SMike Rapoport ret = KSFT_FAIL; 10985f8f0193SMike Rapoport while (retries--) { 10995f8f0193SMike Rapoport uint8_t buf[0x100000]; 11005f8f0193SMike Rapoport long current, sock; 11015f8f0193SMike Rapoport 11025f8f0193SMike Rapoport if (read(sk, buf, sizeof(buf)) <= 0) 11035f8f0193SMike Rapoport goto close_sk; 11045f8f0193SMike Rapoport 11055f8f0193SMike Rapoport current = cg_read_long(cgroup, "memory.current"); 11065f8f0193SMike Rapoport sock = cg_read_key_long(cgroup, "memory.stat", "sock "); 11075f8f0193SMike Rapoport 11085f8f0193SMike Rapoport if (current < 0 || sock < 0) 11095f8f0193SMike Rapoport goto close_sk; 11105f8f0193SMike Rapoport 11115f8f0193SMike Rapoport if (values_close(current, sock, 10)) { 11125f8f0193SMike Rapoport ret = KSFT_PASS; 11135f8f0193SMike Rapoport break; 11145f8f0193SMike Rapoport } 11155f8f0193SMike Rapoport } 11165f8f0193SMike Rapoport 11175f8f0193SMike Rapoport close_sk: 11185f8f0193SMike Rapoport close(sk); 11195f8f0193SMike Rapoport free_ainfo: 11205f8f0193SMike Rapoport freeaddrinfo(ai); 11215f8f0193SMike Rapoport return ret; 11225f8f0193SMike Rapoport } 11235f8f0193SMike Rapoport 11245f8f0193SMike Rapoport /* 11255f8f0193SMike Rapoport * This test checks socket memory accounting. 11265f8f0193SMike Rapoport * The test forks a TCP server listens on a random port between 1000 11275f8f0193SMike Rapoport * and 61000. Once it gets a client connection, it starts writing to 11285f8f0193SMike Rapoport * its socket. 11295f8f0193SMike Rapoport * The TCP client interleaves reads from the socket with check whether 11305f8f0193SMike Rapoport * memory.current and memory.stat.sock are similar. 11315f8f0193SMike Rapoport */ 11325f8f0193SMike Rapoport static int test_memcg_sock(const char *root) 11335f8f0193SMike Rapoport { 11345f8f0193SMike Rapoport int bind_retries = 5, ret = KSFT_FAIL, pid, err; 11355f8f0193SMike Rapoport unsigned short port; 11365f8f0193SMike Rapoport char *memcg; 11375f8f0193SMike Rapoport 11385f8f0193SMike Rapoport memcg = cg_name(root, "memcg_test"); 11395f8f0193SMike Rapoport if (!memcg) 11405f8f0193SMike Rapoport goto cleanup; 11415f8f0193SMike Rapoport 11425f8f0193SMike Rapoport if (cg_create(memcg)) 11435f8f0193SMike Rapoport goto cleanup; 11445f8f0193SMike Rapoport 11455f8f0193SMike Rapoport while (bind_retries--) { 11465f8f0193SMike Rapoport struct tcp_server_args args; 11475f8f0193SMike Rapoport 11485f8f0193SMike Rapoport if (pipe(args.ctl)) 11495f8f0193SMike Rapoport goto cleanup; 11505f8f0193SMike Rapoport 11515f8f0193SMike Rapoport port = args.port = 1000 + rand() % 60000; 11525f8f0193SMike Rapoport 11535f8f0193SMike Rapoport pid = cg_run_nowait(memcg, tcp_server, &args); 11545f8f0193SMike Rapoport if (pid < 0) 11555f8f0193SMike Rapoport goto cleanup; 11565f8f0193SMike Rapoport 11575f8f0193SMike Rapoport close(args.ctl[1]); 11585f8f0193SMike Rapoport if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err)) 11595f8f0193SMike Rapoport goto cleanup; 11605f8f0193SMike Rapoport close(args.ctl[0]); 11615f8f0193SMike Rapoport 11625f8f0193SMike Rapoport if (!err) 11635f8f0193SMike Rapoport break; 11645f8f0193SMike Rapoport if (err != EADDRINUSE) 11655f8f0193SMike Rapoport goto cleanup; 11665f8f0193SMike Rapoport 11675f8f0193SMike Rapoport waitpid(pid, NULL, 0); 11685f8f0193SMike Rapoport } 11695f8f0193SMike Rapoport 11705f8f0193SMike Rapoport if (err == EADDRINUSE) { 11715f8f0193SMike Rapoport ret = KSFT_SKIP; 11725f8f0193SMike Rapoport goto cleanup; 11735f8f0193SMike Rapoport } 11745f8f0193SMike Rapoport 11755f8f0193SMike Rapoport if (tcp_client(memcg, port) != KSFT_PASS) 11765f8f0193SMike Rapoport goto cleanup; 11775f8f0193SMike Rapoport 11785f8f0193SMike Rapoport waitpid(pid, &err, 0); 11795f8f0193SMike Rapoport if (WEXITSTATUS(err)) 11805f8f0193SMike Rapoport goto cleanup; 11815f8f0193SMike Rapoport 11825f8f0193SMike Rapoport if (cg_read_long(memcg, "memory.current") < 0) 11835f8f0193SMike Rapoport goto cleanup; 11845f8f0193SMike Rapoport 11855f8f0193SMike Rapoport if (cg_read_key_long(memcg, "memory.stat", "sock ")) 11865f8f0193SMike Rapoport goto cleanup; 11875f8f0193SMike Rapoport 11885f8f0193SMike Rapoport ret = KSFT_PASS; 11895f8f0193SMike Rapoport 11905f8f0193SMike Rapoport cleanup: 11915f8f0193SMike Rapoport cg_destroy(memcg); 11925f8f0193SMike Rapoport free(memcg); 11935f8f0193SMike Rapoport 11945f8f0193SMike Rapoport return ret; 11955f8f0193SMike Rapoport } 11965f8f0193SMike Rapoport 1197a987785dSJay Kamat /* 1198a987785dSJay Kamat * This test disables swapping and tries to allocate anonymous memory 1199a987785dSJay Kamat * up to OOM with memory.group.oom set. Then it checks that all 1200c85bcc91SRoman Gushchin * processes in the leaf were killed. It also checks that oom_events 1201c85bcc91SRoman Gushchin * were propagated to the parent level. 1202a987785dSJay Kamat */ 1203a987785dSJay Kamat static int test_memcg_oom_group_leaf_events(const char *root) 1204a987785dSJay Kamat { 1205a987785dSJay Kamat int ret = KSFT_FAIL; 1206a987785dSJay Kamat char *parent, *child; 120772b1e03aSDavid Vernet long parent_oom_events; 1208a987785dSJay Kamat 1209a987785dSJay Kamat parent = cg_name(root, "memcg_test_0"); 1210a987785dSJay Kamat child = cg_name(root, "memcg_test_0/memcg_test_1"); 1211a987785dSJay Kamat 1212a987785dSJay Kamat if (!parent || !child) 1213a987785dSJay Kamat goto cleanup; 1214a987785dSJay Kamat 1215a987785dSJay Kamat if (cg_create(parent)) 1216a987785dSJay Kamat goto cleanup; 1217a987785dSJay Kamat 1218a987785dSJay Kamat if (cg_create(child)) 1219a987785dSJay Kamat goto cleanup; 1220a987785dSJay Kamat 1221a987785dSJay Kamat if (cg_write(parent, "cgroup.subtree_control", "+memory")) 1222a987785dSJay Kamat goto cleanup; 1223a987785dSJay Kamat 1224a987785dSJay Kamat if (cg_write(child, "memory.max", "50M")) 1225a987785dSJay Kamat goto cleanup; 1226a987785dSJay Kamat 1227a987785dSJay Kamat if (cg_write(child, "memory.swap.max", "0")) 1228a987785dSJay Kamat goto cleanup; 1229a987785dSJay Kamat 1230a987785dSJay Kamat if (cg_write(child, "memory.oom.group", "1")) 1231a987785dSJay Kamat goto cleanup; 1232a987785dSJay Kamat 1233a987785dSJay Kamat cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); 1234a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1235a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1236a987785dSJay Kamat if (!cg_run(child, alloc_anon, (void *)MB(100))) 1237a987785dSJay Kamat goto cleanup; 1238a987785dSJay Kamat 1239a987785dSJay Kamat if (cg_test_proc_killed(child)) 1240a987785dSJay Kamat goto cleanup; 1241a987785dSJay Kamat 1242a987785dSJay Kamat if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0) 1243a987785dSJay Kamat goto cleanup; 1244a987785dSJay Kamat 1245ff3b72a5SMichal Koutný parent_oom_events = cg_read_key_long( 1246ff3b72a5SMichal Koutný parent, "memory.events", "oom_kill "); 1247ff3b72a5SMichal Koutný /* 1248ff3b72a5SMichal Koutný * If memory_localevents is not enabled (the default), the parent should 1249ff3b72a5SMichal Koutný * count OOM events in its children groups. Otherwise, it should not 1250ff3b72a5SMichal Koutný * have observed any events. 1251ff3b72a5SMichal Koutný */ 1252ff3b72a5SMichal Koutný if (has_localevents && parent_oom_events != 0) 1253ff3b72a5SMichal Koutný goto cleanup; 1254ff3b72a5SMichal Koutný else if (!has_localevents && parent_oom_events <= 0) 1255a987785dSJay Kamat goto cleanup; 1256a987785dSJay Kamat 1257a987785dSJay Kamat ret = KSFT_PASS; 1258a987785dSJay Kamat 1259a987785dSJay Kamat cleanup: 1260a987785dSJay Kamat if (child) 1261a987785dSJay Kamat cg_destroy(child); 1262a987785dSJay Kamat if (parent) 1263a987785dSJay Kamat cg_destroy(parent); 1264a987785dSJay Kamat free(child); 1265a987785dSJay Kamat free(parent); 1266a987785dSJay Kamat 1267a987785dSJay Kamat return ret; 1268a987785dSJay Kamat } 1269a987785dSJay Kamat 1270a987785dSJay Kamat /* 1271a987785dSJay Kamat * This test disables swapping and tries to allocate anonymous memory 1272a987785dSJay Kamat * up to OOM with memory.group.oom set. Then it checks that all 1273a987785dSJay Kamat * processes in the parent and leaf were killed. 1274a987785dSJay Kamat */ 1275a987785dSJay Kamat static int test_memcg_oom_group_parent_events(const char *root) 1276a987785dSJay Kamat { 1277a987785dSJay Kamat int ret = KSFT_FAIL; 1278a987785dSJay Kamat char *parent, *child; 1279a987785dSJay Kamat 1280a987785dSJay Kamat parent = cg_name(root, "memcg_test_0"); 1281a987785dSJay Kamat child = cg_name(root, "memcg_test_0/memcg_test_1"); 1282a987785dSJay Kamat 1283a987785dSJay Kamat if (!parent || !child) 1284a987785dSJay Kamat goto cleanup; 1285a987785dSJay Kamat 1286a987785dSJay Kamat if (cg_create(parent)) 1287a987785dSJay Kamat goto cleanup; 1288a987785dSJay Kamat 1289a987785dSJay Kamat if (cg_create(child)) 1290a987785dSJay Kamat goto cleanup; 1291a987785dSJay Kamat 1292a987785dSJay Kamat if (cg_write(parent, "memory.max", "80M")) 1293a987785dSJay Kamat goto cleanup; 1294a987785dSJay Kamat 1295a987785dSJay Kamat if (cg_write(parent, "memory.swap.max", "0")) 1296a987785dSJay Kamat goto cleanup; 1297a987785dSJay Kamat 1298a987785dSJay Kamat if (cg_write(parent, "memory.oom.group", "1")) 1299a987785dSJay Kamat goto cleanup; 1300a987785dSJay Kamat 1301a987785dSJay Kamat cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); 1302a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1303a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1304a987785dSJay Kamat 1305a987785dSJay Kamat if (!cg_run(child, alloc_anon, (void *)MB(100))) 1306a987785dSJay Kamat goto cleanup; 1307a987785dSJay Kamat 1308a987785dSJay Kamat if (cg_test_proc_killed(child)) 1309a987785dSJay Kamat goto cleanup; 1310a987785dSJay Kamat if (cg_test_proc_killed(parent)) 1311a987785dSJay Kamat goto cleanup; 1312a987785dSJay Kamat 1313a987785dSJay Kamat ret = KSFT_PASS; 1314a987785dSJay Kamat 1315a987785dSJay Kamat cleanup: 1316a987785dSJay Kamat if (child) 1317a987785dSJay Kamat cg_destroy(child); 1318a987785dSJay Kamat if (parent) 1319a987785dSJay Kamat cg_destroy(parent); 1320a987785dSJay Kamat free(child); 1321a987785dSJay Kamat free(parent); 1322a987785dSJay Kamat 1323a987785dSJay Kamat return ret; 1324a987785dSJay Kamat } 1325a987785dSJay Kamat 1326a987785dSJay Kamat /* 1327a987785dSJay Kamat * This test disables swapping and tries to allocate anonymous memory 1328a987785dSJay Kamat * up to OOM with memory.group.oom set. Then it checks that all 1329a987785dSJay Kamat * processes were killed except those set with OOM_SCORE_ADJ_MIN 1330a987785dSJay Kamat */ 1331a987785dSJay Kamat static int test_memcg_oom_group_score_events(const char *root) 1332a987785dSJay Kamat { 1333a987785dSJay Kamat int ret = KSFT_FAIL; 1334a987785dSJay Kamat char *memcg; 1335a987785dSJay Kamat int safe_pid; 1336a987785dSJay Kamat 1337a987785dSJay Kamat memcg = cg_name(root, "memcg_test_0"); 1338a987785dSJay Kamat 1339a987785dSJay Kamat if (!memcg) 1340a987785dSJay Kamat goto cleanup; 1341a987785dSJay Kamat 1342a987785dSJay Kamat if (cg_create(memcg)) 1343a987785dSJay Kamat goto cleanup; 1344a987785dSJay Kamat 1345a987785dSJay Kamat if (cg_write(memcg, "memory.max", "50M")) 1346a987785dSJay Kamat goto cleanup; 1347a987785dSJay Kamat 1348a987785dSJay Kamat if (cg_write(memcg, "memory.swap.max", "0")) 1349a987785dSJay Kamat goto cleanup; 1350a987785dSJay Kamat 1351a987785dSJay Kamat if (cg_write(memcg, "memory.oom.group", "1")) 1352a987785dSJay Kamat goto cleanup; 1353a987785dSJay Kamat 1354a987785dSJay Kamat safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); 1355a987785dSJay Kamat if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN)) 1356a987785dSJay Kamat goto cleanup; 1357a987785dSJay Kamat 1358a987785dSJay Kamat cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); 1359a987785dSJay Kamat if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 1360a987785dSJay Kamat goto cleanup; 1361a987785dSJay Kamat 1362ff3b72a5SMichal Koutný if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3) 1363ff3b72a5SMichal Koutný goto cleanup; 1364a987785dSJay Kamat 1365a987785dSJay Kamat if (kill(safe_pid, SIGKILL)) 1366a987785dSJay Kamat goto cleanup; 1367a987785dSJay Kamat 1368ff3b72a5SMichal Koutný ret = KSFT_PASS; 1369ff3b72a5SMichal Koutný 1370a987785dSJay Kamat cleanup: 1371a987785dSJay Kamat if (memcg) 1372a987785dSJay Kamat cg_destroy(memcg); 1373a987785dSJay Kamat free(memcg); 1374a987785dSJay Kamat 1375a987785dSJay Kamat return ret; 1376a987785dSJay Kamat } 1377a987785dSJay Kamat 137884092dbcSRoman Gushchin #define T(x) { x, #x } 137984092dbcSRoman Gushchin struct memcg_test { 138084092dbcSRoman Gushchin int (*fn)(const char *root); 138184092dbcSRoman Gushchin const char *name; 138284092dbcSRoman Gushchin } tests[] = { 138384092dbcSRoman Gushchin T(test_memcg_subtree_control), 138484092dbcSRoman Gushchin T(test_memcg_current), 138584092dbcSRoman Gushchin T(test_memcg_min), 138684092dbcSRoman Gushchin T(test_memcg_low), 138784092dbcSRoman Gushchin T(test_memcg_high), 13886323ec54SShakeel Butt T(test_memcg_high_sync), 138984092dbcSRoman Gushchin T(test_memcg_max), 1390eae3cb2eSYosry Ahmed T(test_memcg_reclaim), 139184092dbcSRoman Gushchin T(test_memcg_oom_events), 1392478b2784SMike Rapoport T(test_memcg_swap_max), 13935f8f0193SMike Rapoport T(test_memcg_sock), 1394a987785dSJay Kamat T(test_memcg_oom_group_leaf_events), 1395a987785dSJay Kamat T(test_memcg_oom_group_parent_events), 1396a987785dSJay Kamat T(test_memcg_oom_group_score_events), 139784092dbcSRoman Gushchin }; 139884092dbcSRoman Gushchin #undef T 139984092dbcSRoman Gushchin 140084092dbcSRoman Gushchin int main(int argc, char **argv) 140184092dbcSRoman Gushchin { 140284092dbcSRoman Gushchin char root[PATH_MAX]; 1403cdc69458SDavid Vernet int i, proc_status, ret = EXIT_SUCCESS; 140484092dbcSRoman Gushchin 140584092dbcSRoman Gushchin if (cg_find_unified_root(root, sizeof(root))) 140684092dbcSRoman Gushchin ksft_exit_skip("cgroup v2 isn't mounted\n"); 140784092dbcSRoman Gushchin 140884092dbcSRoman Gushchin /* 140984092dbcSRoman Gushchin * Check that memory controller is available: 141084092dbcSRoman Gushchin * memory is listed in cgroup.controllers 141184092dbcSRoman Gushchin */ 141284092dbcSRoman Gushchin if (cg_read_strstr(root, "cgroup.controllers", "memory")) 141384092dbcSRoman Gushchin ksft_exit_skip("memory controller isn't available\n"); 141484092dbcSRoman Gushchin 1415f6131f28SAlex Shi if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 1416f6131f28SAlex Shi if (cg_write(root, "cgroup.subtree_control", "+memory")) 1417f6131f28SAlex Shi ksft_exit_skip("Failed to set memory controller\n"); 1418f6131f28SAlex Shi 1419cdc69458SDavid Vernet proc_status = proc_mount_contains("memory_recursiveprot"); 1420cdc69458SDavid Vernet if (proc_status < 0) 1421cdc69458SDavid Vernet ksft_exit_skip("Failed to query cgroup mount option\n"); 1422cdc69458SDavid Vernet has_recursiveprot = proc_status; 1423cdc69458SDavid Vernet 142472b1e03aSDavid Vernet proc_status = proc_mount_contains("memory_localevents"); 142572b1e03aSDavid Vernet if (proc_status < 0) 142672b1e03aSDavid Vernet ksft_exit_skip("Failed to query cgroup mount option\n"); 142772b1e03aSDavid Vernet has_localevents = proc_status; 142872b1e03aSDavid Vernet 142984092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(tests); i++) { 143084092dbcSRoman Gushchin switch (tests[i].fn(root)) { 143184092dbcSRoman Gushchin case KSFT_PASS: 143284092dbcSRoman Gushchin ksft_test_result_pass("%s\n", tests[i].name); 143384092dbcSRoman Gushchin break; 143484092dbcSRoman Gushchin case KSFT_SKIP: 143584092dbcSRoman Gushchin ksft_test_result_skip("%s\n", tests[i].name); 143684092dbcSRoman Gushchin break; 143784092dbcSRoman Gushchin default: 143884092dbcSRoman Gushchin ret = EXIT_FAILURE; 143984092dbcSRoman Gushchin ksft_test_result_fail("%s\n", tests[i].name); 144084092dbcSRoman Gushchin break; 144184092dbcSRoman Gushchin } 144284092dbcSRoman Gushchin } 144384092dbcSRoman Gushchin 144484092dbcSRoman Gushchin return ret; 144584092dbcSRoman Gushchin } 1446