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

Configure Feed

Select the types of activity you want to include in your feed.

at v6.2-rc1 309 lines 7.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * A test of splitting PMD THPs and PTE-mapped THPs from a specified virtual 4 * address range in a process via <debugfs>/split_huge_pages interface. 5 */ 6 7#define _GNU_SOURCE 8#include <stdio.h> 9#include <stdlib.h> 10#include <stdarg.h> 11#include <unistd.h> 12#include <inttypes.h> 13#include <string.h> 14#include <fcntl.h> 15#include <sys/mman.h> 16#include <sys/mount.h> 17#include <malloc.h> 18#include <stdbool.h> 19#include "vm_util.h" 20 21uint64_t pagesize; 22unsigned int pageshift; 23uint64_t pmd_pagesize; 24 25#define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages" 26#define INPUT_MAX 80 27 28#define PID_FMT "%d,0x%lx,0x%lx" 29#define PATH_FMT "%s,0x%lx,0x%lx" 30 31#define PFN_MASK ((1UL<<55)-1) 32#define KPF_THP (1UL<<22) 33 34int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file) 35{ 36 uint64_t paddr; 37 uint64_t page_flags; 38 39 if (pagemap_file) { 40 pread(pagemap_file, &paddr, sizeof(paddr), 41 ((long)vaddr >> pageshift) * sizeof(paddr)); 42 43 if (kpageflags_file) { 44 pread(kpageflags_file, &page_flags, sizeof(page_flags), 45 (paddr & PFN_MASK) * sizeof(page_flags)); 46 47 return !!(page_flags & KPF_THP); 48 } 49 } 50 return 0; 51} 52 53static int write_file(const char *path, const char *buf, size_t buflen) 54{ 55 int fd; 56 ssize_t numwritten; 57 58 fd = open(path, O_WRONLY); 59 if (fd == -1) 60 return 0; 61 62 numwritten = write(fd, buf, buflen - 1); 63 close(fd); 64 if (numwritten < 1) 65 return 0; 66 67 return (unsigned int) numwritten; 68} 69 70static void write_debugfs(const char *fmt, ...) 71{ 72 char input[INPUT_MAX]; 73 int ret; 74 va_list argp; 75 76 va_start(argp, fmt); 77 ret = vsnprintf(input, INPUT_MAX, fmt, argp); 78 va_end(argp); 79 80 if (ret >= INPUT_MAX) { 81 printf("%s: Debugfs input is too long\n", __func__); 82 exit(EXIT_FAILURE); 83 } 84 85 if (!write_file(SPLIT_DEBUGFS, input, ret + 1)) { 86 perror(SPLIT_DEBUGFS); 87 exit(EXIT_FAILURE); 88 } 89} 90 91void split_pmd_thp(void) 92{ 93 char *one_page; 94 size_t len = 4 * pmd_pagesize; 95 size_t i; 96 97 one_page = memalign(pmd_pagesize, len); 98 99 if (!one_page) { 100 printf("Fail to allocate memory\n"); 101 exit(EXIT_FAILURE); 102 } 103 104 madvise(one_page, len, MADV_HUGEPAGE); 105 106 for (i = 0; i < len; i++) 107 one_page[i] = (char)i; 108 109 if (!check_huge_anon(one_page, 1, pmd_pagesize)) { 110 printf("No THP is allocated\n"); 111 exit(EXIT_FAILURE); 112 } 113 114 /* split all THPs */ 115 write_debugfs(PID_FMT, getpid(), (uint64_t)one_page, 116 (uint64_t)one_page + len); 117 118 for (i = 0; i < len; i++) 119 if (one_page[i] != (char)i) { 120 printf("%ld byte corrupted\n", i); 121 exit(EXIT_FAILURE); 122 } 123 124 125 if (check_huge_anon(one_page, 0, pmd_pagesize)) { 126 printf("Still AnonHugePages not split\n"); 127 exit(EXIT_FAILURE); 128 } 129 130 printf("Split huge pages successful\n"); 131 free(one_page); 132} 133 134void split_pte_mapped_thp(void) 135{ 136 char *one_page, *pte_mapped, *pte_mapped2; 137 size_t len = 4 * pmd_pagesize; 138 uint64_t thp_size; 139 size_t i; 140 const char *pagemap_template = "/proc/%d/pagemap"; 141 const char *kpageflags_proc = "/proc/kpageflags"; 142 char pagemap_proc[255]; 143 int pagemap_fd; 144 int kpageflags_fd; 145 146 if (snprintf(pagemap_proc, 255, pagemap_template, getpid()) < 0) { 147 perror("get pagemap proc error"); 148 exit(EXIT_FAILURE); 149 } 150 pagemap_fd = open(pagemap_proc, O_RDONLY); 151 152 if (pagemap_fd == -1) { 153 perror("read pagemap:"); 154 exit(EXIT_FAILURE); 155 } 156 157 kpageflags_fd = open(kpageflags_proc, O_RDONLY); 158 159 if (kpageflags_fd == -1) { 160 perror("read kpageflags:"); 161 exit(EXIT_FAILURE); 162 } 163 164 one_page = mmap((void *)(1UL << 30), len, PROT_READ | PROT_WRITE, 165 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 166 167 madvise(one_page, len, MADV_HUGEPAGE); 168 169 for (i = 0; i < len; i++) 170 one_page[i] = (char)i; 171 172 if (!check_huge_anon(one_page, 1, pmd_pagesize)) { 173 printf("No THP is allocated\n"); 174 exit(EXIT_FAILURE); 175 } 176 177 /* remap the first pagesize of first THP */ 178 pte_mapped = mremap(one_page, pagesize, pagesize, MREMAP_MAYMOVE); 179 180 /* remap the Nth pagesize of Nth THP */ 181 for (i = 1; i < 4; i++) { 182 pte_mapped2 = mremap(one_page + pmd_pagesize * i + pagesize * i, 183 pagesize, pagesize, 184 MREMAP_MAYMOVE|MREMAP_FIXED, 185 pte_mapped + pagesize * i); 186 if (pte_mapped2 == (char *)-1) { 187 perror("mremap failed"); 188 exit(EXIT_FAILURE); 189 } 190 } 191 192 /* smap does not show THPs after mremap, use kpageflags instead */ 193 thp_size = 0; 194 for (i = 0; i < pagesize * 4; i++) 195 if (i % pagesize == 0 && 196 is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd)) 197 thp_size++; 198 199 if (thp_size != 4) { 200 printf("Some THPs are missing during mremap\n"); 201 exit(EXIT_FAILURE); 202 } 203 204 /* split all remapped THPs */ 205 write_debugfs(PID_FMT, getpid(), (uint64_t)pte_mapped, 206 (uint64_t)pte_mapped + pagesize * 4); 207 208 /* smap does not show THPs after mremap, use kpageflags instead */ 209 thp_size = 0; 210 for (i = 0; i < pagesize * 4; i++) { 211 if (pte_mapped[i] != (char)i) { 212 printf("%ld byte corrupted\n", i); 213 exit(EXIT_FAILURE); 214 } 215 if (i % pagesize == 0 && 216 is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd)) 217 thp_size++; 218 } 219 220 if (thp_size) { 221 printf("Still %ld THPs not split\n", thp_size); 222 exit(EXIT_FAILURE); 223 } 224 225 printf("Split PTE-mapped huge pages successful\n"); 226 munmap(one_page, len); 227 close(pagemap_fd); 228 close(kpageflags_fd); 229} 230 231void split_file_backed_thp(void) 232{ 233 int status; 234 int fd; 235 ssize_t num_written; 236 char tmpfs_template[] = "/tmp/thp_split_XXXXXX"; 237 const char *tmpfs_loc = mkdtemp(tmpfs_template); 238 char testfile[INPUT_MAX]; 239 uint64_t pgoff_start = 0, pgoff_end = 1024; 240 241 printf("Please enable pr_debug in split_huge_pages_in_file() if you need more info.\n"); 242 243 status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m"); 244 245 if (status) { 246 printf("Unable to create a tmpfs for testing\n"); 247 exit(EXIT_FAILURE); 248 } 249 250 status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc); 251 if (status >= INPUT_MAX) { 252 printf("Fail to create file-backed THP split testing file\n"); 253 goto cleanup; 254 } 255 256 fd = open(testfile, O_CREAT|O_WRONLY); 257 if (fd == -1) { 258 perror("Cannot open testing file\n"); 259 goto cleanup; 260 } 261 262 /* write something to the file, so a file-backed THP can be allocated */ 263 num_written = write(fd, tmpfs_loc, strlen(tmpfs_loc) + 1); 264 close(fd); 265 266 if (num_written < 1) { 267 printf("Fail to write data to testing file\n"); 268 goto cleanup; 269 } 270 271 /* split the file-backed THP */ 272 write_debugfs(PATH_FMT, testfile, pgoff_start, pgoff_end); 273 274 status = unlink(testfile); 275 if (status) 276 perror("Cannot remove testing file\n"); 277 278cleanup: 279 status = umount(tmpfs_loc); 280 if (status) { 281 printf("Unable to umount %s\n", tmpfs_loc); 282 exit(EXIT_FAILURE); 283 } 284 status = rmdir(tmpfs_loc); 285 if (status) { 286 perror("cannot remove tmp dir"); 287 exit(EXIT_FAILURE); 288 } 289 290 printf("file-backed THP split test done, please check dmesg for more information\n"); 291} 292 293int main(int argc, char **argv) 294{ 295 if (geteuid() != 0) { 296 printf("Please run the benchmark as root\n"); 297 exit(EXIT_FAILURE); 298 } 299 300 pagesize = getpagesize(); 301 pageshift = ffs(pagesize) - 1; 302 pmd_pagesize = read_pmd_pagesize(); 303 304 split_pmd_thp(); 305 split_pte_mapped_thp(); 306 split_file_backed_thp(); 307 308 return 0; 309}