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