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