File Index
Files and directories
Sign in
FILE

hugetlb-madvise.c

File preview with syntax highlighting.

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