1933dc80eSRoman Gushchin // SPDX-License-Identifier: GPL-2.0 2933dc80eSRoman Gushchin #define _GNU_SOURCE 3933dc80eSRoman Gushchin 4933dc80eSRoman Gushchin #include <linux/limits.h> 5933dc80eSRoman Gushchin #include <fcntl.h> 6933dc80eSRoman Gushchin #include <stdio.h> 7933dc80eSRoman Gushchin #include <stdlib.h> 8933dc80eSRoman Gushchin #include <string.h> 9933dc80eSRoman Gushchin #include <sys/stat.h> 10933dc80eSRoman Gushchin #include <sys/types.h> 11933dc80eSRoman Gushchin #include <unistd.h> 12933dc80eSRoman Gushchin #include <sys/wait.h> 13933dc80eSRoman Gushchin #include <errno.h> 14933dc80eSRoman Gushchin #include <sys/sysinfo.h> 15933dc80eSRoman Gushchin #include <pthread.h> 16933dc80eSRoman Gushchin 17933dc80eSRoman Gushchin #include "../kselftest.h" 18933dc80eSRoman Gushchin #include "cgroup_util.h" 19933dc80eSRoman Gushchin 20933dc80eSRoman Gushchin 2190631e1dSRoman Gushchin /* 22de16d6e4SMichal Hocko * Memory cgroup charging is performed using percpu batches 64 pages 234bbcc5a4SJohannes Weiner * big (look at MEMCG_CHARGE_BATCH), whereas memory.stat is exact. So 244bbcc5a4SJohannes Weiner * the maximum discrepancy between charge and vmstat entries is number 25de16d6e4SMichal Hocko * of cpus multiplied by 64 pages. 2690631e1dSRoman Gushchin */ 27de16d6e4SMichal Hocko #define MAX_VMSTAT_ERROR (4096 * 64 * get_nprocs()) 2890631e1dSRoman Gushchin 2990631e1dSRoman Gushchin 30933dc80eSRoman Gushchin static int alloc_dcache(const char *cgroup, void *arg) 31933dc80eSRoman Gushchin { 32933dc80eSRoman Gushchin unsigned long i; 33933dc80eSRoman Gushchin struct stat st; 34933dc80eSRoman Gushchin char buf[128]; 35933dc80eSRoman Gushchin 36933dc80eSRoman Gushchin for (i = 0; i < (unsigned long)arg; i++) { 37933dc80eSRoman Gushchin snprintf(buf, sizeof(buf), 38933dc80eSRoman Gushchin "/something-non-existent-with-a-long-name-%64lu-%d", 39933dc80eSRoman Gushchin i, getpid()); 40933dc80eSRoman Gushchin stat(buf, &st); 41933dc80eSRoman Gushchin } 42933dc80eSRoman Gushchin 43933dc80eSRoman Gushchin return 0; 44933dc80eSRoman Gushchin } 45933dc80eSRoman Gushchin 46933dc80eSRoman Gushchin /* 47933dc80eSRoman Gushchin * This test allocates 100000 of negative dentries with long names. 48933dc80eSRoman Gushchin * Then it checks that "slab" in memory.stat is larger than 1M. 49933dc80eSRoman Gushchin * Then it sets memory.high to 1M and checks that at least 1/2 50933dc80eSRoman Gushchin * of slab memory has been reclaimed. 51933dc80eSRoman Gushchin */ 52933dc80eSRoman Gushchin static int test_kmem_basic(const char *root) 53933dc80eSRoman Gushchin { 54933dc80eSRoman Gushchin int ret = KSFT_FAIL; 55933dc80eSRoman Gushchin char *cg = NULL; 56933dc80eSRoman Gushchin long slab0, slab1, current; 57933dc80eSRoman Gushchin 58933dc80eSRoman Gushchin cg = cg_name(root, "kmem_basic_test"); 59933dc80eSRoman Gushchin if (!cg) 60933dc80eSRoman Gushchin goto cleanup; 61933dc80eSRoman Gushchin 62933dc80eSRoman Gushchin if (cg_create(cg)) 63933dc80eSRoman Gushchin goto cleanup; 64933dc80eSRoman Gushchin 65933dc80eSRoman Gushchin if (cg_run(cg, alloc_dcache, (void *)100000)) 66933dc80eSRoman Gushchin goto cleanup; 67933dc80eSRoman Gushchin 68933dc80eSRoman Gushchin slab0 = cg_read_key_long(cg, "memory.stat", "slab "); 69933dc80eSRoman Gushchin if (slab0 < (1 << 20)) 70933dc80eSRoman Gushchin goto cleanup; 71933dc80eSRoman Gushchin 72933dc80eSRoman Gushchin cg_write(cg, "memory.high", "1M"); 73fac26502SJohannes Weiner 74fac26502SJohannes Weiner /* wait for RCU freeing */ 75fac26502SJohannes Weiner sleep(1); 76fac26502SJohannes Weiner 77933dc80eSRoman Gushchin slab1 = cg_read_key_long(cg, "memory.stat", "slab "); 7860439471SLucas Karpinski if (slab1 < 0) 79933dc80eSRoman Gushchin goto cleanup; 80933dc80eSRoman Gushchin 81933dc80eSRoman Gushchin current = cg_read_long(cg, "memory.current"); 8260439471SLucas Karpinski if (current < 0) 83933dc80eSRoman Gushchin goto cleanup; 84933dc80eSRoman Gushchin 85933dc80eSRoman Gushchin if (slab1 < slab0 / 2 && current < slab0 / 2) 86933dc80eSRoman Gushchin ret = KSFT_PASS; 87933dc80eSRoman Gushchin cleanup: 88933dc80eSRoman Gushchin cg_destroy(cg); 89933dc80eSRoman Gushchin free(cg); 90933dc80eSRoman Gushchin 91933dc80eSRoman Gushchin return ret; 92933dc80eSRoman Gushchin } 93933dc80eSRoman Gushchin 94933dc80eSRoman Gushchin static void *alloc_kmem_fn(void *arg) 95933dc80eSRoman Gushchin { 96933dc80eSRoman Gushchin alloc_dcache(NULL, (void *)100); 97933dc80eSRoman Gushchin return NULL; 98933dc80eSRoman Gushchin } 99933dc80eSRoman Gushchin 100933dc80eSRoman Gushchin static int alloc_kmem_smp(const char *cgroup, void *arg) 101933dc80eSRoman Gushchin { 102933dc80eSRoman Gushchin int nr_threads = 2 * get_nprocs(); 103933dc80eSRoman Gushchin pthread_t *tinfo; 104933dc80eSRoman Gushchin unsigned long i; 105933dc80eSRoman Gushchin int ret = -1; 106933dc80eSRoman Gushchin 107933dc80eSRoman Gushchin tinfo = calloc(nr_threads, sizeof(pthread_t)); 108933dc80eSRoman Gushchin if (tinfo == NULL) 109933dc80eSRoman Gushchin return -1; 110933dc80eSRoman Gushchin 111933dc80eSRoman Gushchin for (i = 0; i < nr_threads; i++) { 112933dc80eSRoman Gushchin if (pthread_create(&tinfo[i], NULL, &alloc_kmem_fn, 113933dc80eSRoman Gushchin (void *)i)) { 114933dc80eSRoman Gushchin free(tinfo); 115933dc80eSRoman Gushchin return -1; 116933dc80eSRoman Gushchin } 117933dc80eSRoman Gushchin } 118933dc80eSRoman Gushchin 119933dc80eSRoman Gushchin for (i = 0; i < nr_threads; i++) { 120933dc80eSRoman Gushchin ret = pthread_join(tinfo[i], NULL); 121933dc80eSRoman Gushchin if (ret) 122933dc80eSRoman Gushchin break; 123933dc80eSRoman Gushchin } 124933dc80eSRoman Gushchin 125933dc80eSRoman Gushchin free(tinfo); 126933dc80eSRoman Gushchin return ret; 127933dc80eSRoman Gushchin } 128933dc80eSRoman Gushchin 129933dc80eSRoman Gushchin static int cg_run_in_subcgroups(const char *parent, 130933dc80eSRoman Gushchin int (*fn)(const char *cgroup, void *arg), 131933dc80eSRoman Gushchin void *arg, int times) 132933dc80eSRoman Gushchin { 133933dc80eSRoman Gushchin char *child; 134933dc80eSRoman Gushchin int i; 135933dc80eSRoman Gushchin 136933dc80eSRoman Gushchin for (i = 0; i < times; i++) { 137933dc80eSRoman Gushchin child = cg_name_indexed(parent, "child", i); 138933dc80eSRoman Gushchin if (!child) 139933dc80eSRoman Gushchin return -1; 140933dc80eSRoman Gushchin 141933dc80eSRoman Gushchin if (cg_create(child)) { 142933dc80eSRoman Gushchin cg_destroy(child); 143933dc80eSRoman Gushchin free(child); 144933dc80eSRoman Gushchin return -1; 145933dc80eSRoman Gushchin } 146933dc80eSRoman Gushchin 147933dc80eSRoman Gushchin if (cg_run(child, fn, NULL)) { 148933dc80eSRoman Gushchin cg_destroy(child); 149933dc80eSRoman Gushchin free(child); 150933dc80eSRoman Gushchin return -1; 151933dc80eSRoman Gushchin } 152933dc80eSRoman Gushchin 153933dc80eSRoman Gushchin cg_destroy(child); 154933dc80eSRoman Gushchin free(child); 155933dc80eSRoman Gushchin } 156933dc80eSRoman Gushchin 157933dc80eSRoman Gushchin return 0; 158933dc80eSRoman Gushchin } 159933dc80eSRoman Gushchin 160933dc80eSRoman Gushchin /* 161933dc80eSRoman Gushchin * The test creates and destroys a large number of cgroups. In each cgroup it 162933dc80eSRoman Gushchin * allocates some slab memory (mostly negative dentries) using 2 * NR_CPUS 163933dc80eSRoman Gushchin * threads. Then it checks the sanity of numbers on the parent level: 164933dc80eSRoman Gushchin * the total size of the cgroups should be roughly equal to 1657131fd7eSLucas Karpinski * anon + file + kernel + sock. 166933dc80eSRoman Gushchin */ 167933dc80eSRoman Gushchin static int test_kmem_memcg_deletion(const char *root) 168933dc80eSRoman Gushchin { 1697131fd7eSLucas Karpinski long current, anon, file, kernel, sock, sum; 170933dc80eSRoman Gushchin int ret = KSFT_FAIL; 171933dc80eSRoman Gushchin char *parent; 172933dc80eSRoman Gushchin 173933dc80eSRoman Gushchin parent = cg_name(root, "kmem_memcg_deletion_test"); 174933dc80eSRoman Gushchin if (!parent) 175933dc80eSRoman Gushchin goto cleanup; 176933dc80eSRoman Gushchin 177933dc80eSRoman Gushchin if (cg_create(parent)) 178933dc80eSRoman Gushchin goto cleanup; 179933dc80eSRoman Gushchin 180933dc80eSRoman Gushchin if (cg_write(parent, "cgroup.subtree_control", "+memory")) 181933dc80eSRoman Gushchin goto cleanup; 182933dc80eSRoman Gushchin 183933dc80eSRoman Gushchin if (cg_run_in_subcgroups(parent, alloc_kmem_smp, NULL, 100)) 184933dc80eSRoman Gushchin goto cleanup; 185933dc80eSRoman Gushchin 186933dc80eSRoman Gushchin current = cg_read_long(parent, "memory.current"); 187933dc80eSRoman Gushchin anon = cg_read_key_long(parent, "memory.stat", "anon "); 188933dc80eSRoman Gushchin file = cg_read_key_long(parent, "memory.stat", "file "); 1897131fd7eSLucas Karpinski kernel = cg_read_key_long(parent, "memory.stat", "kernel "); 1904bbcc5a4SJohannes Weiner sock = cg_read_key_long(parent, "memory.stat", "sock "); 1917131fd7eSLucas Karpinski if (current < 0 || anon < 0 || file < 0 || kernel < 0 || sock < 0) 192933dc80eSRoman Gushchin goto cleanup; 193933dc80eSRoman Gushchin 1947131fd7eSLucas Karpinski sum = anon + file + kernel + sock; 19590631e1dSRoman Gushchin if (abs(sum - current) < MAX_VMSTAT_ERROR) { 196933dc80eSRoman Gushchin ret = KSFT_PASS; 197933dc80eSRoman Gushchin } else { 198933dc80eSRoman Gushchin printf("memory.current = %ld\n", current); 1997131fd7eSLucas Karpinski printf("anon + file + kernel + sock = %ld\n", sum); 200933dc80eSRoman Gushchin printf("anon = %ld\n", anon); 201933dc80eSRoman Gushchin printf("file = %ld\n", file); 2027131fd7eSLucas Karpinski printf("kernel = %ld\n", kernel); 2034bbcc5a4SJohannes Weiner printf("sock = %ld\n", sock); 204933dc80eSRoman Gushchin } 205933dc80eSRoman Gushchin 206933dc80eSRoman Gushchin cleanup: 207933dc80eSRoman Gushchin cg_destroy(parent); 208933dc80eSRoman Gushchin free(parent); 209933dc80eSRoman Gushchin 210933dc80eSRoman Gushchin return ret; 211933dc80eSRoman Gushchin } 212933dc80eSRoman Gushchin 213933dc80eSRoman Gushchin /* 214933dc80eSRoman Gushchin * The test reads the entire /proc/kpagecgroup. If the operation went 215933dc80eSRoman Gushchin * successfully (and the kernel didn't panic), the test is treated as passed. 216933dc80eSRoman Gushchin */ 217933dc80eSRoman Gushchin static int test_kmem_proc_kpagecgroup(const char *root) 218933dc80eSRoman Gushchin { 219933dc80eSRoman Gushchin unsigned long buf[128]; 220933dc80eSRoman Gushchin int ret = KSFT_FAIL; 221933dc80eSRoman Gushchin ssize_t len; 222933dc80eSRoman Gushchin int fd; 223933dc80eSRoman Gushchin 224933dc80eSRoman Gushchin fd = open("/proc/kpagecgroup", O_RDONLY); 225933dc80eSRoman Gushchin if (fd < 0) 226933dc80eSRoman Gushchin return ret; 227933dc80eSRoman Gushchin 228933dc80eSRoman Gushchin do { 229933dc80eSRoman Gushchin len = read(fd, buf, sizeof(buf)); 230933dc80eSRoman Gushchin } while (len > 0); 231933dc80eSRoman Gushchin 232933dc80eSRoman Gushchin if (len == 0) 233933dc80eSRoman Gushchin ret = KSFT_PASS; 234933dc80eSRoman Gushchin 235933dc80eSRoman Gushchin close(fd); 236933dc80eSRoman Gushchin return ret; 237933dc80eSRoman Gushchin } 238933dc80eSRoman Gushchin 239933dc80eSRoman Gushchin static void *pthread_wait_fn(void *arg) 240933dc80eSRoman Gushchin { 241933dc80eSRoman Gushchin sleep(100); 242933dc80eSRoman Gushchin return NULL; 243933dc80eSRoman Gushchin } 244933dc80eSRoman Gushchin 245933dc80eSRoman Gushchin static int spawn_1000_threads(const char *cgroup, void *arg) 246933dc80eSRoman Gushchin { 247933dc80eSRoman Gushchin int nr_threads = 1000; 248933dc80eSRoman Gushchin pthread_t *tinfo; 249933dc80eSRoman Gushchin unsigned long i; 250933dc80eSRoman Gushchin long stack; 251933dc80eSRoman Gushchin int ret = -1; 252933dc80eSRoman Gushchin 253933dc80eSRoman Gushchin tinfo = calloc(nr_threads, sizeof(pthread_t)); 254933dc80eSRoman Gushchin if (tinfo == NULL) 255933dc80eSRoman Gushchin return -1; 256933dc80eSRoman Gushchin 257933dc80eSRoman Gushchin for (i = 0; i < nr_threads; i++) { 258933dc80eSRoman Gushchin if (pthread_create(&tinfo[i], NULL, &pthread_wait_fn, 259933dc80eSRoman Gushchin (void *)i)) { 260933dc80eSRoman Gushchin free(tinfo); 261933dc80eSRoman Gushchin return(-1); 262933dc80eSRoman Gushchin } 263933dc80eSRoman Gushchin } 264933dc80eSRoman Gushchin 265933dc80eSRoman Gushchin stack = cg_read_key_long(cgroup, "memory.stat", "kernel_stack "); 266933dc80eSRoman Gushchin if (stack >= 4096 * 1000) 267933dc80eSRoman Gushchin ret = 0; 268933dc80eSRoman Gushchin 269933dc80eSRoman Gushchin free(tinfo); 270933dc80eSRoman Gushchin return ret; 271933dc80eSRoman Gushchin } 272933dc80eSRoman Gushchin 273933dc80eSRoman Gushchin /* 274933dc80eSRoman Gushchin * The test spawns a process, which spawns 1000 threads. Then it checks 275933dc80eSRoman Gushchin * that memory.stat's kernel_stack is at least 1000 pages large. 276933dc80eSRoman Gushchin */ 277933dc80eSRoman Gushchin static int test_kmem_kernel_stacks(const char *root) 278933dc80eSRoman Gushchin { 279933dc80eSRoman Gushchin int ret = KSFT_FAIL; 280933dc80eSRoman Gushchin char *cg = NULL; 281933dc80eSRoman Gushchin 282933dc80eSRoman Gushchin cg = cg_name(root, "kmem_kernel_stacks_test"); 283933dc80eSRoman Gushchin if (!cg) 284933dc80eSRoman Gushchin goto cleanup; 285933dc80eSRoman Gushchin 286933dc80eSRoman Gushchin if (cg_create(cg)) 287933dc80eSRoman Gushchin goto cleanup; 288933dc80eSRoman Gushchin 289933dc80eSRoman Gushchin if (cg_run(cg, spawn_1000_threads, NULL)) 290933dc80eSRoman Gushchin goto cleanup; 291933dc80eSRoman Gushchin 292933dc80eSRoman Gushchin ret = KSFT_PASS; 293933dc80eSRoman Gushchin cleanup: 294933dc80eSRoman Gushchin cg_destroy(cg); 295933dc80eSRoman Gushchin free(cg); 296933dc80eSRoman Gushchin 297933dc80eSRoman Gushchin return ret; 298933dc80eSRoman Gushchin } 299933dc80eSRoman Gushchin 300933dc80eSRoman Gushchin /* 301933dc80eSRoman Gushchin * This test sequentionally creates 30 child cgroups, allocates some 302933dc80eSRoman Gushchin * kernel memory in each of them, and deletes them. Then it checks 303933dc80eSRoman Gushchin * that the number of dying cgroups on the parent level is 0. 304933dc80eSRoman Gushchin */ 305933dc80eSRoman Gushchin static int test_kmem_dead_cgroups(const char *root) 306933dc80eSRoman Gushchin { 307933dc80eSRoman Gushchin int ret = KSFT_FAIL; 308933dc80eSRoman Gushchin char *parent; 309933dc80eSRoman Gushchin long dead; 310933dc80eSRoman Gushchin int i; 311933dc80eSRoman Gushchin 312933dc80eSRoman Gushchin parent = cg_name(root, "kmem_dead_cgroups_test"); 313933dc80eSRoman Gushchin if (!parent) 314933dc80eSRoman Gushchin goto cleanup; 315933dc80eSRoman Gushchin 316933dc80eSRoman Gushchin if (cg_create(parent)) 317933dc80eSRoman Gushchin goto cleanup; 318933dc80eSRoman Gushchin 319933dc80eSRoman Gushchin if (cg_write(parent, "cgroup.subtree_control", "+memory")) 320933dc80eSRoman Gushchin goto cleanup; 321933dc80eSRoman Gushchin 322933dc80eSRoman Gushchin if (cg_run_in_subcgroups(parent, alloc_dcache, (void *)100, 30)) 323933dc80eSRoman Gushchin goto cleanup; 324933dc80eSRoman Gushchin 325933dc80eSRoman Gushchin for (i = 0; i < 5; i++) { 326933dc80eSRoman Gushchin dead = cg_read_key_long(parent, "cgroup.stat", 327933dc80eSRoman Gushchin "nr_dying_descendants "); 328933dc80eSRoman Gushchin if (dead == 0) { 329933dc80eSRoman Gushchin ret = KSFT_PASS; 330933dc80eSRoman Gushchin break; 331933dc80eSRoman Gushchin } 332933dc80eSRoman Gushchin /* 333933dc80eSRoman Gushchin * Reclaiming cgroups might take some time, 334933dc80eSRoman Gushchin * let's wait a bit and repeat. 335933dc80eSRoman Gushchin */ 336933dc80eSRoman Gushchin sleep(1); 337933dc80eSRoman Gushchin } 338933dc80eSRoman Gushchin 339933dc80eSRoman Gushchin cleanup: 340933dc80eSRoman Gushchin cg_destroy(parent); 341933dc80eSRoman Gushchin free(parent); 342933dc80eSRoman Gushchin 343933dc80eSRoman Gushchin return ret; 344933dc80eSRoman Gushchin } 345933dc80eSRoman Gushchin 34690631e1dSRoman Gushchin /* 34790631e1dSRoman Gushchin * This test creates a sub-tree with 1000 memory cgroups. 34890631e1dSRoman Gushchin * Then it checks that the memory.current on the parent level 34990631e1dSRoman Gushchin * is greater than 0 and approximates matches the percpu value 35090631e1dSRoman Gushchin * from memory.stat. 35190631e1dSRoman Gushchin */ 35290631e1dSRoman Gushchin static int test_percpu_basic(const char *root) 35390631e1dSRoman Gushchin { 35490631e1dSRoman Gushchin int ret = KSFT_FAIL; 35590631e1dSRoman Gushchin char *parent, *child; 35690631e1dSRoman Gushchin long current, percpu; 35790631e1dSRoman Gushchin int i; 35890631e1dSRoman Gushchin 35990631e1dSRoman Gushchin parent = cg_name(root, "percpu_basic_test"); 36090631e1dSRoman Gushchin if (!parent) 36190631e1dSRoman Gushchin goto cleanup; 36290631e1dSRoman Gushchin 36390631e1dSRoman Gushchin if (cg_create(parent)) 36490631e1dSRoman Gushchin goto cleanup; 36590631e1dSRoman Gushchin 36690631e1dSRoman Gushchin if (cg_write(parent, "cgroup.subtree_control", "+memory")) 36790631e1dSRoman Gushchin goto cleanup; 36890631e1dSRoman Gushchin 36990631e1dSRoman Gushchin for (i = 0; i < 1000; i++) { 37090631e1dSRoman Gushchin child = cg_name_indexed(parent, "child", i); 37190631e1dSRoman Gushchin if (!child) 37290631e1dSRoman Gushchin return -1; 37390631e1dSRoman Gushchin 37490631e1dSRoman Gushchin if (cg_create(child)) 37590631e1dSRoman Gushchin goto cleanup_children; 37690631e1dSRoman Gushchin 37790631e1dSRoman Gushchin free(child); 37890631e1dSRoman Gushchin } 37990631e1dSRoman Gushchin 38090631e1dSRoman Gushchin current = cg_read_long(parent, "memory.current"); 38190631e1dSRoman Gushchin percpu = cg_read_key_long(parent, "memory.stat", "percpu "); 38290631e1dSRoman Gushchin 38390631e1dSRoman Gushchin if (current > 0 && percpu > 0 && abs(current - percpu) < 38490631e1dSRoman Gushchin MAX_VMSTAT_ERROR) 38590631e1dSRoman Gushchin ret = KSFT_PASS; 38690631e1dSRoman Gushchin else 38790631e1dSRoman Gushchin printf("memory.current %ld\npercpu %ld\n", 38890631e1dSRoman Gushchin current, percpu); 38990631e1dSRoman Gushchin 39090631e1dSRoman Gushchin cleanup_children: 39190631e1dSRoman Gushchin for (i = 0; i < 1000; i++) { 39290631e1dSRoman Gushchin child = cg_name_indexed(parent, "child", i); 39390631e1dSRoman Gushchin cg_destroy(child); 39490631e1dSRoman Gushchin free(child); 39590631e1dSRoman Gushchin } 39690631e1dSRoman Gushchin 39790631e1dSRoman Gushchin cleanup: 39890631e1dSRoman Gushchin cg_destroy(parent); 39990631e1dSRoman Gushchin free(parent); 40090631e1dSRoman Gushchin 40190631e1dSRoman Gushchin return ret; 40290631e1dSRoman Gushchin } 40390631e1dSRoman Gushchin 404933dc80eSRoman Gushchin #define T(x) { x, #x } 405933dc80eSRoman Gushchin struct kmem_test { 406933dc80eSRoman Gushchin int (*fn)(const char *root); 407933dc80eSRoman Gushchin const char *name; 408933dc80eSRoman Gushchin } tests[] = { 409933dc80eSRoman Gushchin T(test_kmem_basic), 410933dc80eSRoman Gushchin T(test_kmem_memcg_deletion), 411933dc80eSRoman Gushchin T(test_kmem_proc_kpagecgroup), 412933dc80eSRoman Gushchin T(test_kmem_kernel_stacks), 413933dc80eSRoman Gushchin T(test_kmem_dead_cgroups), 41490631e1dSRoman Gushchin T(test_percpu_basic), 415933dc80eSRoman Gushchin }; 416933dc80eSRoman Gushchin #undef T 417933dc80eSRoman Gushchin 418933dc80eSRoman Gushchin int main(int argc, char **argv) 419933dc80eSRoman Gushchin { 420933dc80eSRoman Gushchin char root[PATH_MAX]; 421933dc80eSRoman Gushchin int i, ret = EXIT_SUCCESS; 422933dc80eSRoman Gushchin 423933dc80eSRoman Gushchin if (cg_find_unified_root(root, sizeof(root))) 424933dc80eSRoman Gushchin ksft_exit_skip("cgroup v2 isn't mounted\n"); 425933dc80eSRoman Gushchin 426933dc80eSRoman Gushchin /* 427933dc80eSRoman Gushchin * Check that memory controller is available: 428933dc80eSRoman Gushchin * memory is listed in cgroup.controllers 429933dc80eSRoman Gushchin */ 430933dc80eSRoman Gushchin if (cg_read_strstr(root, "cgroup.controllers", "memory")) 431933dc80eSRoman Gushchin ksft_exit_skip("memory controller isn't available\n"); 432933dc80eSRoman Gushchin 433933dc80eSRoman Gushchin if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 434933dc80eSRoman Gushchin if (cg_write(root, "cgroup.subtree_control", "+memory")) 435933dc80eSRoman Gushchin ksft_exit_skip("Failed to set memory controller\n"); 436933dc80eSRoman Gushchin 437933dc80eSRoman Gushchin for (i = 0; i < ARRAY_SIZE(tests); i++) { 438933dc80eSRoman Gushchin switch (tests[i].fn(root)) { 439933dc80eSRoman Gushchin case KSFT_PASS: 440933dc80eSRoman Gushchin ksft_test_result_pass("%s\n", tests[i].name); 441933dc80eSRoman Gushchin break; 442933dc80eSRoman Gushchin case KSFT_SKIP: 443933dc80eSRoman Gushchin ksft_test_result_skip("%s\n", tests[i].name); 444933dc80eSRoman Gushchin break; 445933dc80eSRoman Gushchin default: 446933dc80eSRoman Gushchin ret = EXIT_FAILURE; 447933dc80eSRoman Gushchin ksft_test_result_fail("%s\n", tests[i].name); 448933dc80eSRoman Gushchin break; 449933dc80eSRoman Gushchin } 450933dc80eSRoman Gushchin } 451933dc80eSRoman Gushchin 452933dc80eSRoman Gushchin return ret; 453933dc80eSRoman Gushchin } 454