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> 1984092dbcSRoman Gushchin 2084092dbcSRoman Gushchin #include "../kselftest.h" 2184092dbcSRoman Gushchin #include "cgroup_util.h" 2284092dbcSRoman Gushchin 2384092dbcSRoman Gushchin /* 2484092dbcSRoman Gushchin * This test creates two nested cgroups with and without enabling 2584092dbcSRoman Gushchin * the memory controller. 2684092dbcSRoman Gushchin */ 2784092dbcSRoman Gushchin static int test_memcg_subtree_control(const char *root) 2884092dbcSRoman Gushchin { 29e14d314cSRoman Gushchin char *parent, *child, *parent2 = NULL, *child2 = NULL; 3084092dbcSRoman Gushchin int ret = KSFT_FAIL; 3184092dbcSRoman Gushchin char buf[PAGE_SIZE]; 3284092dbcSRoman Gushchin 3384092dbcSRoman Gushchin /* Create two nested cgroups with the memory controller enabled */ 3484092dbcSRoman Gushchin parent = cg_name(root, "memcg_test_0"); 3584092dbcSRoman Gushchin child = cg_name(root, "memcg_test_0/memcg_test_1"); 3684092dbcSRoman Gushchin if (!parent || !child) 37e14d314cSRoman Gushchin goto cleanup_free; 3884092dbcSRoman Gushchin 3984092dbcSRoman Gushchin if (cg_create(parent)) 40e14d314cSRoman Gushchin goto cleanup_free; 4184092dbcSRoman Gushchin 4284092dbcSRoman Gushchin if (cg_write(parent, "cgroup.subtree_control", "+memory")) 43e14d314cSRoman Gushchin goto cleanup_parent; 4484092dbcSRoman Gushchin 4584092dbcSRoman Gushchin if (cg_create(child)) 46e14d314cSRoman Gushchin goto cleanup_parent; 4784092dbcSRoman Gushchin 4884092dbcSRoman Gushchin if (cg_read_strstr(child, "cgroup.controllers", "memory")) 49e14d314cSRoman Gushchin goto cleanup_child; 5084092dbcSRoman Gushchin 5184092dbcSRoman Gushchin /* Create two nested cgroups without enabling memory controller */ 5284092dbcSRoman Gushchin parent2 = cg_name(root, "memcg_test_1"); 5384092dbcSRoman Gushchin child2 = cg_name(root, "memcg_test_1/memcg_test_1"); 5484092dbcSRoman Gushchin if (!parent2 || !child2) 55e14d314cSRoman Gushchin goto cleanup_free2; 5684092dbcSRoman Gushchin 5784092dbcSRoman Gushchin if (cg_create(parent2)) 58e14d314cSRoman Gushchin goto cleanup_free2; 5984092dbcSRoman Gushchin 6084092dbcSRoman Gushchin if (cg_create(child2)) 61e14d314cSRoman Gushchin goto cleanup_parent2; 6284092dbcSRoman Gushchin 6384092dbcSRoman Gushchin if (cg_read(child2, "cgroup.controllers", buf, sizeof(buf))) 64e14d314cSRoman Gushchin goto cleanup_all; 6584092dbcSRoman Gushchin 6684092dbcSRoman Gushchin if (!cg_read_strstr(child2, "cgroup.controllers", "memory")) 67e14d314cSRoman Gushchin goto cleanup_all; 6884092dbcSRoman Gushchin 6984092dbcSRoman Gushchin ret = KSFT_PASS; 7084092dbcSRoman Gushchin 71e14d314cSRoman Gushchin cleanup_all: 7284092dbcSRoman Gushchin cg_destroy(child2); 73e14d314cSRoman Gushchin cleanup_parent2: 7484092dbcSRoman Gushchin cg_destroy(parent2); 75e14d314cSRoman Gushchin cleanup_free2: 7684092dbcSRoman Gushchin free(parent2); 7784092dbcSRoman Gushchin free(child2); 78e14d314cSRoman Gushchin cleanup_child: 79e14d314cSRoman Gushchin cg_destroy(child); 80e14d314cSRoman Gushchin cleanup_parent: 81e14d314cSRoman Gushchin cg_destroy(parent); 82e14d314cSRoman Gushchin cleanup_free: 83e14d314cSRoman Gushchin free(parent); 84e14d314cSRoman Gushchin free(child); 8584092dbcSRoman Gushchin 8684092dbcSRoman Gushchin return ret; 8784092dbcSRoman Gushchin } 8884092dbcSRoman Gushchin 8984092dbcSRoman Gushchin static int alloc_anon_50M_check(const char *cgroup, void *arg) 9084092dbcSRoman Gushchin { 9184092dbcSRoman Gushchin size_t size = MB(50); 9284092dbcSRoman Gushchin char *buf, *ptr; 9384092dbcSRoman Gushchin long anon, current; 9484092dbcSRoman Gushchin int ret = -1; 9584092dbcSRoman Gushchin 9684092dbcSRoman Gushchin buf = malloc(size); 9784092dbcSRoman Gushchin for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 9884092dbcSRoman Gushchin *ptr = 0; 9984092dbcSRoman Gushchin 10084092dbcSRoman Gushchin current = cg_read_long(cgroup, "memory.current"); 10184092dbcSRoman Gushchin if (current < size) 10284092dbcSRoman Gushchin goto cleanup; 10384092dbcSRoman Gushchin 10484092dbcSRoman Gushchin if (!values_close(size, current, 3)) 10584092dbcSRoman Gushchin goto cleanup; 10684092dbcSRoman Gushchin 10784092dbcSRoman Gushchin anon = cg_read_key_long(cgroup, "memory.stat", "anon "); 10884092dbcSRoman Gushchin if (anon < 0) 10984092dbcSRoman Gushchin goto cleanup; 11084092dbcSRoman Gushchin 11184092dbcSRoman Gushchin if (!values_close(anon, current, 3)) 11284092dbcSRoman Gushchin goto cleanup; 11384092dbcSRoman Gushchin 11484092dbcSRoman Gushchin ret = 0; 11584092dbcSRoman Gushchin cleanup: 11684092dbcSRoman Gushchin free(buf); 11784092dbcSRoman Gushchin return ret; 11884092dbcSRoman Gushchin } 11984092dbcSRoman Gushchin 12084092dbcSRoman Gushchin static int alloc_pagecache_50M_check(const char *cgroup, void *arg) 12184092dbcSRoman Gushchin { 12284092dbcSRoman Gushchin size_t size = MB(50); 12384092dbcSRoman Gushchin int ret = -1; 12484092dbcSRoman Gushchin long current, file; 12584092dbcSRoman Gushchin int fd; 12684092dbcSRoman Gushchin 12784092dbcSRoman Gushchin fd = get_temp_fd(); 12884092dbcSRoman Gushchin if (fd < 0) 12984092dbcSRoman Gushchin return -1; 13084092dbcSRoman Gushchin 13184092dbcSRoman Gushchin if (alloc_pagecache(fd, size)) 13284092dbcSRoman Gushchin goto cleanup; 13384092dbcSRoman Gushchin 13484092dbcSRoman Gushchin current = cg_read_long(cgroup, "memory.current"); 13584092dbcSRoman Gushchin if (current < size) 13684092dbcSRoman Gushchin goto cleanup; 13784092dbcSRoman Gushchin 13884092dbcSRoman Gushchin file = cg_read_key_long(cgroup, "memory.stat", "file "); 13984092dbcSRoman Gushchin if (file < 0) 14084092dbcSRoman Gushchin goto cleanup; 14184092dbcSRoman Gushchin 14284092dbcSRoman Gushchin if (!values_close(file, current, 10)) 14384092dbcSRoman Gushchin goto cleanup; 14484092dbcSRoman Gushchin 14584092dbcSRoman Gushchin ret = 0; 14684092dbcSRoman Gushchin 14784092dbcSRoman Gushchin cleanup: 14884092dbcSRoman Gushchin close(fd); 14984092dbcSRoman Gushchin return ret; 15084092dbcSRoman Gushchin } 15184092dbcSRoman Gushchin 15284092dbcSRoman Gushchin /* 15384092dbcSRoman Gushchin * This test create a memory cgroup, allocates 15484092dbcSRoman Gushchin * some anonymous memory and some pagecache 15584092dbcSRoman Gushchin * and check memory.current and some memory.stat values. 15684092dbcSRoman Gushchin */ 15784092dbcSRoman Gushchin static int test_memcg_current(const char *root) 15884092dbcSRoman Gushchin { 15984092dbcSRoman Gushchin int ret = KSFT_FAIL; 16084092dbcSRoman Gushchin long current; 16184092dbcSRoman Gushchin char *memcg; 16284092dbcSRoman Gushchin 16384092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 16484092dbcSRoman Gushchin if (!memcg) 16584092dbcSRoman Gushchin goto cleanup; 16684092dbcSRoman Gushchin 16784092dbcSRoman Gushchin if (cg_create(memcg)) 16884092dbcSRoman Gushchin goto cleanup; 16984092dbcSRoman Gushchin 17084092dbcSRoman Gushchin current = cg_read_long(memcg, "memory.current"); 17184092dbcSRoman Gushchin if (current != 0) 17284092dbcSRoman Gushchin goto cleanup; 17384092dbcSRoman Gushchin 17484092dbcSRoman Gushchin if (cg_run(memcg, alloc_anon_50M_check, NULL)) 17584092dbcSRoman Gushchin goto cleanup; 17684092dbcSRoman Gushchin 17784092dbcSRoman Gushchin if (cg_run(memcg, alloc_pagecache_50M_check, NULL)) 17884092dbcSRoman Gushchin goto cleanup; 17984092dbcSRoman Gushchin 18084092dbcSRoman Gushchin ret = KSFT_PASS; 18184092dbcSRoman Gushchin 18284092dbcSRoman Gushchin cleanup: 18384092dbcSRoman Gushchin cg_destroy(memcg); 18484092dbcSRoman Gushchin free(memcg); 18584092dbcSRoman Gushchin 18684092dbcSRoman Gushchin return ret; 18784092dbcSRoman Gushchin } 18884092dbcSRoman Gushchin 18984092dbcSRoman Gushchin static int alloc_pagecache_50M(const char *cgroup, void *arg) 19084092dbcSRoman Gushchin { 19184092dbcSRoman Gushchin int fd = (long)arg; 19284092dbcSRoman Gushchin 19384092dbcSRoman Gushchin return alloc_pagecache(fd, MB(50)); 19484092dbcSRoman Gushchin } 19584092dbcSRoman Gushchin 19684092dbcSRoman Gushchin static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg) 19784092dbcSRoman Gushchin { 19884092dbcSRoman Gushchin int fd = (long)arg; 19984092dbcSRoman Gushchin int ppid = getppid(); 20084092dbcSRoman Gushchin 20184092dbcSRoman Gushchin if (alloc_pagecache(fd, MB(50))) 20284092dbcSRoman Gushchin return -1; 20384092dbcSRoman Gushchin 20484092dbcSRoman Gushchin while (getppid() == ppid) 20584092dbcSRoman Gushchin sleep(1); 20684092dbcSRoman Gushchin 20784092dbcSRoman Gushchin return 0; 20884092dbcSRoman Gushchin } 20984092dbcSRoman Gushchin 210a987785dSJay Kamat static int alloc_anon_noexit(const char *cgroup, void *arg) 211a987785dSJay Kamat { 212a987785dSJay Kamat int ppid = getppid(); 213a987785dSJay Kamat 214a987785dSJay Kamat if (alloc_anon(cgroup, arg)) 215a987785dSJay Kamat return -1; 216a987785dSJay Kamat 217a987785dSJay Kamat while (getppid() == ppid) 218a987785dSJay Kamat sleep(1); 219a987785dSJay Kamat 220a987785dSJay Kamat return 0; 221a987785dSJay Kamat } 222a987785dSJay Kamat 223a987785dSJay Kamat /* 224a987785dSJay Kamat * Wait until processes are killed asynchronously by the OOM killer 225a987785dSJay Kamat * If we exceed a timeout, fail. 226a987785dSJay Kamat */ 227a987785dSJay Kamat static int cg_test_proc_killed(const char *cgroup) 228a987785dSJay Kamat { 229a987785dSJay Kamat int limit; 230a987785dSJay Kamat 231a987785dSJay Kamat for (limit = 10; limit > 0; limit--) { 232a987785dSJay Kamat if (cg_read_strcmp(cgroup, "cgroup.procs", "") == 0) 233a987785dSJay Kamat return 0; 234a987785dSJay Kamat 235a987785dSJay Kamat usleep(100000); 236a987785dSJay Kamat } 237a987785dSJay Kamat return -1; 238a987785dSJay Kamat } 239a987785dSJay Kamat 24084092dbcSRoman Gushchin /* 24184092dbcSRoman Gushchin * First, this test creates the following hierarchy: 24284092dbcSRoman Gushchin * A memory.min = 50M, memory.max = 200M 24384092dbcSRoman Gushchin * A/B memory.min = 50M, memory.current = 50M 24484092dbcSRoman Gushchin * A/B/C memory.min = 75M, memory.current = 50M 24584092dbcSRoman Gushchin * A/B/D memory.min = 25M, memory.current = 50M 24684092dbcSRoman Gushchin * A/B/E memory.min = 500M, memory.current = 0 24784092dbcSRoman Gushchin * A/B/F memory.min = 0, memory.current = 50M 24884092dbcSRoman Gushchin * 24984092dbcSRoman Gushchin * Usages are pagecache, but the test keeps a running 25084092dbcSRoman Gushchin * process in every leaf cgroup. 25184092dbcSRoman Gushchin * Then it creates A/G and creates a significant 25284092dbcSRoman Gushchin * memory pressure in it. 25384092dbcSRoman Gushchin * 25484092dbcSRoman Gushchin * A/B memory.current ~= 50M 25584092dbcSRoman Gushchin * A/B/C memory.current ~= 33M 25684092dbcSRoman Gushchin * A/B/D memory.current ~= 17M 25784092dbcSRoman Gushchin * A/B/E memory.current ~= 0 25884092dbcSRoman Gushchin * 25984092dbcSRoman Gushchin * After that it tries to allocate more than there is 26084092dbcSRoman Gushchin * unprotected memory in A available, and checks 26184092dbcSRoman Gushchin * checks that memory.min protects pagecache even 26284092dbcSRoman Gushchin * in this case. 26384092dbcSRoman Gushchin */ 26484092dbcSRoman Gushchin static int test_memcg_min(const char *root) 26584092dbcSRoman Gushchin { 26684092dbcSRoman Gushchin int ret = KSFT_FAIL; 26784092dbcSRoman Gushchin char *parent[3] = {NULL}; 26884092dbcSRoman Gushchin char *children[4] = {NULL}; 26984092dbcSRoman Gushchin long c[4]; 27084092dbcSRoman Gushchin int i, attempts; 27184092dbcSRoman Gushchin int fd; 27284092dbcSRoman Gushchin 27384092dbcSRoman Gushchin fd = get_temp_fd(); 27484092dbcSRoman Gushchin if (fd < 0) 27584092dbcSRoman Gushchin goto cleanup; 27684092dbcSRoman Gushchin 27784092dbcSRoman Gushchin parent[0] = cg_name(root, "memcg_test_0"); 27884092dbcSRoman Gushchin if (!parent[0]) 27984092dbcSRoman Gushchin goto cleanup; 28084092dbcSRoman Gushchin 28184092dbcSRoman Gushchin parent[1] = cg_name(parent[0], "memcg_test_1"); 28284092dbcSRoman Gushchin if (!parent[1]) 28384092dbcSRoman Gushchin goto cleanup; 28484092dbcSRoman Gushchin 28584092dbcSRoman Gushchin parent[2] = cg_name(parent[0], "memcg_test_2"); 28684092dbcSRoman Gushchin if (!parent[2]) 28784092dbcSRoman Gushchin goto cleanup; 28884092dbcSRoman Gushchin 28984092dbcSRoman Gushchin if (cg_create(parent[0])) 29084092dbcSRoman Gushchin goto cleanup; 29184092dbcSRoman Gushchin 29284092dbcSRoman Gushchin if (cg_read_long(parent[0], "memory.min")) { 29384092dbcSRoman Gushchin ret = KSFT_SKIP; 29484092dbcSRoman Gushchin goto cleanup; 29584092dbcSRoman Gushchin } 29684092dbcSRoman Gushchin 29784092dbcSRoman Gushchin if (cg_write(parent[0], "cgroup.subtree_control", "+memory")) 29884092dbcSRoman Gushchin goto cleanup; 29984092dbcSRoman Gushchin 30084092dbcSRoman Gushchin if (cg_write(parent[0], "memory.max", "200M")) 30184092dbcSRoman Gushchin goto cleanup; 30284092dbcSRoman Gushchin 30384092dbcSRoman Gushchin if (cg_write(parent[0], "memory.swap.max", "0")) 30484092dbcSRoman Gushchin goto cleanup; 30584092dbcSRoman Gushchin 30684092dbcSRoman Gushchin if (cg_create(parent[1])) 30784092dbcSRoman Gushchin goto cleanup; 30884092dbcSRoman Gushchin 30984092dbcSRoman Gushchin if (cg_write(parent[1], "cgroup.subtree_control", "+memory")) 31084092dbcSRoman Gushchin goto cleanup; 31184092dbcSRoman Gushchin 31284092dbcSRoman Gushchin if (cg_create(parent[2])) 31384092dbcSRoman Gushchin goto cleanup; 31484092dbcSRoman Gushchin 31584092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) { 31684092dbcSRoman Gushchin children[i] = cg_name_indexed(parent[1], "child_memcg", i); 31784092dbcSRoman Gushchin if (!children[i]) 31884092dbcSRoman Gushchin goto cleanup; 31984092dbcSRoman Gushchin 32084092dbcSRoman Gushchin if (cg_create(children[i])) 32184092dbcSRoman Gushchin goto cleanup; 32284092dbcSRoman Gushchin 32384092dbcSRoman Gushchin if (i == 2) 32484092dbcSRoman Gushchin continue; 32584092dbcSRoman Gushchin 32684092dbcSRoman Gushchin cg_run_nowait(children[i], alloc_pagecache_50M_noexit, 32784092dbcSRoman Gushchin (void *)(long)fd); 32884092dbcSRoman Gushchin } 32984092dbcSRoman Gushchin 33084092dbcSRoman Gushchin if (cg_write(parent[0], "memory.min", "50M")) 33184092dbcSRoman Gushchin goto cleanup; 33284092dbcSRoman Gushchin if (cg_write(parent[1], "memory.min", "50M")) 33384092dbcSRoman Gushchin goto cleanup; 33484092dbcSRoman Gushchin if (cg_write(children[0], "memory.min", "75M")) 33584092dbcSRoman Gushchin goto cleanup; 33684092dbcSRoman Gushchin if (cg_write(children[1], "memory.min", "25M")) 33784092dbcSRoman Gushchin goto cleanup; 33884092dbcSRoman Gushchin if (cg_write(children[2], "memory.min", "500M")) 33984092dbcSRoman Gushchin goto cleanup; 34084092dbcSRoman Gushchin if (cg_write(children[3], "memory.min", "0")) 34184092dbcSRoman Gushchin goto cleanup; 34284092dbcSRoman Gushchin 34384092dbcSRoman Gushchin attempts = 0; 34484092dbcSRoman Gushchin while (!values_close(cg_read_long(parent[1], "memory.current"), 34584092dbcSRoman Gushchin MB(150), 3)) { 34684092dbcSRoman Gushchin if (attempts++ > 5) 34784092dbcSRoman Gushchin break; 34884092dbcSRoman Gushchin sleep(1); 34984092dbcSRoman Gushchin } 35084092dbcSRoman Gushchin 35184092dbcSRoman Gushchin if (cg_run(parent[2], alloc_anon, (void *)MB(148))) 35284092dbcSRoman Gushchin goto cleanup; 35384092dbcSRoman Gushchin 35484092dbcSRoman Gushchin if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) 35584092dbcSRoman Gushchin goto cleanup; 35684092dbcSRoman Gushchin 35784092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) 35884092dbcSRoman Gushchin c[i] = cg_read_long(children[i], "memory.current"); 35984092dbcSRoman Gushchin 36084092dbcSRoman Gushchin if (!values_close(c[0], MB(33), 10)) 36184092dbcSRoman Gushchin goto cleanup; 36284092dbcSRoman Gushchin 36384092dbcSRoman Gushchin if (!values_close(c[1], MB(17), 10)) 36484092dbcSRoman Gushchin goto cleanup; 36584092dbcSRoman Gushchin 36684092dbcSRoman Gushchin if (!values_close(c[2], 0, 1)) 36784092dbcSRoman Gushchin goto cleanup; 36884092dbcSRoman Gushchin 36984092dbcSRoman Gushchin if (!cg_run(parent[2], alloc_anon, (void *)MB(170))) 37084092dbcSRoman Gushchin goto cleanup; 37184092dbcSRoman Gushchin 37284092dbcSRoman Gushchin if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) 37384092dbcSRoman Gushchin goto cleanup; 37484092dbcSRoman Gushchin 37584092dbcSRoman Gushchin ret = KSFT_PASS; 37684092dbcSRoman Gushchin 37784092dbcSRoman Gushchin cleanup: 37884092dbcSRoman Gushchin for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) { 37984092dbcSRoman Gushchin if (!children[i]) 38084092dbcSRoman Gushchin continue; 38184092dbcSRoman Gushchin 38284092dbcSRoman Gushchin cg_destroy(children[i]); 38384092dbcSRoman Gushchin free(children[i]); 38484092dbcSRoman Gushchin } 38584092dbcSRoman Gushchin 38684092dbcSRoman Gushchin for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) { 38784092dbcSRoman Gushchin if (!parent[i]) 38884092dbcSRoman Gushchin continue; 38984092dbcSRoman Gushchin 39084092dbcSRoman Gushchin cg_destroy(parent[i]); 39184092dbcSRoman Gushchin free(parent[i]); 39284092dbcSRoman Gushchin } 39384092dbcSRoman Gushchin close(fd); 39484092dbcSRoman Gushchin return ret; 39584092dbcSRoman Gushchin } 39684092dbcSRoman Gushchin 39784092dbcSRoman Gushchin /* 39884092dbcSRoman Gushchin * First, this test creates the following hierarchy: 39984092dbcSRoman Gushchin * A memory.low = 50M, memory.max = 200M 40084092dbcSRoman Gushchin * A/B memory.low = 50M, memory.current = 50M 40184092dbcSRoman Gushchin * A/B/C memory.low = 75M, memory.current = 50M 40284092dbcSRoman Gushchin * A/B/D memory.low = 25M, memory.current = 50M 40384092dbcSRoman Gushchin * A/B/E memory.low = 500M, memory.current = 0 40484092dbcSRoman Gushchin * A/B/F memory.low = 0, memory.current = 50M 40584092dbcSRoman Gushchin * 40684092dbcSRoman Gushchin * Usages are pagecache. 40784092dbcSRoman Gushchin * Then it creates A/G an creates a significant 40884092dbcSRoman Gushchin * memory pressure in it. 40984092dbcSRoman Gushchin * 41084092dbcSRoman Gushchin * Then it checks actual memory usages and expects that: 41184092dbcSRoman Gushchin * A/B memory.current ~= 50M 41284092dbcSRoman Gushchin * A/B/ memory.current ~= 33M 41384092dbcSRoman Gushchin * A/B/D memory.current ~= 17M 41484092dbcSRoman Gushchin * A/B/E memory.current ~= 0 41584092dbcSRoman Gushchin * 41684092dbcSRoman Gushchin * After that it tries to allocate more than there is 41784092dbcSRoman Gushchin * unprotected memory in A available, 41884092dbcSRoman Gushchin * and checks low and oom events in memory.events. 41984092dbcSRoman Gushchin */ 42084092dbcSRoman Gushchin static int test_memcg_low(const char *root) 42184092dbcSRoman Gushchin { 42284092dbcSRoman Gushchin int ret = KSFT_FAIL; 42384092dbcSRoman Gushchin char *parent[3] = {NULL}; 42484092dbcSRoman Gushchin char *children[4] = {NULL}; 42584092dbcSRoman Gushchin long low, oom; 42684092dbcSRoman Gushchin long c[4]; 42784092dbcSRoman Gushchin int i; 42884092dbcSRoman Gushchin int fd; 42984092dbcSRoman Gushchin 43084092dbcSRoman Gushchin fd = get_temp_fd(); 43184092dbcSRoman Gushchin if (fd < 0) 43284092dbcSRoman Gushchin goto cleanup; 43384092dbcSRoman Gushchin 43484092dbcSRoman Gushchin parent[0] = cg_name(root, "memcg_test_0"); 43584092dbcSRoman Gushchin if (!parent[0]) 43684092dbcSRoman Gushchin goto cleanup; 43784092dbcSRoman Gushchin 43884092dbcSRoman Gushchin parent[1] = cg_name(parent[0], "memcg_test_1"); 43984092dbcSRoman Gushchin if (!parent[1]) 44084092dbcSRoman Gushchin goto cleanup; 44184092dbcSRoman Gushchin 44284092dbcSRoman Gushchin parent[2] = cg_name(parent[0], "memcg_test_2"); 44384092dbcSRoman Gushchin if (!parent[2]) 44484092dbcSRoman Gushchin goto cleanup; 44584092dbcSRoman Gushchin 44684092dbcSRoman Gushchin if (cg_create(parent[0])) 44784092dbcSRoman Gushchin goto cleanup; 44884092dbcSRoman Gushchin 44984092dbcSRoman Gushchin if (cg_read_long(parent[0], "memory.low")) 45084092dbcSRoman Gushchin goto cleanup; 45184092dbcSRoman Gushchin 45284092dbcSRoman Gushchin if (cg_write(parent[0], "cgroup.subtree_control", "+memory")) 45384092dbcSRoman Gushchin goto cleanup; 45484092dbcSRoman Gushchin 45584092dbcSRoman Gushchin if (cg_write(parent[0], "memory.max", "200M")) 45684092dbcSRoman Gushchin goto cleanup; 45784092dbcSRoman Gushchin 45884092dbcSRoman Gushchin if (cg_write(parent[0], "memory.swap.max", "0")) 45984092dbcSRoman Gushchin goto cleanup; 46084092dbcSRoman Gushchin 46184092dbcSRoman Gushchin if (cg_create(parent[1])) 46284092dbcSRoman Gushchin goto cleanup; 46384092dbcSRoman Gushchin 46484092dbcSRoman Gushchin if (cg_write(parent[1], "cgroup.subtree_control", "+memory")) 46584092dbcSRoman Gushchin goto cleanup; 46684092dbcSRoman Gushchin 46784092dbcSRoman Gushchin if (cg_create(parent[2])) 46884092dbcSRoman Gushchin goto cleanup; 46984092dbcSRoman Gushchin 47084092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) { 47184092dbcSRoman Gushchin children[i] = cg_name_indexed(parent[1], "child_memcg", i); 47284092dbcSRoman Gushchin if (!children[i]) 47384092dbcSRoman Gushchin goto cleanup; 47484092dbcSRoman Gushchin 47584092dbcSRoman Gushchin if (cg_create(children[i])) 47684092dbcSRoman Gushchin goto cleanup; 47784092dbcSRoman Gushchin 47884092dbcSRoman Gushchin if (i == 2) 47984092dbcSRoman Gushchin continue; 48084092dbcSRoman Gushchin 48184092dbcSRoman Gushchin if (cg_run(children[i], alloc_pagecache_50M, (void *)(long)fd)) 48284092dbcSRoman Gushchin goto cleanup; 48384092dbcSRoman Gushchin } 48484092dbcSRoman Gushchin 48584092dbcSRoman Gushchin if (cg_write(parent[0], "memory.low", "50M")) 48684092dbcSRoman Gushchin goto cleanup; 48784092dbcSRoman Gushchin if (cg_write(parent[1], "memory.low", "50M")) 48884092dbcSRoman Gushchin goto cleanup; 48984092dbcSRoman Gushchin if (cg_write(children[0], "memory.low", "75M")) 49084092dbcSRoman Gushchin goto cleanup; 49184092dbcSRoman Gushchin if (cg_write(children[1], "memory.low", "25M")) 49284092dbcSRoman Gushchin goto cleanup; 49384092dbcSRoman Gushchin if (cg_write(children[2], "memory.low", "500M")) 49484092dbcSRoman Gushchin goto cleanup; 49584092dbcSRoman Gushchin if (cg_write(children[3], "memory.low", "0")) 49684092dbcSRoman Gushchin goto cleanup; 49784092dbcSRoman Gushchin 49884092dbcSRoman Gushchin if (cg_run(parent[2], alloc_anon, (void *)MB(148))) 49984092dbcSRoman Gushchin goto cleanup; 50084092dbcSRoman Gushchin 50184092dbcSRoman Gushchin if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3)) 50284092dbcSRoman Gushchin goto cleanup; 50384092dbcSRoman Gushchin 50484092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) 50584092dbcSRoman Gushchin c[i] = cg_read_long(children[i], "memory.current"); 50684092dbcSRoman Gushchin 50784092dbcSRoman Gushchin if (!values_close(c[0], MB(33), 10)) 50884092dbcSRoman Gushchin goto cleanup; 50984092dbcSRoman Gushchin 51084092dbcSRoman Gushchin if (!values_close(c[1], MB(17), 10)) 51184092dbcSRoman Gushchin goto cleanup; 51284092dbcSRoman Gushchin 51384092dbcSRoman Gushchin if (!values_close(c[2], 0, 1)) 51484092dbcSRoman Gushchin goto cleanup; 51584092dbcSRoman Gushchin 51684092dbcSRoman Gushchin if (cg_run(parent[2], alloc_anon, (void *)MB(166))) { 51784092dbcSRoman Gushchin fprintf(stderr, 51884092dbcSRoman Gushchin "memory.low prevents from allocating anon memory\n"); 51984092dbcSRoman Gushchin goto cleanup; 52084092dbcSRoman Gushchin } 52184092dbcSRoman Gushchin 52284092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(children); i++) { 52384092dbcSRoman Gushchin oom = cg_read_key_long(children[i], "memory.events", "oom "); 52484092dbcSRoman Gushchin low = cg_read_key_long(children[i], "memory.events", "low "); 52584092dbcSRoman Gushchin 52684092dbcSRoman Gushchin if (oom) 52784092dbcSRoman Gushchin goto cleanup; 52884092dbcSRoman Gushchin if (i < 2 && low <= 0) 52984092dbcSRoman Gushchin goto cleanup; 53084092dbcSRoman Gushchin if (i >= 2 && low) 53184092dbcSRoman Gushchin goto cleanup; 53284092dbcSRoman Gushchin } 53384092dbcSRoman Gushchin 53484092dbcSRoman Gushchin ret = KSFT_PASS; 53584092dbcSRoman Gushchin 53684092dbcSRoman Gushchin cleanup: 53784092dbcSRoman Gushchin for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) { 53884092dbcSRoman Gushchin if (!children[i]) 53984092dbcSRoman Gushchin continue; 54084092dbcSRoman Gushchin 54184092dbcSRoman Gushchin cg_destroy(children[i]); 54284092dbcSRoman Gushchin free(children[i]); 54384092dbcSRoman Gushchin } 54484092dbcSRoman Gushchin 54584092dbcSRoman Gushchin for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) { 54684092dbcSRoman Gushchin if (!parent[i]) 54784092dbcSRoman Gushchin continue; 54884092dbcSRoman Gushchin 54984092dbcSRoman Gushchin cg_destroy(parent[i]); 55084092dbcSRoman Gushchin free(parent[i]); 55184092dbcSRoman Gushchin } 55284092dbcSRoman Gushchin close(fd); 55384092dbcSRoman Gushchin return ret; 55484092dbcSRoman Gushchin } 55584092dbcSRoman Gushchin 55684092dbcSRoman Gushchin static int alloc_pagecache_max_30M(const char *cgroup, void *arg) 55784092dbcSRoman Gushchin { 55884092dbcSRoman Gushchin size_t size = MB(50); 55984092dbcSRoman Gushchin int ret = -1; 56084092dbcSRoman Gushchin long current; 56184092dbcSRoman Gushchin int fd; 56284092dbcSRoman Gushchin 56384092dbcSRoman Gushchin fd = get_temp_fd(); 56484092dbcSRoman Gushchin if (fd < 0) 56584092dbcSRoman Gushchin return -1; 56684092dbcSRoman Gushchin 56784092dbcSRoman Gushchin if (alloc_pagecache(fd, size)) 56884092dbcSRoman Gushchin goto cleanup; 56984092dbcSRoman Gushchin 57084092dbcSRoman Gushchin current = cg_read_long(cgroup, "memory.current"); 57184092dbcSRoman Gushchin if (current <= MB(29) || current > MB(30)) 57284092dbcSRoman Gushchin goto cleanup; 57384092dbcSRoman Gushchin 57484092dbcSRoman Gushchin ret = 0; 57584092dbcSRoman Gushchin 57684092dbcSRoman Gushchin cleanup: 57784092dbcSRoman Gushchin close(fd); 57884092dbcSRoman Gushchin return ret; 57984092dbcSRoman Gushchin 58084092dbcSRoman Gushchin } 58184092dbcSRoman Gushchin 58284092dbcSRoman Gushchin /* 58384092dbcSRoman Gushchin * This test checks that memory.high limits the amount of 58484092dbcSRoman Gushchin * memory which can be consumed by either anonymous memory 58584092dbcSRoman Gushchin * or pagecache. 58684092dbcSRoman Gushchin */ 58784092dbcSRoman Gushchin static int test_memcg_high(const char *root) 58884092dbcSRoman Gushchin { 58984092dbcSRoman Gushchin int ret = KSFT_FAIL; 59084092dbcSRoman Gushchin char *memcg; 59184092dbcSRoman Gushchin long high; 59284092dbcSRoman Gushchin 59384092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 59484092dbcSRoman Gushchin if (!memcg) 59584092dbcSRoman Gushchin goto cleanup; 59684092dbcSRoman Gushchin 59784092dbcSRoman Gushchin if (cg_create(memcg)) 59884092dbcSRoman Gushchin goto cleanup; 59984092dbcSRoman Gushchin 60084092dbcSRoman Gushchin if (cg_read_strcmp(memcg, "memory.high", "max\n")) 60184092dbcSRoman Gushchin goto cleanup; 60284092dbcSRoman Gushchin 60384092dbcSRoman Gushchin if (cg_write(memcg, "memory.swap.max", "0")) 60484092dbcSRoman Gushchin goto cleanup; 60584092dbcSRoman Gushchin 60684092dbcSRoman Gushchin if (cg_write(memcg, "memory.high", "30M")) 60784092dbcSRoman Gushchin goto cleanup; 60884092dbcSRoman Gushchin 60984092dbcSRoman Gushchin if (cg_run(memcg, alloc_anon, (void *)MB(100))) 61084092dbcSRoman Gushchin goto cleanup; 61184092dbcSRoman Gushchin 61284092dbcSRoman Gushchin if (!cg_run(memcg, alloc_pagecache_50M_check, NULL)) 61384092dbcSRoman Gushchin goto cleanup; 61484092dbcSRoman Gushchin 61584092dbcSRoman Gushchin if (cg_run(memcg, alloc_pagecache_max_30M, NULL)) 61684092dbcSRoman Gushchin goto cleanup; 61784092dbcSRoman Gushchin 61884092dbcSRoman Gushchin high = cg_read_key_long(memcg, "memory.events", "high "); 61984092dbcSRoman Gushchin if (high <= 0) 62084092dbcSRoman Gushchin goto cleanup; 62184092dbcSRoman Gushchin 62284092dbcSRoman Gushchin ret = KSFT_PASS; 62384092dbcSRoman Gushchin 62484092dbcSRoman Gushchin cleanup: 62584092dbcSRoman Gushchin cg_destroy(memcg); 62684092dbcSRoman Gushchin free(memcg); 62784092dbcSRoman Gushchin 62884092dbcSRoman Gushchin return ret; 62984092dbcSRoman Gushchin } 63084092dbcSRoman Gushchin 63184092dbcSRoman Gushchin /* 63284092dbcSRoman Gushchin * This test checks that memory.max limits the amount of 63384092dbcSRoman Gushchin * memory which can be consumed by either anonymous memory 63484092dbcSRoman Gushchin * or pagecache. 63584092dbcSRoman Gushchin */ 63684092dbcSRoman Gushchin static int test_memcg_max(const char *root) 63784092dbcSRoman Gushchin { 63884092dbcSRoman Gushchin int ret = KSFT_FAIL; 63984092dbcSRoman Gushchin char *memcg; 64084092dbcSRoman Gushchin long current, max; 64184092dbcSRoman Gushchin 64284092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 64384092dbcSRoman Gushchin if (!memcg) 64484092dbcSRoman Gushchin goto cleanup; 64584092dbcSRoman Gushchin 64684092dbcSRoman Gushchin if (cg_create(memcg)) 64784092dbcSRoman Gushchin goto cleanup; 64884092dbcSRoman Gushchin 64984092dbcSRoman Gushchin if (cg_read_strcmp(memcg, "memory.max", "max\n")) 65084092dbcSRoman Gushchin goto cleanup; 65184092dbcSRoman Gushchin 65284092dbcSRoman Gushchin if (cg_write(memcg, "memory.swap.max", "0")) 65384092dbcSRoman Gushchin goto cleanup; 65484092dbcSRoman Gushchin 65584092dbcSRoman Gushchin if (cg_write(memcg, "memory.max", "30M")) 65684092dbcSRoman Gushchin goto cleanup; 65784092dbcSRoman Gushchin 65884092dbcSRoman Gushchin /* Should be killed by OOM killer */ 65984092dbcSRoman Gushchin if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 66084092dbcSRoman Gushchin goto cleanup; 66184092dbcSRoman Gushchin 66284092dbcSRoman Gushchin if (cg_run(memcg, alloc_pagecache_max_30M, NULL)) 66384092dbcSRoman Gushchin goto cleanup; 66484092dbcSRoman Gushchin 66584092dbcSRoman Gushchin current = cg_read_long(memcg, "memory.current"); 66684092dbcSRoman Gushchin if (current > MB(30) || !current) 66784092dbcSRoman Gushchin goto cleanup; 66884092dbcSRoman Gushchin 66984092dbcSRoman Gushchin max = cg_read_key_long(memcg, "memory.events", "max "); 67084092dbcSRoman Gushchin if (max <= 0) 67184092dbcSRoman Gushchin goto cleanup; 67284092dbcSRoman Gushchin 67384092dbcSRoman Gushchin ret = KSFT_PASS; 67484092dbcSRoman Gushchin 67584092dbcSRoman Gushchin cleanup: 67684092dbcSRoman Gushchin cg_destroy(memcg); 67784092dbcSRoman Gushchin free(memcg); 67884092dbcSRoman Gushchin 67984092dbcSRoman Gushchin return ret; 68084092dbcSRoman Gushchin } 68184092dbcSRoman Gushchin 682478b2784SMike Rapoport static int alloc_anon_50M_check_swap(const char *cgroup, void *arg) 683478b2784SMike Rapoport { 684478b2784SMike Rapoport long mem_max = (long)arg; 685478b2784SMike Rapoport size_t size = MB(50); 686478b2784SMike Rapoport char *buf, *ptr; 687478b2784SMike Rapoport long mem_current, swap_current; 688478b2784SMike Rapoport int ret = -1; 689478b2784SMike Rapoport 690478b2784SMike Rapoport buf = malloc(size); 691478b2784SMike Rapoport for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE) 692478b2784SMike Rapoport *ptr = 0; 693478b2784SMike Rapoport 694478b2784SMike Rapoport mem_current = cg_read_long(cgroup, "memory.current"); 695478b2784SMike Rapoport if (!mem_current || !values_close(mem_current, mem_max, 3)) 696478b2784SMike Rapoport goto cleanup; 697478b2784SMike Rapoport 698478b2784SMike Rapoport swap_current = cg_read_long(cgroup, "memory.swap.current"); 699478b2784SMike Rapoport if (!swap_current || 700478b2784SMike Rapoport !values_close(mem_current + swap_current, size, 3)) 701478b2784SMike Rapoport goto cleanup; 702478b2784SMike Rapoport 703478b2784SMike Rapoport ret = 0; 704478b2784SMike Rapoport cleanup: 705478b2784SMike Rapoport free(buf); 706478b2784SMike Rapoport return ret; 707478b2784SMike Rapoport } 708478b2784SMike Rapoport 709478b2784SMike Rapoport /* 710478b2784SMike Rapoport * This test checks that memory.swap.max limits the amount of 711478b2784SMike Rapoport * anonymous memory which can be swapped out. 712478b2784SMike Rapoport */ 713478b2784SMike Rapoport static int test_memcg_swap_max(const char *root) 714478b2784SMike Rapoport { 715478b2784SMike Rapoport int ret = KSFT_FAIL; 716478b2784SMike Rapoport char *memcg; 717478b2784SMike Rapoport long max; 718478b2784SMike Rapoport 719478b2784SMike Rapoport if (!is_swap_enabled()) 720478b2784SMike Rapoport return KSFT_SKIP; 721478b2784SMike Rapoport 722478b2784SMike Rapoport memcg = cg_name(root, "memcg_test"); 723478b2784SMike Rapoport if (!memcg) 724478b2784SMike Rapoport goto cleanup; 725478b2784SMike Rapoport 726478b2784SMike Rapoport if (cg_create(memcg)) 727478b2784SMike Rapoport goto cleanup; 728478b2784SMike Rapoport 729478b2784SMike Rapoport if (cg_read_long(memcg, "memory.swap.current")) { 730478b2784SMike Rapoport ret = KSFT_SKIP; 731478b2784SMike Rapoport goto cleanup; 732478b2784SMike Rapoport } 733478b2784SMike Rapoport 734478b2784SMike Rapoport if (cg_read_strcmp(memcg, "memory.max", "max\n")) 735478b2784SMike Rapoport goto cleanup; 736478b2784SMike Rapoport 737478b2784SMike Rapoport if (cg_read_strcmp(memcg, "memory.swap.max", "max\n")) 738478b2784SMike Rapoport goto cleanup; 739478b2784SMike Rapoport 740478b2784SMike Rapoport if (cg_write(memcg, "memory.swap.max", "30M")) 741478b2784SMike Rapoport goto cleanup; 742478b2784SMike Rapoport 743478b2784SMike Rapoport if (cg_write(memcg, "memory.max", "30M")) 744478b2784SMike Rapoport goto cleanup; 745478b2784SMike Rapoport 746478b2784SMike Rapoport /* Should be killed by OOM killer */ 747478b2784SMike Rapoport if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 748478b2784SMike Rapoport goto cleanup; 749478b2784SMike Rapoport 750478b2784SMike Rapoport if (cg_read_key_long(memcg, "memory.events", "oom ") != 1) 751478b2784SMike Rapoport goto cleanup; 752478b2784SMike Rapoport 753478b2784SMike Rapoport if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1) 754478b2784SMike Rapoport goto cleanup; 755478b2784SMike Rapoport 756478b2784SMike Rapoport if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30))) 757478b2784SMike Rapoport goto cleanup; 758478b2784SMike Rapoport 759478b2784SMike Rapoport max = cg_read_key_long(memcg, "memory.events", "max "); 760478b2784SMike Rapoport if (max <= 0) 761478b2784SMike Rapoport goto cleanup; 762478b2784SMike Rapoport 763478b2784SMike Rapoport ret = KSFT_PASS; 764478b2784SMike Rapoport 765478b2784SMike Rapoport cleanup: 766478b2784SMike Rapoport cg_destroy(memcg); 767478b2784SMike Rapoport free(memcg); 768478b2784SMike Rapoport 769478b2784SMike Rapoport return ret; 770478b2784SMike Rapoport } 771478b2784SMike Rapoport 77284092dbcSRoman Gushchin /* 77384092dbcSRoman Gushchin * This test disables swapping and tries to allocate anonymous memory 77484092dbcSRoman Gushchin * up to OOM. Then it checks for oom and oom_kill events in 77584092dbcSRoman Gushchin * memory.events. 77684092dbcSRoman Gushchin */ 77784092dbcSRoman Gushchin static int test_memcg_oom_events(const char *root) 77884092dbcSRoman Gushchin { 77984092dbcSRoman Gushchin int ret = KSFT_FAIL; 78084092dbcSRoman Gushchin char *memcg; 78184092dbcSRoman Gushchin 78284092dbcSRoman Gushchin memcg = cg_name(root, "memcg_test"); 78384092dbcSRoman Gushchin if (!memcg) 78484092dbcSRoman Gushchin goto cleanup; 78584092dbcSRoman Gushchin 78684092dbcSRoman Gushchin if (cg_create(memcg)) 78784092dbcSRoman Gushchin goto cleanup; 78884092dbcSRoman Gushchin 78984092dbcSRoman Gushchin if (cg_write(memcg, "memory.max", "30M")) 79084092dbcSRoman Gushchin goto cleanup; 79184092dbcSRoman Gushchin 79284092dbcSRoman Gushchin if (cg_write(memcg, "memory.swap.max", "0")) 79384092dbcSRoman Gushchin goto cleanup; 79484092dbcSRoman Gushchin 79584092dbcSRoman Gushchin if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 79684092dbcSRoman Gushchin goto cleanup; 79784092dbcSRoman Gushchin 79884092dbcSRoman Gushchin if (cg_read_strcmp(memcg, "cgroup.procs", "")) 79984092dbcSRoman Gushchin goto cleanup; 80084092dbcSRoman Gushchin 80184092dbcSRoman Gushchin if (cg_read_key_long(memcg, "memory.events", "oom ") != 1) 80284092dbcSRoman Gushchin goto cleanup; 80384092dbcSRoman Gushchin 80484092dbcSRoman Gushchin if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1) 80584092dbcSRoman Gushchin goto cleanup; 80684092dbcSRoman Gushchin 80784092dbcSRoman Gushchin ret = KSFT_PASS; 80884092dbcSRoman Gushchin 80984092dbcSRoman Gushchin cleanup: 81084092dbcSRoman Gushchin cg_destroy(memcg); 81184092dbcSRoman Gushchin free(memcg); 81284092dbcSRoman Gushchin 81384092dbcSRoman Gushchin return ret; 81484092dbcSRoman Gushchin } 81584092dbcSRoman Gushchin 8165f8f0193SMike Rapoport struct tcp_server_args { 8175f8f0193SMike Rapoport unsigned short port; 8185f8f0193SMike Rapoport int ctl[2]; 8195f8f0193SMike Rapoport }; 8205f8f0193SMike Rapoport 8215f8f0193SMike Rapoport static int tcp_server(const char *cgroup, void *arg) 8225f8f0193SMike Rapoport { 8235f8f0193SMike Rapoport struct tcp_server_args *srv_args = arg; 8245f8f0193SMike Rapoport struct sockaddr_in6 saddr = { 0 }; 8255f8f0193SMike Rapoport socklen_t slen = sizeof(saddr); 8265f8f0193SMike Rapoport int sk, client_sk, ctl_fd, yes = 1, ret = -1; 8275f8f0193SMike Rapoport 8285f8f0193SMike Rapoport close(srv_args->ctl[0]); 8295f8f0193SMike Rapoport ctl_fd = srv_args->ctl[1]; 8305f8f0193SMike Rapoport 8315f8f0193SMike Rapoport saddr.sin6_family = AF_INET6; 8325f8f0193SMike Rapoport saddr.sin6_addr = in6addr_any; 8335f8f0193SMike Rapoport saddr.sin6_port = htons(srv_args->port); 8345f8f0193SMike Rapoport 8355f8f0193SMike Rapoport sk = socket(AF_INET6, SOCK_STREAM, 0); 8365f8f0193SMike Rapoport if (sk < 0) 8375f8f0193SMike Rapoport return ret; 8385f8f0193SMike Rapoport 8395f8f0193SMike Rapoport if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) 8405f8f0193SMike Rapoport goto cleanup; 8415f8f0193SMike Rapoport 8425f8f0193SMike Rapoport if (bind(sk, (struct sockaddr *)&saddr, slen)) { 8435f8f0193SMike Rapoport write(ctl_fd, &errno, sizeof(errno)); 8445f8f0193SMike Rapoport goto cleanup; 8455f8f0193SMike Rapoport } 8465f8f0193SMike Rapoport 8475f8f0193SMike Rapoport if (listen(sk, 1)) 8485f8f0193SMike Rapoport goto cleanup; 8495f8f0193SMike Rapoport 8505f8f0193SMike Rapoport ret = 0; 8515f8f0193SMike Rapoport if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) { 8525f8f0193SMike Rapoport ret = -1; 8535f8f0193SMike Rapoport goto cleanup; 8545f8f0193SMike Rapoport } 8555f8f0193SMike Rapoport 8565f8f0193SMike Rapoport client_sk = accept(sk, NULL, NULL); 8575f8f0193SMike Rapoport if (client_sk < 0) 8585f8f0193SMike Rapoport goto cleanup; 8595f8f0193SMike Rapoport 8605f8f0193SMike Rapoport ret = -1; 8615f8f0193SMike Rapoport for (;;) { 8625f8f0193SMike Rapoport uint8_t buf[0x100000]; 8635f8f0193SMike Rapoport 8645f8f0193SMike Rapoport if (write(client_sk, buf, sizeof(buf)) <= 0) { 8655f8f0193SMike Rapoport if (errno == ECONNRESET) 8665f8f0193SMike Rapoport ret = 0; 8675f8f0193SMike Rapoport break; 8685f8f0193SMike Rapoport } 8695f8f0193SMike Rapoport } 8705f8f0193SMike Rapoport 8715f8f0193SMike Rapoport close(client_sk); 8725f8f0193SMike Rapoport 8735f8f0193SMike Rapoport cleanup: 8745f8f0193SMike Rapoport close(sk); 8755f8f0193SMike Rapoport return ret; 8765f8f0193SMike Rapoport } 8775f8f0193SMike Rapoport 8785f8f0193SMike Rapoport static int tcp_client(const char *cgroup, unsigned short port) 8795f8f0193SMike Rapoport { 8805f8f0193SMike Rapoport const char server[] = "localhost"; 8815f8f0193SMike Rapoport struct addrinfo *ai; 8825f8f0193SMike Rapoport char servport[6]; 8835f8f0193SMike Rapoport int retries = 0x10; /* nice round number */ 8845f8f0193SMike Rapoport int sk, ret; 8855f8f0193SMike Rapoport 8865f8f0193SMike Rapoport snprintf(servport, sizeof(servport), "%hd", port); 8875f8f0193SMike Rapoport ret = getaddrinfo(server, servport, NULL, &ai); 8885f8f0193SMike Rapoport if (ret) 8895f8f0193SMike Rapoport return ret; 8905f8f0193SMike Rapoport 8915f8f0193SMike Rapoport sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 8925f8f0193SMike Rapoport if (sk < 0) 8935f8f0193SMike Rapoport goto free_ainfo; 8945f8f0193SMike Rapoport 8955f8f0193SMike Rapoport ret = connect(sk, ai->ai_addr, ai->ai_addrlen); 8965f8f0193SMike Rapoport if (ret < 0) 8975f8f0193SMike Rapoport goto close_sk; 8985f8f0193SMike Rapoport 8995f8f0193SMike Rapoport ret = KSFT_FAIL; 9005f8f0193SMike Rapoport while (retries--) { 9015f8f0193SMike Rapoport uint8_t buf[0x100000]; 9025f8f0193SMike Rapoport long current, sock; 9035f8f0193SMike Rapoport 9045f8f0193SMike Rapoport if (read(sk, buf, sizeof(buf)) <= 0) 9055f8f0193SMike Rapoport goto close_sk; 9065f8f0193SMike Rapoport 9075f8f0193SMike Rapoport current = cg_read_long(cgroup, "memory.current"); 9085f8f0193SMike Rapoport sock = cg_read_key_long(cgroup, "memory.stat", "sock "); 9095f8f0193SMike Rapoport 9105f8f0193SMike Rapoport if (current < 0 || sock < 0) 9115f8f0193SMike Rapoport goto close_sk; 9125f8f0193SMike Rapoport 9135f8f0193SMike Rapoport if (current < sock) 9145f8f0193SMike Rapoport goto close_sk; 9155f8f0193SMike Rapoport 9165f8f0193SMike Rapoport if (values_close(current, sock, 10)) { 9175f8f0193SMike Rapoport ret = KSFT_PASS; 9185f8f0193SMike Rapoport break; 9195f8f0193SMike Rapoport } 9205f8f0193SMike Rapoport } 9215f8f0193SMike Rapoport 9225f8f0193SMike Rapoport close_sk: 9235f8f0193SMike Rapoport close(sk); 9245f8f0193SMike Rapoport free_ainfo: 9255f8f0193SMike Rapoport freeaddrinfo(ai); 9265f8f0193SMike Rapoport return ret; 9275f8f0193SMike Rapoport } 9285f8f0193SMike Rapoport 9295f8f0193SMike Rapoport /* 9305f8f0193SMike Rapoport * This test checks socket memory accounting. 9315f8f0193SMike Rapoport * The test forks a TCP server listens on a random port between 1000 9325f8f0193SMike Rapoport * and 61000. Once it gets a client connection, it starts writing to 9335f8f0193SMike Rapoport * its socket. 9345f8f0193SMike Rapoport * The TCP client interleaves reads from the socket with check whether 9355f8f0193SMike Rapoport * memory.current and memory.stat.sock are similar. 9365f8f0193SMike Rapoport */ 9375f8f0193SMike Rapoport static int test_memcg_sock(const char *root) 9385f8f0193SMike Rapoport { 9395f8f0193SMike Rapoport int bind_retries = 5, ret = KSFT_FAIL, pid, err; 9405f8f0193SMike Rapoport unsigned short port; 9415f8f0193SMike Rapoport char *memcg; 9425f8f0193SMike Rapoport 9435f8f0193SMike Rapoport memcg = cg_name(root, "memcg_test"); 9445f8f0193SMike Rapoport if (!memcg) 9455f8f0193SMike Rapoport goto cleanup; 9465f8f0193SMike Rapoport 9475f8f0193SMike Rapoport if (cg_create(memcg)) 9485f8f0193SMike Rapoport goto cleanup; 9495f8f0193SMike Rapoport 9505f8f0193SMike Rapoport while (bind_retries--) { 9515f8f0193SMike Rapoport struct tcp_server_args args; 9525f8f0193SMike Rapoport 9535f8f0193SMike Rapoport if (pipe(args.ctl)) 9545f8f0193SMike Rapoport goto cleanup; 9555f8f0193SMike Rapoport 9565f8f0193SMike Rapoport port = args.port = 1000 + rand() % 60000; 9575f8f0193SMike Rapoport 9585f8f0193SMike Rapoport pid = cg_run_nowait(memcg, tcp_server, &args); 9595f8f0193SMike Rapoport if (pid < 0) 9605f8f0193SMike Rapoport goto cleanup; 9615f8f0193SMike Rapoport 9625f8f0193SMike Rapoport close(args.ctl[1]); 9635f8f0193SMike Rapoport if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err)) 9645f8f0193SMike Rapoport goto cleanup; 9655f8f0193SMike Rapoport close(args.ctl[0]); 9665f8f0193SMike Rapoport 9675f8f0193SMike Rapoport if (!err) 9685f8f0193SMike Rapoport break; 9695f8f0193SMike Rapoport if (err != EADDRINUSE) 9705f8f0193SMike Rapoport goto cleanup; 9715f8f0193SMike Rapoport 9725f8f0193SMike Rapoport waitpid(pid, NULL, 0); 9735f8f0193SMike Rapoport } 9745f8f0193SMike Rapoport 9755f8f0193SMike Rapoport if (err == EADDRINUSE) { 9765f8f0193SMike Rapoport ret = KSFT_SKIP; 9775f8f0193SMike Rapoport goto cleanup; 9785f8f0193SMike Rapoport } 9795f8f0193SMike Rapoport 9805f8f0193SMike Rapoport if (tcp_client(memcg, port) != KSFT_PASS) 9815f8f0193SMike Rapoport goto cleanup; 9825f8f0193SMike Rapoport 9835f8f0193SMike Rapoport waitpid(pid, &err, 0); 9845f8f0193SMike Rapoport if (WEXITSTATUS(err)) 9855f8f0193SMike Rapoport goto cleanup; 9865f8f0193SMike Rapoport 9875f8f0193SMike Rapoport if (cg_read_long(memcg, "memory.current") < 0) 9885f8f0193SMike Rapoport goto cleanup; 9895f8f0193SMike Rapoport 9905f8f0193SMike Rapoport if (cg_read_key_long(memcg, "memory.stat", "sock ")) 9915f8f0193SMike Rapoport goto cleanup; 9925f8f0193SMike Rapoport 9935f8f0193SMike Rapoport ret = KSFT_PASS; 9945f8f0193SMike Rapoport 9955f8f0193SMike Rapoport cleanup: 9965f8f0193SMike Rapoport cg_destroy(memcg); 9975f8f0193SMike Rapoport free(memcg); 9985f8f0193SMike Rapoport 9995f8f0193SMike Rapoport return ret; 10005f8f0193SMike Rapoport } 10015f8f0193SMike Rapoport 1002a987785dSJay Kamat /* 1003a987785dSJay Kamat * This test disables swapping and tries to allocate anonymous memory 1004a987785dSJay Kamat * up to OOM with memory.group.oom set. Then it checks that all 1005a987785dSJay Kamat * processes in the leaf (but not the parent) were killed. 1006a987785dSJay Kamat */ 1007a987785dSJay Kamat static int test_memcg_oom_group_leaf_events(const char *root) 1008a987785dSJay Kamat { 1009a987785dSJay Kamat int ret = KSFT_FAIL; 1010a987785dSJay Kamat char *parent, *child; 1011a987785dSJay Kamat 1012a987785dSJay Kamat parent = cg_name(root, "memcg_test_0"); 1013a987785dSJay Kamat child = cg_name(root, "memcg_test_0/memcg_test_1"); 1014a987785dSJay Kamat 1015a987785dSJay Kamat if (!parent || !child) 1016a987785dSJay Kamat goto cleanup; 1017a987785dSJay Kamat 1018a987785dSJay Kamat if (cg_create(parent)) 1019a987785dSJay Kamat goto cleanup; 1020a987785dSJay Kamat 1021a987785dSJay Kamat if (cg_create(child)) 1022a987785dSJay Kamat goto cleanup; 1023a987785dSJay Kamat 1024a987785dSJay Kamat if (cg_write(parent, "cgroup.subtree_control", "+memory")) 1025a987785dSJay Kamat goto cleanup; 1026a987785dSJay Kamat 1027a987785dSJay Kamat if (cg_write(child, "memory.max", "50M")) 1028a987785dSJay Kamat goto cleanup; 1029a987785dSJay Kamat 1030a987785dSJay Kamat if (cg_write(child, "memory.swap.max", "0")) 1031a987785dSJay Kamat goto cleanup; 1032a987785dSJay Kamat 1033a987785dSJay Kamat if (cg_write(child, "memory.oom.group", "1")) 1034a987785dSJay Kamat goto cleanup; 1035a987785dSJay Kamat 1036a987785dSJay Kamat cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); 1037a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1038a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1039a987785dSJay Kamat if (!cg_run(child, alloc_anon, (void *)MB(100))) 1040a987785dSJay Kamat goto cleanup; 1041a987785dSJay Kamat 1042a987785dSJay Kamat if (cg_test_proc_killed(child)) 1043a987785dSJay Kamat goto cleanup; 1044a987785dSJay Kamat 1045a987785dSJay Kamat if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0) 1046a987785dSJay Kamat goto cleanup; 1047a987785dSJay Kamat 1048a987785dSJay Kamat if (cg_read_key_long(parent, "memory.events", "oom_kill ") != 0) 1049a987785dSJay Kamat goto cleanup; 1050a987785dSJay Kamat 1051a987785dSJay Kamat ret = KSFT_PASS; 1052a987785dSJay Kamat 1053a987785dSJay Kamat cleanup: 1054a987785dSJay Kamat if (child) 1055a987785dSJay Kamat cg_destroy(child); 1056a987785dSJay Kamat if (parent) 1057a987785dSJay Kamat cg_destroy(parent); 1058a987785dSJay Kamat free(child); 1059a987785dSJay Kamat free(parent); 1060a987785dSJay Kamat 1061a987785dSJay Kamat return ret; 1062a987785dSJay Kamat } 1063a987785dSJay Kamat 1064a987785dSJay Kamat /* 1065a987785dSJay Kamat * This test disables swapping and tries to allocate anonymous memory 1066a987785dSJay Kamat * up to OOM with memory.group.oom set. Then it checks that all 1067a987785dSJay Kamat * processes in the parent and leaf were killed. 1068a987785dSJay Kamat */ 1069a987785dSJay Kamat static int test_memcg_oom_group_parent_events(const char *root) 1070a987785dSJay Kamat { 1071a987785dSJay Kamat int ret = KSFT_FAIL; 1072a987785dSJay Kamat char *parent, *child; 1073a987785dSJay Kamat 1074a987785dSJay Kamat parent = cg_name(root, "memcg_test_0"); 1075a987785dSJay Kamat child = cg_name(root, "memcg_test_0/memcg_test_1"); 1076a987785dSJay Kamat 1077a987785dSJay Kamat if (!parent || !child) 1078a987785dSJay Kamat goto cleanup; 1079a987785dSJay Kamat 1080a987785dSJay Kamat if (cg_create(parent)) 1081a987785dSJay Kamat goto cleanup; 1082a987785dSJay Kamat 1083a987785dSJay Kamat if (cg_create(child)) 1084a987785dSJay Kamat goto cleanup; 1085a987785dSJay Kamat 1086a987785dSJay Kamat if (cg_write(parent, "memory.max", "80M")) 1087a987785dSJay Kamat goto cleanup; 1088a987785dSJay Kamat 1089a987785dSJay Kamat if (cg_write(parent, "memory.swap.max", "0")) 1090a987785dSJay Kamat goto cleanup; 1091a987785dSJay Kamat 1092a987785dSJay Kamat if (cg_write(parent, "memory.oom.group", "1")) 1093a987785dSJay Kamat goto cleanup; 1094a987785dSJay Kamat 1095a987785dSJay Kamat cg_run_nowait(parent, alloc_anon_noexit, (void *) MB(60)); 1096a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1097a987785dSJay Kamat cg_run_nowait(child, alloc_anon_noexit, (void *) MB(1)); 1098a987785dSJay Kamat 1099a987785dSJay Kamat if (!cg_run(child, alloc_anon, (void *)MB(100))) 1100a987785dSJay Kamat goto cleanup; 1101a987785dSJay Kamat 1102a987785dSJay Kamat if (cg_test_proc_killed(child)) 1103a987785dSJay Kamat goto cleanup; 1104a987785dSJay Kamat if (cg_test_proc_killed(parent)) 1105a987785dSJay Kamat goto cleanup; 1106a987785dSJay Kamat 1107a987785dSJay Kamat ret = KSFT_PASS; 1108a987785dSJay Kamat 1109a987785dSJay Kamat cleanup: 1110a987785dSJay Kamat if (child) 1111a987785dSJay Kamat cg_destroy(child); 1112a987785dSJay Kamat if (parent) 1113a987785dSJay Kamat cg_destroy(parent); 1114a987785dSJay Kamat free(child); 1115a987785dSJay Kamat free(parent); 1116a987785dSJay Kamat 1117a987785dSJay Kamat return ret; 1118a987785dSJay Kamat } 1119a987785dSJay Kamat 1120a987785dSJay Kamat /* 1121a987785dSJay Kamat * This test disables swapping and tries to allocate anonymous memory 1122a987785dSJay Kamat * up to OOM with memory.group.oom set. Then it checks that all 1123a987785dSJay Kamat * processes were killed except those set with OOM_SCORE_ADJ_MIN 1124a987785dSJay Kamat */ 1125a987785dSJay Kamat static int test_memcg_oom_group_score_events(const char *root) 1126a987785dSJay Kamat { 1127a987785dSJay Kamat int ret = KSFT_FAIL; 1128a987785dSJay Kamat char *memcg; 1129a987785dSJay Kamat int safe_pid; 1130a987785dSJay Kamat 1131a987785dSJay Kamat memcg = cg_name(root, "memcg_test_0"); 1132a987785dSJay Kamat 1133a987785dSJay Kamat if (!memcg) 1134a987785dSJay Kamat goto cleanup; 1135a987785dSJay Kamat 1136a987785dSJay Kamat if (cg_create(memcg)) 1137a987785dSJay Kamat goto cleanup; 1138a987785dSJay Kamat 1139a987785dSJay Kamat if (cg_write(memcg, "memory.max", "50M")) 1140a987785dSJay Kamat goto cleanup; 1141a987785dSJay Kamat 1142a987785dSJay Kamat if (cg_write(memcg, "memory.swap.max", "0")) 1143a987785dSJay Kamat goto cleanup; 1144a987785dSJay Kamat 1145a987785dSJay Kamat if (cg_write(memcg, "memory.oom.group", "1")) 1146a987785dSJay Kamat goto cleanup; 1147a987785dSJay Kamat 1148a987785dSJay Kamat safe_pid = cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); 1149a987785dSJay Kamat if (set_oom_adj_score(safe_pid, OOM_SCORE_ADJ_MIN)) 1150a987785dSJay Kamat goto cleanup; 1151a987785dSJay Kamat 1152a987785dSJay Kamat cg_run_nowait(memcg, alloc_anon_noexit, (void *) MB(1)); 1153a987785dSJay Kamat if (!cg_run(memcg, alloc_anon, (void *)MB(100))) 1154a987785dSJay Kamat goto cleanup; 1155a987785dSJay Kamat 1156a987785dSJay Kamat if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3) 1157a987785dSJay Kamat goto cleanup; 1158a987785dSJay Kamat 1159a987785dSJay Kamat if (kill(safe_pid, SIGKILL)) 1160a987785dSJay Kamat goto cleanup; 1161a987785dSJay Kamat 1162a987785dSJay Kamat ret = KSFT_PASS; 1163a987785dSJay Kamat 1164a987785dSJay Kamat cleanup: 1165a987785dSJay Kamat if (memcg) 1166a987785dSJay Kamat cg_destroy(memcg); 1167a987785dSJay Kamat free(memcg); 1168a987785dSJay Kamat 1169a987785dSJay Kamat return ret; 1170a987785dSJay Kamat } 1171a987785dSJay Kamat 1172a987785dSJay Kamat 117384092dbcSRoman Gushchin #define T(x) { x, #x } 117484092dbcSRoman Gushchin struct memcg_test { 117584092dbcSRoman Gushchin int (*fn)(const char *root); 117684092dbcSRoman Gushchin const char *name; 117784092dbcSRoman Gushchin } tests[] = { 117884092dbcSRoman Gushchin T(test_memcg_subtree_control), 117984092dbcSRoman Gushchin T(test_memcg_current), 118084092dbcSRoman Gushchin T(test_memcg_min), 118184092dbcSRoman Gushchin T(test_memcg_low), 118284092dbcSRoman Gushchin T(test_memcg_high), 118384092dbcSRoman Gushchin T(test_memcg_max), 118484092dbcSRoman Gushchin T(test_memcg_oom_events), 1185478b2784SMike Rapoport T(test_memcg_swap_max), 11865f8f0193SMike Rapoport T(test_memcg_sock), 1187a987785dSJay Kamat T(test_memcg_oom_group_leaf_events), 1188a987785dSJay Kamat T(test_memcg_oom_group_parent_events), 1189a987785dSJay Kamat T(test_memcg_oom_group_score_events), 119084092dbcSRoman Gushchin }; 119184092dbcSRoman Gushchin #undef T 119284092dbcSRoman Gushchin 119384092dbcSRoman Gushchin int main(int argc, char **argv) 119484092dbcSRoman Gushchin { 119584092dbcSRoman Gushchin char root[PATH_MAX]; 119684092dbcSRoman Gushchin int i, ret = EXIT_SUCCESS; 119784092dbcSRoman Gushchin 119884092dbcSRoman Gushchin if (cg_find_unified_root(root, sizeof(root))) 119984092dbcSRoman Gushchin ksft_exit_skip("cgroup v2 isn't mounted\n"); 120084092dbcSRoman Gushchin 120184092dbcSRoman Gushchin /* 120284092dbcSRoman Gushchin * Check that memory controller is available: 120384092dbcSRoman Gushchin * memory is listed in cgroup.controllers 120484092dbcSRoman Gushchin */ 120584092dbcSRoman Gushchin if (cg_read_strstr(root, "cgroup.controllers", "memory")) 120684092dbcSRoman Gushchin ksft_exit_skip("memory controller isn't available\n"); 120784092dbcSRoman Gushchin 1208f6131f28SAlex Shi if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 1209f6131f28SAlex Shi if (cg_write(root, "cgroup.subtree_control", "+memory")) 1210f6131f28SAlex Shi ksft_exit_skip("Failed to set memory controller\n"); 1211f6131f28SAlex Shi 121284092dbcSRoman Gushchin for (i = 0; i < ARRAY_SIZE(tests); i++) { 121384092dbcSRoman Gushchin switch (tests[i].fn(root)) { 121484092dbcSRoman Gushchin case KSFT_PASS: 121584092dbcSRoman Gushchin ksft_test_result_pass("%s\n", tests[i].name); 121684092dbcSRoman Gushchin break; 121784092dbcSRoman Gushchin case KSFT_SKIP: 121884092dbcSRoman Gushchin ksft_test_result_skip("%s\n", tests[i].name); 121984092dbcSRoman Gushchin break; 122084092dbcSRoman Gushchin default: 122184092dbcSRoman Gushchin ret = EXIT_FAILURE; 122284092dbcSRoman Gushchin ksft_test_result_fail("%s\n", tests[i].name); 122384092dbcSRoman Gushchin break; 122484092dbcSRoman Gushchin } 122584092dbcSRoman Gushchin } 122684092dbcSRoman Gushchin 122784092dbcSRoman Gushchin return ret; 122884092dbcSRoman Gushchin } 1229