1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * hugetlb-madvise: 4 * 5 * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE 6 * on hugetlb mappings. 7 */ 8 9 #define _GNU_SOURCE 10 #include <stdlib.h> 11 #include <stdio.h> 12 #include <unistd.h> 13 #include <sys/mman.h> 14 #include <fcntl.h> 15 #include "vm_util.h" 16 #include "kselftest.h" 17 #include "hugepage_settings.h" 18 19 #define MIN_FREE_PAGES 20 20 #define NR_HUGE_PAGES 10 /* common number of pages to map/allocate */ 21 22 #define validate_free_pages(exp_free) \ 23 do { \ 24 unsigned long fhp = hugetlb_free_default_pages(); \ 25 if (fhp != (exp_free)) \ 26 ksft_exit_fail_msg("Unexpected number of free " \ 27 "huge pages %lu, expected %lu line %d\n", \ 28 fhp, (exp_free), __LINE__); \ 29 } while (0) 30 31 unsigned long huge_page_size; 32 unsigned long base_page_size; 33 34 void write_fault_pages(void *addr, unsigned long nr_pages) 35 { 36 unsigned long i; 37 38 for (i = 0; i < nr_pages; i++) 39 *((unsigned long *)(addr + (i * huge_page_size))) = i; 40 } 41 42 void read_fault_pages(void *addr, unsigned long nr_pages) 43 { 44 force_read_pages(addr, nr_pages, huge_page_size); 45 } 46 47 int main(int argc, char **argv) 48 { 49 unsigned long free_hugepages; 50 void *addr, *addr2; 51 int fd; 52 int ret; 53 54 ksft_print_header(); 55 ksft_set_plan(1); 56 57 huge_page_size = default_huge_page_size(); 58 if (!huge_page_size) 59 ksft_exit_skip("Unable to determine huge page size\n"); 60 61 base_page_size = sysconf(_SC_PAGE_SIZE); 62 if (!base_page_size) 63 ksft_exit_fail_msg("Unable to determine base page size\n"); 64 65 if (!hugetlb_setup_default(MIN_FREE_PAGES)) 66 ksft_exit_skip("Not enough free huge pages (have %lu, need %d)\n", hugetlb_free_default_pages(), MIN_FREE_PAGES); 67 free_hugepages = hugetlb_free_default_pages(); 68 69 fd = memfd_create(argv[0], MFD_HUGETLB); 70 if (fd < 0) 71 ksft_exit_fail_perror("memfd_create"); 72 73 /* 74 * Test validity of MADV_DONTNEED addr and length arguments. mmap 75 * size is NR_HUGE_PAGES + 2. One page at the beginning and end of 76 * the mapping will be unmapped so we KNOW there is nothing mapped 77 * there. 78 */ 79 addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size, 80 PROT_READ | PROT_WRITE, 81 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 82 -1, 0); 83 if (addr == MAP_FAILED) 84 ksft_exit_fail_perror("mmap"); 85 86 if (munmap(addr, huge_page_size) || 87 munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size, 88 huge_page_size)) 89 ksft_exit_fail_perror("munmap"); 90 addr = addr + huge_page_size; 91 92 write_fault_pages(addr, NR_HUGE_PAGES); 93 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 94 95 /* addr before mapping should fail */ 96 ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size, 97 MADV_DONTNEED); 98 if (!ret) 99 ksft_exit_fail_msg("madvise with invalid addr unexpectedly succeeded line %d\n", __LINE__); 100 101 /* addr + length after mapping should fail */ 102 ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size, 103 MADV_DONTNEED); 104 if (!ret) 105 ksft_exit_fail_msg("madvise with invalid length unexpectedly succeeded line %d\n", __LINE__); 106 107 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 108 109 /* 110 * Test alignment of MADV_DONTNEED addr and length arguments 111 */ 112 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 113 PROT_READ | PROT_WRITE, 114 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 115 -1, 0); 116 if (addr == MAP_FAILED) 117 ksft_exit_fail_perror("mmap"); 118 119 write_fault_pages(addr, NR_HUGE_PAGES); 120 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 121 122 /* addr is not huge page size aligned and should fail */ 123 ret = madvise(addr + base_page_size, 124 NR_HUGE_PAGES * huge_page_size - base_page_size, 125 MADV_DONTNEED); 126 if (!ret) 127 ksft_exit_fail_msg("madvise with unaligned start unexpectedly succeeded line %d\n", __LINE__); 128 129 /* addr + length should be aligned down to huge page size */ 130 if (madvise(addr, 131 ((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size, 132 MADV_DONTNEED)) 133 ksft_exit_fail_perror("madvise"); 134 135 /* should free all but last page in mapping */ 136 validate_free_pages(free_hugepages - 1); 137 138 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 139 validate_free_pages(free_hugepages); 140 141 /* 142 * Test MADV_DONTNEED on anonymous private mapping 143 */ 144 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 145 PROT_READ | PROT_WRITE, 146 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 147 -1, 0); 148 if (addr == MAP_FAILED) 149 ksft_exit_fail_perror("mmap"); 150 151 write_fault_pages(addr, NR_HUGE_PAGES); 152 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 153 154 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) 155 ksft_exit_fail_perror("madvise"); 156 157 /* should free all pages in mapping */ 158 validate_free_pages(free_hugepages); 159 160 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 161 162 /* 163 * Test MADV_DONTNEED on private mapping of hugetlb file 164 */ 165 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) 166 ksft_exit_fail_perror("fallocate"); 167 168 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 169 170 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 171 PROT_READ | PROT_WRITE, 172 MAP_PRIVATE, fd, 0); 173 if (addr == MAP_FAILED) 174 ksft_exit_fail_perror("mmap"); 175 176 /* read should not consume any pages */ 177 read_fault_pages(addr, NR_HUGE_PAGES); 178 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 179 180 /* madvise should not free any pages */ 181 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) 182 ksft_exit_fail_perror("madvise"); 183 184 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 185 186 /* writes should allocate private pages */ 187 write_fault_pages(addr, NR_HUGE_PAGES); 188 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 189 190 /* madvise should free private pages */ 191 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) 192 ksft_exit_fail_perror("madvise"); 193 194 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 195 196 /* writes should allocate private pages */ 197 write_fault_pages(addr, NR_HUGE_PAGES); 198 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 199 200 /* 201 * The fallocate below certainly should free the pages associated 202 * with the file. However, pages in the private mapping are also 203 * freed. This is not the 'correct' behavior, but is expected 204 * because this is how it has worked since the initial hugetlb 205 * implementation. 206 */ 207 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 208 0, NR_HUGE_PAGES * huge_page_size)) 209 ksft_exit_fail_perror("fallocate"); 210 211 validate_free_pages(free_hugepages); 212 213 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 214 215 /* 216 * Test MADV_DONTNEED on shared mapping of hugetlb file 217 */ 218 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) 219 ksft_exit_fail_perror("fallocate"); 220 221 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 222 223 addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 224 PROT_READ | PROT_WRITE, 225 MAP_SHARED, fd, 0); 226 if (addr == MAP_FAILED) 227 ksft_exit_fail_perror("mmap"); 228 229 /* write should not consume any pages */ 230 write_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 ksft_exit_fail_perror("madvise"); 236 237 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 238 239 /* 240 * Test MADV_REMOVE on shared mapping of hugetlb file 241 * 242 * madvise is same as hole punch and should free all pages. 243 */ 244 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) 245 ksft_exit_fail_perror("madvise"); 246 247 validate_free_pages(free_hugepages); 248 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 249 250 /* 251 * Test MADV_REMOVE on shared and private mapping of hugetlb file 252 */ 253 if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) 254 ksft_exit_fail_perror("fallocate"); 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 ksft_exit_fail_perror("mmap"); 263 264 /* shared write should not consume any additional pages */ 265 write_fault_pages(addr, NR_HUGE_PAGES); 266 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 267 268 addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size, 269 PROT_READ | PROT_WRITE, 270 MAP_PRIVATE, fd, 0); 271 if (addr2 == MAP_FAILED) 272 ksft_exit_fail_perror("mmap"); 273 274 /* private read should not consume any pages */ 275 read_fault_pages(addr2, NR_HUGE_PAGES); 276 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 277 278 /* private write should consume additional pages */ 279 write_fault_pages(addr2, NR_HUGE_PAGES); 280 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 281 282 /* madvise of shared mapping should not free any pages */ 283 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) 284 ksft_exit_fail_perror("madvise"); 285 286 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 287 288 /* madvise of private mapping should free private pages */ 289 if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) 290 ksft_exit_fail_perror("madvise"); 291 292 validate_free_pages(free_hugepages - NR_HUGE_PAGES); 293 294 /* private write should consume additional pages again */ 295 write_fault_pages(addr2, NR_HUGE_PAGES); 296 validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES)); 297 298 /* 299 * madvise should free both file and private pages although this is 300 * not correct. private pages should not be freed, but this is 301 * expected. See comment associated with FALLOC_FL_PUNCH_HOLE call. 302 */ 303 if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) 304 ksft_exit_fail_perror("madvise"); 305 306 validate_free_pages(free_hugepages); 307 308 (void)munmap(addr, NR_HUGE_PAGES * huge_page_size); 309 (void)munmap(addr2, NR_HUGE_PAGES * huge_page_size); 310 311 close(fd); 312 313 ksft_test_result_pass("MADV_DONTNEED and MADV_REMOVE on hugetlb\n"); 314 ksft_finished(); 315 } 316