xref: /linux/tools/testing/selftests/mm/hugepage-vmemmap.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1b147c89cSMuchun Song // SPDX-License-Identifier: GPL-2.0
2b147c89cSMuchun Song /*
3b147c89cSMuchun Song  * A test case of using hugepage memory in a user application using the
4b147c89cSMuchun Song  * mmap system call with MAP_HUGETLB flag.  Before running this program
5b147c89cSMuchun Song  * make sure the administrator has allocated enough default sized huge
6b147c89cSMuchun Song  * pages to cover the 2 MB allocation.
7b147c89cSMuchun Song  */
8b147c89cSMuchun Song #include <stdlib.h>
9b147c89cSMuchun Song #include <stdio.h>
10b147c89cSMuchun Song #include <unistd.h>
11b147c89cSMuchun Song #include <sys/mman.h>
12b147c89cSMuchun Song #include <fcntl.h>
1300bcfcd4SDonet Tom #include "vm_util.h"
14b147c89cSMuchun Song 
15b147c89cSMuchun Song #define PAGE_COMPOUND_HEAD	(1UL << 15)
16b147c89cSMuchun Song #define PAGE_COMPOUND_TAIL	(1UL << 16)
17b147c89cSMuchun Song #define PAGE_HUGE		(1UL << 17)
18b147c89cSMuchun Song 
19b147c89cSMuchun Song #define HEAD_PAGE_FLAGS		(PAGE_COMPOUND_HEAD | PAGE_HUGE)
20b147c89cSMuchun Song #define TAIL_PAGE_FLAGS		(PAGE_COMPOUND_TAIL | PAGE_HUGE)
21b147c89cSMuchun Song 
22b147c89cSMuchun Song #define PM_PFRAME_BITS		55
23b147c89cSMuchun Song #define PM_PFRAME_MASK		~((1UL << PM_PFRAME_BITS) - 1)
24b147c89cSMuchun Song 
2500bcfcd4SDonet Tom static size_t pagesize;
2600bcfcd4SDonet Tom static size_t maplength;
2700bcfcd4SDonet Tom 
write_bytes(char * addr,size_t length)28b147c89cSMuchun Song static void write_bytes(char *addr, size_t length)
29b147c89cSMuchun Song {
30b147c89cSMuchun Song 	unsigned long i;
31b147c89cSMuchun Song 
32b147c89cSMuchun Song 	for (i = 0; i < length; i++)
33b147c89cSMuchun Song 		*(addr + i) = (char)i;
34b147c89cSMuchun Song }
35b147c89cSMuchun Song 
virt_to_pfn(void * addr)36b147c89cSMuchun Song static unsigned long virt_to_pfn(void *addr)
37b147c89cSMuchun Song {
38b147c89cSMuchun Song 	int fd;
39b147c89cSMuchun Song 	unsigned long pagemap;
40b147c89cSMuchun Song 
41b147c89cSMuchun Song 	fd = open("/proc/self/pagemap", O_RDONLY);
42b147c89cSMuchun Song 	if (fd < 0)
43b147c89cSMuchun Song 		return -1UL;
44b147c89cSMuchun Song 
4500bcfcd4SDonet Tom 	lseek(fd, (unsigned long)addr / pagesize * sizeof(pagemap), SEEK_SET);
46b147c89cSMuchun Song 	read(fd, &pagemap, sizeof(pagemap));
47b147c89cSMuchun Song 	close(fd);
48b147c89cSMuchun Song 
49b147c89cSMuchun Song 	return pagemap & ~PM_PFRAME_MASK;
50b147c89cSMuchun Song }
51b147c89cSMuchun Song 
check_page_flags(unsigned long pfn)52b147c89cSMuchun Song static int check_page_flags(unsigned long pfn)
53b147c89cSMuchun Song {
54b147c89cSMuchun Song 	int fd, i;
55b147c89cSMuchun Song 	unsigned long pageflags;
56b147c89cSMuchun Song 
57b147c89cSMuchun Song 	fd = open("/proc/kpageflags", O_RDONLY);
58b147c89cSMuchun Song 	if (fd < 0)
59b147c89cSMuchun Song 		return -1;
60b147c89cSMuchun Song 
61b147c89cSMuchun Song 	lseek(fd, pfn * sizeof(pageflags), SEEK_SET);
62b147c89cSMuchun Song 
63b147c89cSMuchun Song 	read(fd, &pageflags, sizeof(pageflags));
64b147c89cSMuchun Song 	if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) {
65b147c89cSMuchun Song 		close(fd);
66b147c89cSMuchun Song 		printf("Head page flags (%lx) is invalid\n", pageflags);
67b147c89cSMuchun Song 		return -1;
68b147c89cSMuchun Song 	}
69b147c89cSMuchun Song 
70b147c89cSMuchun Song 	/*
71b147c89cSMuchun Song 	 * pages other than the first page must be tail and shouldn't be head;
72b147c89cSMuchun Song 	 * this also verifies kernel has correctly set the fake page_head to tail
73b147c89cSMuchun Song 	 * while hugetlb_free_vmemmap is enabled.
74b147c89cSMuchun Song 	 */
7500bcfcd4SDonet Tom 	for (i = 1; i < maplength / pagesize; i++) {
76b147c89cSMuchun Song 		read(fd, &pageflags, sizeof(pageflags));
77b147c89cSMuchun Song 		if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS ||
78b147c89cSMuchun Song 		    (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) {
79b147c89cSMuchun Song 			close(fd);
80b147c89cSMuchun Song 			printf("Tail page flags (%lx) is invalid\n", pageflags);
81b147c89cSMuchun Song 			return -1;
82b147c89cSMuchun Song 		}
83b147c89cSMuchun Song 	}
84b147c89cSMuchun Song 
85b147c89cSMuchun Song 	close(fd);
86b147c89cSMuchun Song 
87b147c89cSMuchun Song 	return 0;
88b147c89cSMuchun Song }
89b147c89cSMuchun Song 
main(int argc,char ** argv)90b147c89cSMuchun Song int main(int argc, char **argv)
91b147c89cSMuchun Song {
92b147c89cSMuchun Song 	void *addr;
93b147c89cSMuchun Song 	unsigned long pfn;
94b147c89cSMuchun Song 
9500bcfcd4SDonet Tom 	pagesize  = psize();
9600bcfcd4SDonet Tom 	maplength = default_huge_page_size();
9700bcfcd4SDonet Tom 	if (!maplength) {
9800bcfcd4SDonet Tom 		printf("Unable to determine huge page size\n");
9900bcfcd4SDonet Tom 		exit(1);
10000bcfcd4SDonet Tom 	}
10100bcfcd4SDonet Tom 
1022f4db286SJinjiang Tu 	addr = mmap(NULL, maplength, PROT_READ | PROT_WRITE,
1032f4db286SJinjiang Tu 			MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
104b147c89cSMuchun Song 	if (addr == MAP_FAILED) {
105b147c89cSMuchun Song 		perror("mmap");
106b147c89cSMuchun Song 		exit(1);
107b147c89cSMuchun Song 	}
108b147c89cSMuchun Song 
109b147c89cSMuchun Song 	/* Trigger allocation of HugeTLB page. */
11000bcfcd4SDonet Tom 	write_bytes(addr, maplength);
111b147c89cSMuchun Song 
112b147c89cSMuchun Song 	pfn = virt_to_pfn(addr);
113b147c89cSMuchun Song 	if (pfn == -1UL) {
11400bcfcd4SDonet Tom 		munmap(addr, maplength);
115b147c89cSMuchun Song 		perror("virt_to_pfn");
116b147c89cSMuchun Song 		exit(1);
117b147c89cSMuchun Song 	}
118b147c89cSMuchun Song 
119b147c89cSMuchun Song 	printf("Returned address is %p whose pfn is %lx\n", addr, pfn);
120b147c89cSMuchun Song 
121b147c89cSMuchun Song 	if (check_page_flags(pfn) < 0) {
12200bcfcd4SDonet Tom 		munmap(addr, maplength);
123b147c89cSMuchun Song 		perror("check_page_flags");
124b147c89cSMuchun Song 		exit(1);
125b147c89cSMuchun Song 	}
126b147c89cSMuchun Song 
127b147c89cSMuchun Song 	/* munmap() length of MAP_HUGETLB memory must be hugepage aligned */
12800bcfcd4SDonet Tom 	if (munmap(addr, maplength)) {
129b147c89cSMuchun Song 		perror("munmap");
130b147c89cSMuchun Song 		exit(1);
131b147c89cSMuchun Song 	}
132b147c89cSMuchun Song 
133b147c89cSMuchun Song 	return 0;
134b147c89cSMuchun Song }
135