brintos

brintos / linux-shallow public Read only

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