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