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