Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: add a selftest to verify hugetlb usage in memcg

This patch add a new kselftest to demonstrate and verify the new hugetlb
memcg accounting behavior.

Link: https://lkml.kernel.org/r/20231006184629.155543-5-nphamcs@gmail.com
Signed-off-by: Nhat Pham <nphamcs@gmail.com>
Cc: Frank van der Linden <fvdl@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Rik van Riel <riel@surriel.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Tejun heo <tj@kernel.org>
Cc: Yosry Ahmed <yosryahmed@google.com>
Cc: Zefan Li <lizefan.x@bytedance.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Nhat Pham and committed by
Andrew Morton
c0dddb7a 8cba9576

+239
+2
MAINTAINERS
··· 5269 5269 F: mm/memcontrol.c 5270 5270 F: mm/swap_cgroup.c 5271 5271 F: tools/testing/selftests/cgroup/memcg_protection.m 5272 + F: tools/testing/selftests/cgroup/test_hugetlb_memcg.c 5272 5273 F: tools/testing/selftests/cgroup/test_kmem.c 5273 5274 F: tools/testing/selftests/cgroup/test_memcontrol.c 5274 5275 ··· 9653 9652 F: mm/hugetlb.c 9654 9653 F: mm/hugetlb_vmemmap.c 9655 9654 F: mm/hugetlb_vmemmap.h 9655 + F: tools/testing/selftests/cgroup/test_hugetlb_memcg.c 9656 9656 9657 9657 HVA ST MEDIA DRIVER 9658 9658 M: Jean-Christophe Trotin <jean-christophe.trotin@foss.st.com>
+1
tools/testing/selftests/cgroup/.gitignore
··· 7 7 test_cpu 8 8 test_cpuset 9 9 test_zswap 10 + test_hugetlb_memcg 10 11 wait_inotify
+2
tools/testing/selftests/cgroup/Makefile
··· 14 14 TEST_GEN_PROGS += test_cpu 15 15 TEST_GEN_PROGS += test_cpuset 16 16 TEST_GEN_PROGS += test_zswap 17 + TEST_GEN_PROGS += test_hugetlb_memcg 17 18 18 19 LOCAL_HDRS += $(selfdir)/clone3/clone3_selftests.h $(selfdir)/pidfd/pidfd.h 19 20 ··· 28 27 $(OUTPUT)/test_cpu: cgroup_util.c 29 28 $(OUTPUT)/test_cpuset: cgroup_util.c 30 29 $(OUTPUT)/test_zswap: cgroup_util.c 30 + $(OUTPUT)/test_hugetlb_memcg: cgroup_util.c
+234
tools/testing/selftests/cgroup/test_hugetlb_memcg.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + 4 + #include <linux/limits.h> 5 + #include <sys/mman.h> 6 + #include <stdio.h> 7 + #include <stdlib.h> 8 + #include <string.h> 9 + #include <fcntl.h> 10 + #include "../kselftest.h" 11 + #include "cgroup_util.h" 12 + 13 + #define ADDR ((void *)(0x0UL)) 14 + #define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB) 15 + /* mapping 8 MBs == 4 hugepages */ 16 + #define LENGTH (8UL*1024*1024) 17 + #define PROTECTION (PROT_READ | PROT_WRITE) 18 + 19 + /* borrowed from mm/hmm-tests.c */ 20 + static long get_hugepage_size(void) 21 + { 22 + int fd; 23 + char buf[2048]; 24 + int len; 25 + char *p, *q, *path = "/proc/meminfo", *tag = "Hugepagesize:"; 26 + long val; 27 + 28 + fd = open(path, O_RDONLY); 29 + if (fd < 0) { 30 + /* Error opening the file */ 31 + return -1; 32 + } 33 + 34 + len = read(fd, buf, sizeof(buf)); 35 + close(fd); 36 + if (len < 0) { 37 + /* Error in reading the file */ 38 + return -1; 39 + } 40 + if (len == sizeof(buf)) { 41 + /* Error file is too large */ 42 + return -1; 43 + } 44 + buf[len] = '\0'; 45 + 46 + /* Search for a tag if provided */ 47 + if (tag) { 48 + p = strstr(buf, tag); 49 + if (!p) 50 + return -1; /* looks like the line we want isn't there */ 51 + p += strlen(tag); 52 + } else 53 + p = buf; 54 + 55 + val = strtol(p, &q, 0); 56 + if (*q != ' ') { 57 + /* Error parsing the file */ 58 + return -1; 59 + } 60 + 61 + return val; 62 + } 63 + 64 + static int set_file(const char *path, long value) 65 + { 66 + FILE *file; 67 + int ret; 68 + 69 + file = fopen(path, "w"); 70 + if (!file) 71 + return -1; 72 + ret = fprintf(file, "%ld\n", value); 73 + fclose(file); 74 + return ret; 75 + } 76 + 77 + static int set_nr_hugepages(long value) 78 + { 79 + return set_file("/proc/sys/vm/nr_hugepages", value); 80 + } 81 + 82 + static unsigned int check_first(char *addr) 83 + { 84 + return *(unsigned int *)addr; 85 + } 86 + 87 + static void write_data(char *addr) 88 + { 89 + unsigned long i; 90 + 91 + for (i = 0; i < LENGTH; i++) 92 + *(addr + i) = (char)i; 93 + } 94 + 95 + static int hugetlb_test_program(const char *cgroup, void *arg) 96 + { 97 + char *test_group = (char *)arg; 98 + void *addr; 99 + long old_current, expected_current, current; 100 + int ret = EXIT_FAILURE; 101 + 102 + old_current = cg_read_long(test_group, "memory.current"); 103 + set_nr_hugepages(20); 104 + current = cg_read_long(test_group, "memory.current"); 105 + if (current - old_current >= MB(2)) { 106 + ksft_print_msg( 107 + "setting nr_hugepages should not increase hugepage usage.\n"); 108 + ksft_print_msg("before: %ld, after: %ld\n", old_current, current); 109 + return EXIT_FAILURE; 110 + } 111 + 112 + addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, 0, 0); 113 + if (addr == MAP_FAILED) { 114 + ksft_print_msg("fail to mmap.\n"); 115 + return EXIT_FAILURE; 116 + } 117 + current = cg_read_long(test_group, "memory.current"); 118 + if (current - old_current >= MB(2)) { 119 + ksft_print_msg("mmap should not increase hugepage usage.\n"); 120 + ksft_print_msg("before: %ld, after: %ld\n", old_current, current); 121 + goto out_failed_munmap; 122 + } 123 + old_current = current; 124 + 125 + /* read the first page */ 126 + check_first(addr); 127 + expected_current = old_current + MB(2); 128 + current = cg_read_long(test_group, "memory.current"); 129 + if (!values_close(expected_current, current, 5)) { 130 + ksft_print_msg("memory usage should increase by around 2MB.\n"); 131 + ksft_print_msg( 132 + "expected memory: %ld, actual memory: %ld\n", 133 + expected_current, current); 134 + goto out_failed_munmap; 135 + } 136 + 137 + /* write to the whole range */ 138 + write_data(addr); 139 + current = cg_read_long(test_group, "memory.current"); 140 + expected_current = old_current + MB(8); 141 + if (!values_close(expected_current, current, 5)) { 142 + ksft_print_msg("memory usage should increase by around 8MB.\n"); 143 + ksft_print_msg( 144 + "expected memory: %ld, actual memory: %ld\n", 145 + expected_current, current); 146 + goto out_failed_munmap; 147 + } 148 + 149 + /* unmap the whole range */ 150 + munmap(addr, LENGTH); 151 + current = cg_read_long(test_group, "memory.current"); 152 + expected_current = old_current; 153 + if (!values_close(expected_current, current, 5)) { 154 + ksft_print_msg("memory usage should go back down.\n"); 155 + ksft_print_msg( 156 + "expected memory: %ld, actual memory: %ld\n", 157 + expected_current, current); 158 + return ret; 159 + } 160 + 161 + ret = EXIT_SUCCESS; 162 + return ret; 163 + 164 + out_failed_munmap: 165 + munmap(addr, LENGTH); 166 + return ret; 167 + } 168 + 169 + static int test_hugetlb_memcg(char *root) 170 + { 171 + int ret = KSFT_FAIL; 172 + char *test_group; 173 + 174 + test_group = cg_name(root, "hugetlb_memcg_test"); 175 + if (!test_group || cg_create(test_group)) { 176 + ksft_print_msg("fail to create cgroup.\n"); 177 + goto out; 178 + } 179 + 180 + if (cg_write(test_group, "memory.max", "100M")) { 181 + ksft_print_msg("fail to set cgroup memory limit.\n"); 182 + goto out; 183 + } 184 + 185 + /* disable swap */ 186 + if (cg_write(test_group, "memory.swap.max", "0")) { 187 + ksft_print_msg("fail to disable swap.\n"); 188 + goto out; 189 + } 190 + 191 + if (!cg_run(test_group, hugetlb_test_program, (void *)test_group)) 192 + ret = KSFT_PASS; 193 + out: 194 + cg_destroy(test_group); 195 + free(test_group); 196 + return ret; 197 + } 198 + 199 + int main(int argc, char **argv) 200 + { 201 + char root[PATH_MAX]; 202 + int ret = EXIT_SUCCESS, has_memory_hugetlb_acc; 203 + 204 + has_memory_hugetlb_acc = proc_mount_contains("memory_hugetlb_accounting"); 205 + if (has_memory_hugetlb_acc < 0) 206 + ksft_exit_skip("Failed to query cgroup mount option\n"); 207 + else if (!has_memory_hugetlb_acc) 208 + ksft_exit_skip("memory hugetlb accounting is disabled\n"); 209 + 210 + /* Unit is kB! */ 211 + if (get_hugepage_size() != 2048) { 212 + ksft_print_msg("test_hugetlb_memcg requires 2MB hugepages\n"); 213 + ksft_test_result_skip("test_hugetlb_memcg\n"); 214 + return ret; 215 + } 216 + 217 + if (cg_find_unified_root(root, sizeof(root))) 218 + ksft_exit_skip("cgroup v2 isn't mounted\n"); 219 + 220 + switch (test_hugetlb_memcg(root)) { 221 + case KSFT_PASS: 222 + ksft_test_result_pass("test_hugetlb_memcg\n"); 223 + break; 224 + case KSFT_SKIP: 225 + ksft_test_result_skip("test_hugetlb_memcg\n"); 226 + break; 227 + default: 228 + ret = EXIT_FAILURE; 229 + ksft_test_result_fail("test_hugetlb_memcg\n"); 230 + break; 231 + } 232 + 233 + return ret; 234 + }