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