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
write_fault_pages(void * addr,unsigned long nr_pages)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
read_fault_pages(void * addr,unsigned long nr_pages)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
main(int argc,char ** argv)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