18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#define _GNU_SOURCE 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/limits.h> 58c2ecf20Sopenharmony_ci#include <fcntl.h> 68c2ecf20Sopenharmony_ci#include <stdio.h> 78c2ecf20Sopenharmony_ci#include <stdlib.h> 88c2ecf20Sopenharmony_ci#include <string.h> 98c2ecf20Sopenharmony_ci#include <sys/stat.h> 108c2ecf20Sopenharmony_ci#include <sys/types.h> 118c2ecf20Sopenharmony_ci#include <unistd.h> 128c2ecf20Sopenharmony_ci#include <sys/wait.h> 138c2ecf20Sopenharmony_ci#include <errno.h> 148c2ecf20Sopenharmony_ci#include <sys/sysinfo.h> 158c2ecf20Sopenharmony_ci#include <pthread.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "../kselftest.h" 188c2ecf20Sopenharmony_ci#include "cgroup_util.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Memory cgroup charging and vmstat data aggregation is performed using 238c2ecf20Sopenharmony_ci * percpu batches 32 pages big (look at MEMCG_CHARGE_BATCH). So the maximum 248c2ecf20Sopenharmony_ci * discrepancy between charge and vmstat entries is number of cpus multiplied 258c2ecf20Sopenharmony_ci * by 32 pages multiplied by 2. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci#define MAX_VMSTAT_ERROR (4096 * 32 * 2 * get_nprocs()) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int alloc_dcache(const char *cgroup, void *arg) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci unsigned long i; 338c2ecf20Sopenharmony_ci struct stat st; 348c2ecf20Sopenharmony_ci char buf[128]; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci for (i = 0; i < (unsigned long)arg; i++) { 378c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), 388c2ecf20Sopenharmony_ci "/something-non-existent-with-a-long-name-%64lu-%d", 398c2ecf20Sopenharmony_ci i, getpid()); 408c2ecf20Sopenharmony_ci stat(buf, &st); 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * This test allocates 100000 of negative dentries with long names. 488c2ecf20Sopenharmony_ci * Then it checks that "slab" in memory.stat is larger than 1M. 498c2ecf20Sopenharmony_ci * Then it sets memory.high to 1M and checks that at least 1/2 508c2ecf20Sopenharmony_ci * of slab memory has been reclaimed. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic int test_kmem_basic(const char *root) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci int ret = KSFT_FAIL; 558c2ecf20Sopenharmony_ci char *cg = NULL; 568c2ecf20Sopenharmony_ci long slab0, slab1, current; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci cg = cg_name(root, "kmem_basic_test"); 598c2ecf20Sopenharmony_ci if (!cg) 608c2ecf20Sopenharmony_ci goto cleanup; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (cg_create(cg)) 638c2ecf20Sopenharmony_ci goto cleanup; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (cg_run(cg, alloc_dcache, (void *)100000)) 668c2ecf20Sopenharmony_ci goto cleanup; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci slab0 = cg_read_key_long(cg, "memory.stat", "slab "); 698c2ecf20Sopenharmony_ci if (slab0 < (1 << 20)) 708c2ecf20Sopenharmony_ci goto cleanup; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci cg_write(cg, "memory.high", "1M"); 738c2ecf20Sopenharmony_ci slab1 = cg_read_key_long(cg, "memory.stat", "slab "); 748c2ecf20Sopenharmony_ci if (slab1 <= 0) 758c2ecf20Sopenharmony_ci goto cleanup; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci current = cg_read_long(cg, "memory.current"); 788c2ecf20Sopenharmony_ci if (current <= 0) 798c2ecf20Sopenharmony_ci goto cleanup; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (slab1 < slab0 / 2 && current < slab0 / 2) 828c2ecf20Sopenharmony_ci ret = KSFT_PASS; 838c2ecf20Sopenharmony_cicleanup: 848c2ecf20Sopenharmony_ci cg_destroy(cg); 858c2ecf20Sopenharmony_ci free(cg); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void *alloc_kmem_fn(void *arg) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci alloc_dcache(NULL, (void *)100); 938c2ecf20Sopenharmony_ci return NULL; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int alloc_kmem_smp(const char *cgroup, void *arg) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int nr_threads = 2 * get_nprocs(); 998c2ecf20Sopenharmony_ci pthread_t *tinfo; 1008c2ecf20Sopenharmony_ci unsigned long i; 1018c2ecf20Sopenharmony_ci int ret = -1; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci tinfo = calloc(nr_threads, sizeof(pthread_t)); 1048c2ecf20Sopenharmony_ci if (tinfo == NULL) 1058c2ecf20Sopenharmony_ci return -1; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci for (i = 0; i < nr_threads; i++) { 1088c2ecf20Sopenharmony_ci if (pthread_create(&tinfo[i], NULL, &alloc_kmem_fn, 1098c2ecf20Sopenharmony_ci (void *)i)) { 1108c2ecf20Sopenharmony_ci free(tinfo); 1118c2ecf20Sopenharmony_ci return -1; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci for (i = 0; i < nr_threads; i++) { 1168c2ecf20Sopenharmony_ci ret = pthread_join(tinfo[i], NULL); 1178c2ecf20Sopenharmony_ci if (ret) 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci free(tinfo); 1228c2ecf20Sopenharmony_ci return ret; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int cg_run_in_subcgroups(const char *parent, 1268c2ecf20Sopenharmony_ci int (*fn)(const char *cgroup, void *arg), 1278c2ecf20Sopenharmony_ci void *arg, int times) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci char *child; 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (i = 0; i < times; i++) { 1338c2ecf20Sopenharmony_ci child = cg_name_indexed(parent, "child", i); 1348c2ecf20Sopenharmony_ci if (!child) 1358c2ecf20Sopenharmony_ci return -1; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (cg_create(child)) { 1388c2ecf20Sopenharmony_ci cg_destroy(child); 1398c2ecf20Sopenharmony_ci free(child); 1408c2ecf20Sopenharmony_ci return -1; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (cg_run(child, fn, NULL)) { 1448c2ecf20Sopenharmony_ci cg_destroy(child); 1458c2ecf20Sopenharmony_ci free(child); 1468c2ecf20Sopenharmony_ci return -1; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci cg_destroy(child); 1508c2ecf20Sopenharmony_ci free(child); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * The test creates and destroys a large number of cgroups. In each cgroup it 1588c2ecf20Sopenharmony_ci * allocates some slab memory (mostly negative dentries) using 2 * NR_CPUS 1598c2ecf20Sopenharmony_ci * threads. Then it checks the sanity of numbers on the parent level: 1608c2ecf20Sopenharmony_ci * the total size of the cgroups should be roughly equal to 1618c2ecf20Sopenharmony_ci * anon + file + slab + kernel_stack. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic int test_kmem_memcg_deletion(const char *root) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci long current, slab, anon, file, kernel_stack, sum; 1668c2ecf20Sopenharmony_ci int ret = KSFT_FAIL; 1678c2ecf20Sopenharmony_ci char *parent; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci parent = cg_name(root, "kmem_memcg_deletion_test"); 1708c2ecf20Sopenharmony_ci if (!parent) 1718c2ecf20Sopenharmony_ci goto cleanup; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (cg_create(parent)) 1748c2ecf20Sopenharmony_ci goto cleanup; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (cg_write(parent, "cgroup.subtree_control", "+memory")) 1778c2ecf20Sopenharmony_ci goto cleanup; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (cg_run_in_subcgroups(parent, alloc_kmem_smp, NULL, 100)) 1808c2ecf20Sopenharmony_ci goto cleanup; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci current = cg_read_long(parent, "memory.current"); 1838c2ecf20Sopenharmony_ci slab = cg_read_key_long(parent, "memory.stat", "slab "); 1848c2ecf20Sopenharmony_ci anon = cg_read_key_long(parent, "memory.stat", "anon "); 1858c2ecf20Sopenharmony_ci file = cg_read_key_long(parent, "memory.stat", "file "); 1868c2ecf20Sopenharmony_ci kernel_stack = cg_read_key_long(parent, "memory.stat", "kernel_stack "); 1878c2ecf20Sopenharmony_ci if (current < 0 || slab < 0 || anon < 0 || file < 0 || 1888c2ecf20Sopenharmony_ci kernel_stack < 0) 1898c2ecf20Sopenharmony_ci goto cleanup; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci sum = slab + anon + file + kernel_stack; 1928c2ecf20Sopenharmony_ci if (abs(sum - current) < MAX_VMSTAT_ERROR) { 1938c2ecf20Sopenharmony_ci ret = KSFT_PASS; 1948c2ecf20Sopenharmony_ci } else { 1958c2ecf20Sopenharmony_ci printf("memory.current = %ld\n", current); 1968c2ecf20Sopenharmony_ci printf("slab + anon + file + kernel_stack = %ld\n", sum); 1978c2ecf20Sopenharmony_ci printf("slab = %ld\n", slab); 1988c2ecf20Sopenharmony_ci printf("anon = %ld\n", anon); 1998c2ecf20Sopenharmony_ci printf("file = %ld\n", file); 2008c2ecf20Sopenharmony_ci printf("kernel_stack = %ld\n", kernel_stack); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cicleanup: 2048c2ecf20Sopenharmony_ci cg_destroy(parent); 2058c2ecf20Sopenharmony_ci free(parent); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* 2118c2ecf20Sopenharmony_ci * The test reads the entire /proc/kpagecgroup. If the operation went 2128c2ecf20Sopenharmony_ci * successfully (and the kernel didn't panic), the test is treated as passed. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic int test_kmem_proc_kpagecgroup(const char *root) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci unsigned long buf[128]; 2178c2ecf20Sopenharmony_ci int ret = KSFT_FAIL; 2188c2ecf20Sopenharmony_ci ssize_t len; 2198c2ecf20Sopenharmony_ci int fd; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci fd = open("/proc/kpagecgroup", O_RDONLY); 2228c2ecf20Sopenharmony_ci if (fd < 0) 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci do { 2268c2ecf20Sopenharmony_ci len = read(fd, buf, sizeof(buf)); 2278c2ecf20Sopenharmony_ci } while (len > 0); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (len == 0) 2308c2ecf20Sopenharmony_ci ret = KSFT_PASS; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci close(fd); 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void *pthread_wait_fn(void *arg) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci sleep(100); 2398c2ecf20Sopenharmony_ci return NULL; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int spawn_1000_threads(const char *cgroup, void *arg) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int nr_threads = 1000; 2458c2ecf20Sopenharmony_ci pthread_t *tinfo; 2468c2ecf20Sopenharmony_ci unsigned long i; 2478c2ecf20Sopenharmony_ci long stack; 2488c2ecf20Sopenharmony_ci int ret = -1; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci tinfo = calloc(nr_threads, sizeof(pthread_t)); 2518c2ecf20Sopenharmony_ci if (tinfo == NULL) 2528c2ecf20Sopenharmony_ci return -1; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci for (i = 0; i < nr_threads; i++) { 2558c2ecf20Sopenharmony_ci if (pthread_create(&tinfo[i], NULL, &pthread_wait_fn, 2568c2ecf20Sopenharmony_ci (void *)i)) { 2578c2ecf20Sopenharmony_ci free(tinfo); 2588c2ecf20Sopenharmony_ci return(-1); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci stack = cg_read_key_long(cgroup, "memory.stat", "kernel_stack "); 2638c2ecf20Sopenharmony_ci if (stack >= 4096 * 1000) 2648c2ecf20Sopenharmony_ci ret = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci free(tinfo); 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* 2718c2ecf20Sopenharmony_ci * The test spawns a process, which spawns 1000 threads. Then it checks 2728c2ecf20Sopenharmony_ci * that memory.stat's kernel_stack is at least 1000 pages large. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_cistatic int test_kmem_kernel_stacks(const char *root) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci int ret = KSFT_FAIL; 2778c2ecf20Sopenharmony_ci char *cg = NULL; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci cg = cg_name(root, "kmem_kernel_stacks_test"); 2808c2ecf20Sopenharmony_ci if (!cg) 2818c2ecf20Sopenharmony_ci goto cleanup; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (cg_create(cg)) 2848c2ecf20Sopenharmony_ci goto cleanup; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (cg_run(cg, spawn_1000_threads, NULL)) 2878c2ecf20Sopenharmony_ci goto cleanup; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ret = KSFT_PASS; 2908c2ecf20Sopenharmony_cicleanup: 2918c2ecf20Sopenharmony_ci cg_destroy(cg); 2928c2ecf20Sopenharmony_ci free(cg); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * This test sequentionally creates 30 child cgroups, allocates some 2998c2ecf20Sopenharmony_ci * kernel memory in each of them, and deletes them. Then it checks 3008c2ecf20Sopenharmony_ci * that the number of dying cgroups on the parent level is 0. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic int test_kmem_dead_cgroups(const char *root) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int ret = KSFT_FAIL; 3058c2ecf20Sopenharmony_ci char *parent; 3068c2ecf20Sopenharmony_ci long dead; 3078c2ecf20Sopenharmony_ci int i; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci parent = cg_name(root, "kmem_dead_cgroups_test"); 3108c2ecf20Sopenharmony_ci if (!parent) 3118c2ecf20Sopenharmony_ci goto cleanup; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (cg_create(parent)) 3148c2ecf20Sopenharmony_ci goto cleanup; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (cg_write(parent, "cgroup.subtree_control", "+memory")) 3178c2ecf20Sopenharmony_ci goto cleanup; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (cg_run_in_subcgroups(parent, alloc_dcache, (void *)100, 30)) 3208c2ecf20Sopenharmony_ci goto cleanup; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 3238c2ecf20Sopenharmony_ci dead = cg_read_key_long(parent, "cgroup.stat", 3248c2ecf20Sopenharmony_ci "nr_dying_descendants "); 3258c2ecf20Sopenharmony_ci if (dead == 0) { 3268c2ecf20Sopenharmony_ci ret = KSFT_PASS; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * Reclaiming cgroups might take some time, 3318c2ecf20Sopenharmony_ci * let's wait a bit and repeat. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci sleep(1); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cicleanup: 3378c2ecf20Sopenharmony_ci cg_destroy(parent); 3388c2ecf20Sopenharmony_ci free(parent); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* 3448c2ecf20Sopenharmony_ci * This test creates a sub-tree with 1000 memory cgroups. 3458c2ecf20Sopenharmony_ci * Then it checks that the memory.current on the parent level 3468c2ecf20Sopenharmony_ci * is greater than 0 and approximates matches the percpu value 3478c2ecf20Sopenharmony_ci * from memory.stat. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic int test_percpu_basic(const char *root) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci int ret = KSFT_FAIL; 3528c2ecf20Sopenharmony_ci char *parent, *child; 3538c2ecf20Sopenharmony_ci long current, percpu; 3548c2ecf20Sopenharmony_ci int i; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci parent = cg_name(root, "percpu_basic_test"); 3578c2ecf20Sopenharmony_ci if (!parent) 3588c2ecf20Sopenharmony_ci goto cleanup; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (cg_create(parent)) 3618c2ecf20Sopenharmony_ci goto cleanup; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (cg_write(parent, "cgroup.subtree_control", "+memory")) 3648c2ecf20Sopenharmony_ci goto cleanup; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 3678c2ecf20Sopenharmony_ci child = cg_name_indexed(parent, "child", i); 3688c2ecf20Sopenharmony_ci if (!child) 3698c2ecf20Sopenharmony_ci return -1; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (cg_create(child)) 3728c2ecf20Sopenharmony_ci goto cleanup_children; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci free(child); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci current = cg_read_long(parent, "memory.current"); 3788c2ecf20Sopenharmony_ci percpu = cg_read_key_long(parent, "memory.stat", "percpu "); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (current > 0 && percpu > 0 && abs(current - percpu) < 3818c2ecf20Sopenharmony_ci MAX_VMSTAT_ERROR) 3828c2ecf20Sopenharmony_ci ret = KSFT_PASS; 3838c2ecf20Sopenharmony_ci else 3848c2ecf20Sopenharmony_ci printf("memory.current %ld\npercpu %ld\n", 3858c2ecf20Sopenharmony_ci current, percpu); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cicleanup_children: 3888c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 3898c2ecf20Sopenharmony_ci child = cg_name_indexed(parent, "child", i); 3908c2ecf20Sopenharmony_ci cg_destroy(child); 3918c2ecf20Sopenharmony_ci free(child); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cicleanup: 3958c2ecf20Sopenharmony_ci cg_destroy(parent); 3968c2ecf20Sopenharmony_ci free(parent); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci#define T(x) { x, #x } 4028c2ecf20Sopenharmony_cistruct kmem_test { 4038c2ecf20Sopenharmony_ci int (*fn)(const char *root); 4048c2ecf20Sopenharmony_ci const char *name; 4058c2ecf20Sopenharmony_ci} tests[] = { 4068c2ecf20Sopenharmony_ci T(test_kmem_basic), 4078c2ecf20Sopenharmony_ci T(test_kmem_memcg_deletion), 4088c2ecf20Sopenharmony_ci T(test_kmem_proc_kpagecgroup), 4098c2ecf20Sopenharmony_ci T(test_kmem_kernel_stacks), 4108c2ecf20Sopenharmony_ci T(test_kmem_dead_cgroups), 4118c2ecf20Sopenharmony_ci T(test_percpu_basic), 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci#undef T 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciint main(int argc, char **argv) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci char root[PATH_MAX]; 4188c2ecf20Sopenharmony_ci int i, ret = EXIT_SUCCESS; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (cg_find_unified_root(root, sizeof(root))) 4218c2ecf20Sopenharmony_ci ksft_exit_skip("cgroup v2 isn't mounted\n"); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* 4248c2ecf20Sopenharmony_ci * Check that memory controller is available: 4258c2ecf20Sopenharmony_ci * memory is listed in cgroup.controllers 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci if (cg_read_strstr(root, "cgroup.controllers", "memory")) 4288c2ecf20Sopenharmony_ci ksft_exit_skip("memory controller isn't available\n"); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 4318c2ecf20Sopenharmony_ci if (cg_write(root, "cgroup.subtree_control", "+memory")) 4328c2ecf20Sopenharmony_ci ksft_exit_skip("Failed to set memory controller\n"); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tests); i++) { 4358c2ecf20Sopenharmony_ci switch (tests[i].fn(root)) { 4368c2ecf20Sopenharmony_ci case KSFT_PASS: 4378c2ecf20Sopenharmony_ci ksft_test_result_pass("%s\n", tests[i].name); 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci case KSFT_SKIP: 4408c2ecf20Sopenharmony_ci ksft_test_result_skip("%s\n", tests[i].name); 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci default: 4438c2ecf20Sopenharmony_ci ret = EXIT_FAILURE; 4448c2ecf20Sopenharmony_ci ksft_test_result_fail("%s\n", tests[i].name); 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return ret; 4508c2ecf20Sopenharmony_ci} 451