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