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

Configure Feed

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

at v6.1-rc4 411 lines 11 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * hugepage-madvise: 4 * 5 * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE 6 * on hugetlb mappings. 7 * 8 * Before running this test, make sure the administrator has pre-allocated 9 * at least MIN_FREE_PAGES hugetlb pages and they are free. In addition, 10 * the test takes an argument that is the path to a file in a hugetlbfs 11 * filesystem. Therefore, a hugetlbfs filesystem must be mounted on some 12 * directory. 13 */ 14 15#include <stdlib.h> 16#include <stdio.h> 17#include <unistd.h> 18#include <sys/mman.h> 19#define __USE_GNU 20#include <fcntl.h> 21 22#define USAGE "USAGE: %s <hugepagefile_name>\n" 23#define MIN_FREE_PAGES 20 24#define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */ 25 26#define validate_free_pages(exp_free) \ 27 do { \ 28 int fhp = get_free_hugepages(); \ 29 if (fhp != (exp_free)) { \ 30 printf("Unexpected number of free huge " \ 31 "pages line %d\n", __LINE__); \ 32 exit(1); \ 33 } \ 34 } while (0) 35 36unsigned long huge_page_size; 37unsigned long base_page_size; 38 39/* 40 * default_huge_page_size copied from mlock2-tests.c 41 */ 42unsigned long default_huge_page_size(void) 43{ 44 unsigned long hps = 0; 45 char *line = NULL; 46 size_t linelen = 0; 47 FILE *f = fopen("/proc/meminfo", "r"); 48 49 if (!f) 50 return 0; 51 while (getline(&line, &linelen, f) > 0) { 52 if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { 53 hps <<= 10; 54 break; 55 } 56 } 57 58 free(line); 59 fclose(f); 60 return hps; 61} 62 63unsigned long get_free_hugepages(void) 64{ 65 unsigned long fhp = 0; 66 char *line = NULL; 67 size_t linelen = 0; 68 FILE *f = fopen("/proc/meminfo", "r"); 69 70 if (!f) 71 return fhp; 72 while (getline(&line, &linelen, f) > 0) { 73 if (sscanf(line, "HugePages_Free: %lu", &fhp) == 1) 74 break; 75 } 76 77 free(line); 78 fclose(f); 79 return fhp; 80} 81 82void write_fault_pages(void *addr, unsigned long nr_pages) 83{ 84 unsigned long i; 85 86 for (i = 0; i < nr_pages; i++) 87 *((unsigned long *)(addr + (i * huge_page_size))) = i; 88} 89 90void read_fault_pages(void *addr, unsigned long nr_pages) 91{ 92 unsigned long dummy = 0; 93 unsigned long i; 94 95 for (i = 0; i < nr_pages; i++) 96 dummy += *((unsigned long *)(addr + (i * huge_page_size))); 97} 98 99int main(int argc, char **argv) 100{ 101 unsigned long free_hugepages; 102 void *addr, *addr2; 103 int fd; 104 int ret; 105 106 if (argc != 2) { 107 printf(USAGE, argv[0]); 108 exit(1); 109 } 110 111 huge_page_size = default_huge_page_size(); 112 if (!huge_page_size) { 113 printf("Unable to determine huge page size, exiting!\n"); 114 exit(1); 115 } 116 base_page_size = sysconf(_SC_PAGE_SIZE); 117 if (!huge_page_size) { 118 printf("Unable to determine base page size, exiting!\n"); 119 exit(1); 120 } 121 122 free_hugepages = get_free_hugepages(); 123 if (free_hugepages < MIN_FREE_PAGES) { 124 printf("Not enough free huge pages to test, exiting!\n"); 125 exit(1); 126 } 127 128 fd = open(argv[1], O_CREAT | O_RDWR, 0755); 129 if (fd < 0) { 130 perror("Open failed"); 131 exit(1); 132 } 133 134 /* 135 * Test validity of MADV_DONTNEED addr and length arguments. mmap 136 * size is NR_HUGE_PAGES + 2. One page at the beginning and end of 137 * the mapping will be unmapped so we KNOW there is nothing mapped 138 * there. 139 */ 140 addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size, 141 PROT_READ | PROT_WRITE, 142 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 143 -1, 0); 144 if (addr == MAP_FAILED) { 145 perror("mmap"); 146 exit(1); 147 } 148 if (munmap(addr, huge_page_size) || 149 munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size, 150 huge_page_size)) { 151 perror("munmap"); 152 exit(1); 153 } 154 addr = addr + huge_page_size; 155 156 write_fault_pages(addr, NR_HUGE_PAGES); 157 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 158 159 /* addr before mapping should fail */ 160 ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size, 161 MADV_DONTNEED); 162 if (!ret) { 163 printf("Unexpected success of madvise call with invalid addr line %d\n", 164 __LINE__); 165 exit(1); 166 } 167 168 /* addr + length after mapping should fail */ 169 ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size, 170 MADV_DONTNEED); 171 if (!ret) { 172 printf("Unexpected success of madvise call with invalid length line %d\n", 173 __LINE__); 174 exit(1); 175 } 176 177 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 178 179 /* 180 * Test alignment of MADV_DONTNEED addr and length arguments 181 */ 182 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 183 PROT_READ | PROT_WRITE, 184 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 185 -1, 0); 186 if (addr == MAP_FAILED) { 187 perror("mmap"); 188 exit(1); 189 } 190 write_fault_pages(addr, NR_HUGE_PAGES); 191 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 192 193 /* addr is not huge page size aligned and should fail */ 194 ret = madvise(addr + base_page_size, 195 NR_HUGE_PAGES * huge_page_size - base_page_size, 196 MADV_DONTNEED); 197 if (!ret) { 198 printf("Unexpected success of madvise call with unaligned start address %d\n", 199 __LINE__); 200 exit(1); 201 } 202 203 /* addr + length should be aligned up to huge page size */ 204 if (madvise(addr, 205 ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size, 206 MADV_DONTNEED)) { 207 perror("madvise"); 208 exit(1); 209 } 210 211 /* should free all pages in mapping */ 212 validate_free_pages(free_hugepages); 213 214 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 215 216 /* 217 * Test MADV_DONTNEED on anonymous private mapping 218 */ 219 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 220 PROT_READ | PROT_WRITE, 221 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 222 -1, 0); 223 if (addr == MAP_FAILED) { 224 perror("mmap"); 225 exit(1); 226 } 227 write_fault_pages(addr, NR_HUGE_PAGES); 228 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 229 230 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 231 perror("madvise"); 232 exit(1); 233 } 234 235 /* should free all pages in mapping */ 236 validate_free_pages(free_hugepages); 237 238 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 239 240 /* 241 * Test MADV_DONTNEED on private mapping of hugetlb file 242 */ 243 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { 244 perror("fallocate"); 245 exit(1); 246 } 247 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 248 249 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 250 PROT_READ | PROT_WRITE, 251 MAP_PRIVATE, fd, 0); 252 if (addr == MAP_FAILED) { 253 perror("mmap"); 254 exit(1); 255 } 256 257 /* read should not consume any pages */ 258 read_fault_pages(addr, NR_HUGE_PAGES); 259 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 260 261 /* madvise should not free any pages */ 262 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 263 perror("madvise"); 264 exit(1); 265 } 266 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 267 268 /* writes should allocate private pages */ 269 write_fault_pages(addr, NR_HUGE_PAGES); 270 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 271 272 /* madvise should free private pages */ 273 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 274 perror("madvise"); 275 exit(1); 276 } 277 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 278 279 /* writes should allocate private pages */ 280 write_fault_pages(addr, NR_HUGE_PAGES); 281 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 282 283 /* 284 * The fallocate below certainly should free the pages associated 285 * with the file. However, pages in the private mapping are also 286 * freed. This is not the 'correct' behavior, but is expected 287 * because this is how it has worked since the initial hugetlb 288 * implementation. 289 */ 290 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 291 0, NR_HUGE_PAGES * huge_page_size)) { 292 perror("fallocate"); 293 exit(1); 294 } 295 validate_free_pages(free_hugepages); 296 297 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 298 299 /* 300 * Test MADV_DONTNEED on shared mapping of hugetlb file 301 */ 302 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { 303 perror("fallocate"); 304 exit(1); 305 } 306 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 307 308 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 309 PROT_READ | PROT_WRITE, 310 MAP_SHARED, fd, 0); 311 if (addr == MAP_FAILED) { 312 perror("mmap"); 313 exit(1); 314 } 315 316 /* write should not consume any pages */ 317 write_fault_pages(addr, NR_HUGE_PAGES); 318 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 319 320 /* madvise should not free any pages */ 321 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 322 perror("madvise"); 323 exit(1); 324 } 325 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 326 327 /* 328 * Test MADV_REMOVE on shared mapping of hugetlb file 329 * 330 * madvise is same as hole punch and should free all pages. 331 */ 332 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) { 333 perror("madvise"); 334 exit(1); 335 } 336 validate_free_pages(free_hugepages); 337 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 338 339 /* 340 * Test MADV_REMOVE on shared and private mapping of hugetlb file 341 */ 342 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { 343 perror("fallocate"); 344 exit(1); 345 } 346 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 347 348 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 349 PROT_READ | PROT_WRITE, 350 MAP_SHARED, fd, 0); 351 if (addr == MAP_FAILED) { 352 perror("mmap"); 353 exit(1); 354 } 355 356 /* shared write should not consume any additional pages */ 357 write_fault_pages(addr, NR_HUGE_PAGES); 358 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 359 360 addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 361 PROT_READ | PROT_WRITE, 362 MAP_PRIVATE, fd, 0); 363 if (addr2 == MAP_FAILED) { 364 perror("mmap"); 365 exit(1); 366 } 367 368 /* private read should not consume any pages */ 369 read_fault_pages(addr2, NR_HUGE_PAGES); 370 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 371 372 /* private write should consume additional pages */ 373 write_fault_pages(addr2, NR_HUGE_PAGES); 374 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 375 376 /* madvise of shared mapping should not free any pages */ 377 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 378 perror("madvise"); 379 exit(1); 380 } 381 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 382 383 /* madvise of private mapping should free private pages */ 384 if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 385 perror("madvise"); 386 exit(1); 387 } 388 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 389 390 /* private write should consume additional pages again */ 391 write_fault_pages(addr2, NR_HUGE_PAGES); 392 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 393 394 /* 395 * madvise should free both file and private pages although this is 396 * not correct. private pages should not be freed, but this is 397 * expected. See comment associated with FALLOC_FL_PUNCH_HOLE call. 398 */ 399 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) { 400 perror("madvise"); 401 exit(1); 402 } 403 validate_free_pages(free_hugepages); 404 405 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 406 (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size); 407 408 close(fd); 409 unlink(argv[1]); 410 return 0; 411}