hugetlb-madvise.c (10576B)
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 i, tmp; 93 94 for (i = 0; i < nr_pages; i++) 95 tmp += *((unsigned long *)(addr + (i * huge_page_size))); 96} 97 98int main(int argc, char **argv) 99{ 100 unsigned long free_hugepages; 101 void *addr, *addr2; 102 int fd; 103 int ret; 104 105 if (argc != 2) { 106 printf(USAGE, argv[0]); 107 exit(1); 108 } 109 110 huge_page_size = default_huge_page_size(); 111 if (!huge_page_size) { 112 printf("Unable to determine huge page size, exiting!\n"); 113 exit(1); 114 } 115 base_page_size = sysconf(_SC_PAGE_SIZE); 116 if (!huge_page_size) { 117 printf("Unable to determine base page size, exiting!\n"); 118 exit(1); 119 } 120 121 free_hugepages = get_free_hugepages(); 122 if (free_hugepages < MIN_FREE_PAGES) { 123 printf("Not enough free huge pages to test, exiting!\n"); 124 exit(1); 125 } 126 127 fd = open(argv[1], O_CREAT | O_RDWR, 0755); 128 if (fd < 0) { 129 perror("Open failed"); 130 exit(1); 131 } 132 133 /* 134 * Test validity of MADV_DONTNEED addr and length arguments. mmap 135 * size is NR_HUGE_PAGES + 2. One page at the beginning and end of 136 * the mapping will be unmapped so we KNOW there is nothing mapped 137 * there. 138 */ 139 addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size, 140 PROT_READ | PROT_WRITE, 141 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 142 -1, 0); 143 if (addr == MAP_FAILED) { 144 perror("mmap"); 145 exit(1); 146 } 147 if (munmap(addr, huge_page_size) || 148 munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size, 149 huge_page_size)) { 150 perror("munmap"); 151 exit(1); 152 } 153 addr = addr + huge_page_size; 154 155 write_fault_pages(addr, NR_HUGE_PAGES); 156 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 157 158 /* addr before mapping should fail */ 159 ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size, 160 MADV_DONTNEED); 161 if (!ret) { 162 printf("Unexpected success of madvise call with invalid addr line %d\n", 163 __LINE__); 164 exit(1); 165 } 166 167 /* addr + length after mapping should fail */ 168 ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size, 169 MADV_DONTNEED); 170 if (!ret) { 171 printf("Unexpected success of madvise call with invalid length line %d\n", 172 __LINE__); 173 exit(1); 174 } 175 176 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 177 178 /* 179 * Test alignment of MADV_DONTNEED addr and length arguments 180 */ 181 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 182 PROT_READ | PROT_WRITE, 183 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 184 -1, 0); 185 if (addr == MAP_FAILED) { 186 perror("mmap"); 187 exit(1); 188 } 189 write_fault_pages(addr, NR_HUGE_PAGES); 190 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 191 192 /* addr is not huge page size aligned and should fail */ 193 ret = madvise(addr + base_page_size, 194 NR_HUGE_PAGES * huge_page_size - base_page_size, 195 MADV_DONTNEED); 196 if (!ret) { 197 printf("Unexpected success of madvise call with unaligned start address %d\n", 198 __LINE__); 199 exit(1); 200 } 201 202 /* addr + length should be aligned up to huge page size */ 203 if (madvise(addr, 204 ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size, 205 MADV_DONTNEED)) { 206 perror("madvise"); 207 exit(1); 208 } 209 210 /* should free all pages in mapping */ 211 validate_free_pages(free_hugepages); 212 213 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 214 215 /* 216 * Test MADV_DONTNEED on anonymous private mapping 217 */ 218 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 219 PROT_READ | PROT_WRITE, 220 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 221 -1, 0); 222 if (addr == MAP_FAILED) { 223 perror("mmap"); 224 exit(1); 225 } 226 write_fault_pages(addr, NR_HUGE_PAGES); 227 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 228 229 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 230 perror("madvise"); 231 exit(1); 232 } 233 234 /* should free all pages in mapping */ 235 validate_free_pages(free_hugepages); 236 237 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 238 239 /* 240 * Test MADV_DONTNEED on private mapping of hugetlb file 241 */ 242 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { 243 perror("fallocate"); 244 exit(1); 245 } 246 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 247 248 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 249 PROT_READ | PROT_WRITE, 250 MAP_PRIVATE, fd, 0); 251 if (addr == MAP_FAILED) { 252 perror("mmap"); 253 exit(1); 254 } 255 256 /* read should not consume any pages */ 257 read_fault_pages(addr, NR_HUGE_PAGES); 258 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 259 260 /* madvise should not free any pages */ 261 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 262 perror("madvise"); 263 exit(1); 264 } 265 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 266 267 /* writes should allocate private pages */ 268 write_fault_pages(addr, NR_HUGE_PAGES); 269 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 270 271 /* madvise should free private pages */ 272 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 273 perror("madvise"); 274 exit(1); 275 } 276 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 277 278 /* writes should allocate private pages */ 279 write_fault_pages(addr, NR_HUGE_PAGES); 280 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 281 282 /* 283 * The fallocate below certainly should free the pages associated 284 * with the file. However, pages in the private mapping are also 285 * freed. This is not the 'correct' behavior, but is expected 286 * because this is how it has worked since the initial hugetlb 287 * implementation. 288 */ 289 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 290 0, NR_HUGE_PAGES * huge_page_size)) { 291 perror("fallocate"); 292 exit(1); 293 } 294 validate_free_pages(free_hugepages); 295 296 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 297 298 /* 299 * Test MADV_DONTNEED on shared mapping of hugetlb file 300 */ 301 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { 302 perror("fallocate"); 303 exit(1); 304 } 305 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 306 307 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 308 PROT_READ | PROT_WRITE, 309 MAP_SHARED, fd, 0); 310 if (addr == MAP_FAILED) { 311 perror("mmap"); 312 exit(1); 313 } 314 315 /* write should not consume any pages */ 316 write_fault_pages(addr, NR_HUGE_PAGES); 317 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 318 319 /* madvise should not free any pages */ 320 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 321 perror("madvise"); 322 exit(1); 323 } 324 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 325 326 /* 327 * Test MADV_REMOVE on shared mapping of hugetlb file 328 * 329 * madvise is same as hole punch and should free all pages. 330 */ 331 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) { 332 perror("madvise"); 333 exit(1); 334 } 335 validate_free_pages(free_hugepages); 336 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 337 338 /* 339 * Test MADV_REMOVE on shared and private mapping of hugetlb file 340 */ 341 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) { 342 perror("fallocate"); 343 exit(1); 344 } 345 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 346 347 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 348 PROT_READ | PROT_WRITE, 349 MAP_SHARED, fd, 0); 350 if (addr == MAP_FAILED) { 351 perror("mmap"); 352 exit(1); 353 } 354 355 /* shared write should not consume any additional pages */ 356 write_fault_pages(addr, NR_HUGE_PAGES); 357 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 358 359 addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 360 PROT_READ | PROT_WRITE, 361 MAP_PRIVATE, fd, 0); 362 if (addr2 == MAP_FAILED) { 363 perror("mmap"); 364 exit(1); 365 } 366 367 /* private read should not consume any pages */ 368 read_fault_pages(addr2, NR_HUGE_PAGES); 369 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 370 371 /* private write should consume additional pages */ 372 write_fault_pages(addr2, NR_HUGE_PAGES); 373 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 374 375 /* madvise of shared mapping should not free any pages */ 376 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 377 perror("madvise"); 378 exit(1); 379 } 380 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 381 382 /* madvise of private mapping should free private pages */ 383 if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) { 384 perror("madvise"); 385 exit(1); 386 } 387 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 388 389 /* private write should consume additional pages again */ 390 write_fault_pages(addr2, NR_HUGE_PAGES); 391 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 392 393 /* 394 * madvise should free both file and private pages although this is 395 * not correct. private pages should not be freed, but this is 396 * expected. See comment associated with FALLOC_FL_PUNCH_HOLE call. 397 */ 398 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) { 399 perror("madvise"); 400 exit(1); 401 } 402 validate_free_pages(free_hugepages); 403 404 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 405 (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size); 406 407 close(fd); 408 unlink(argv[1]); 409 return 0; 410}