xref: /linux/tools/testing/selftests/mm/hugetlb-vmemmap.c (revision 00599d4841790d05401820a96cf7edb193888b00)
1a93cbee5SMike Rapoport (Microsoft) // SPDX-License-Identifier: GPL-2.0
2a93cbee5SMike Rapoport (Microsoft) /*
3a93cbee5SMike Rapoport (Microsoft)  * A test case of using hugepage memory in a user application using the
4a93cbee5SMike Rapoport (Microsoft)  * mmap system call with MAP_HUGETLB flag.  Before running this program
5a93cbee5SMike Rapoport (Microsoft)  * make sure the administrator has allocated enough default sized huge
6a93cbee5SMike Rapoport (Microsoft)  * pages to cover the 2 MB allocation.
7a93cbee5SMike Rapoport (Microsoft)  */
8a93cbee5SMike Rapoport (Microsoft) #include <stdlib.h>
9a93cbee5SMike Rapoport (Microsoft) #include <stdio.h>
10a93cbee5SMike Rapoport (Microsoft) #include <unistd.h>
11a93cbee5SMike Rapoport (Microsoft) #include <sys/mman.h>
12a93cbee5SMike Rapoport (Microsoft) #include <fcntl.h>
13a93cbee5SMike Rapoport (Microsoft) #include "vm_util.h"
141bdc1698SMike Rapoport (Microsoft) #include "hugepage_settings.h"
15a93cbee5SMike Rapoport (Microsoft) 
16a93cbee5SMike Rapoport (Microsoft) #define PAGE_COMPOUND_HEAD	(1UL << 15)
17a93cbee5SMike Rapoport (Microsoft) #define PAGE_COMPOUND_TAIL	(1UL << 16)
18a93cbee5SMike Rapoport (Microsoft) #define PAGE_HUGE		(1UL << 17)
19a93cbee5SMike Rapoport (Microsoft) 
20a93cbee5SMike Rapoport (Microsoft) #define HEAD_PAGE_FLAGS		(PAGE_COMPOUND_HEAD | PAGE_HUGE)
21a93cbee5SMike Rapoport (Microsoft) #define TAIL_PAGE_FLAGS		(PAGE_COMPOUND_TAIL | PAGE_HUGE)
22a93cbee5SMike Rapoport (Microsoft) 
23a93cbee5SMike Rapoport (Microsoft) #define PM_PFRAME_BITS		55
24a93cbee5SMike Rapoport (Microsoft) #define PM_PFRAME_MASK		~((1UL << PM_PFRAME_BITS) - 1)
25a93cbee5SMike Rapoport (Microsoft) 
26a93cbee5SMike Rapoport (Microsoft) static size_t pagesize;
27a93cbee5SMike Rapoport (Microsoft) static size_t maplength;
28a93cbee5SMike Rapoport (Microsoft) 
29a93cbee5SMike Rapoport (Microsoft) static void write_bytes(char *addr, size_t length)
30a93cbee5SMike Rapoport (Microsoft) {
31a93cbee5SMike Rapoport (Microsoft) 	unsigned long i;
32a93cbee5SMike Rapoport (Microsoft) 
33a93cbee5SMike Rapoport (Microsoft) 	for (i = 0; i < length; i++)
34a93cbee5SMike Rapoport (Microsoft) 		*(addr + i) = (char)i;
35a93cbee5SMike Rapoport (Microsoft) }
36a93cbee5SMike Rapoport (Microsoft) 
37a93cbee5SMike Rapoport (Microsoft) static unsigned long virt_to_pfn(void *addr)
38a93cbee5SMike Rapoport (Microsoft) {
39a93cbee5SMike Rapoport (Microsoft) 	int fd;
40a93cbee5SMike Rapoport (Microsoft) 	unsigned long pagemap;
41a93cbee5SMike Rapoport (Microsoft) 
42a93cbee5SMike Rapoport (Microsoft) 	fd = open("/proc/self/pagemap", O_RDONLY);
43a93cbee5SMike Rapoport (Microsoft) 	if (fd < 0)
44a93cbee5SMike Rapoport (Microsoft) 		return -1UL;
45a93cbee5SMike Rapoport (Microsoft) 
46a93cbee5SMike Rapoport (Microsoft) 	lseek(fd, (unsigned long)addr / pagesize * sizeof(pagemap), SEEK_SET);
47a93cbee5SMike Rapoport (Microsoft) 	read(fd, &pagemap, sizeof(pagemap));
48a93cbee5SMike Rapoport (Microsoft) 	close(fd);
49a93cbee5SMike Rapoport (Microsoft) 
50a93cbee5SMike Rapoport (Microsoft) 	return pagemap & ~PM_PFRAME_MASK;
51a93cbee5SMike Rapoport (Microsoft) }
52a93cbee5SMike Rapoport (Microsoft) 
53a93cbee5SMike Rapoport (Microsoft) static int check_page_flags(unsigned long pfn)
54a93cbee5SMike Rapoport (Microsoft) {
55a93cbee5SMike Rapoport (Microsoft) 	int fd, i;
56a93cbee5SMike Rapoport (Microsoft) 	unsigned long pageflags;
57a93cbee5SMike Rapoport (Microsoft) 
58a93cbee5SMike Rapoport (Microsoft) 	fd = open("/proc/kpageflags", O_RDONLY);
59a93cbee5SMike Rapoport (Microsoft) 	if (fd < 0)
60a93cbee5SMike Rapoport (Microsoft) 		return -1;
61a93cbee5SMike Rapoport (Microsoft) 
62a93cbee5SMike Rapoport (Microsoft) 	lseek(fd, pfn * sizeof(pageflags), SEEK_SET);
63a93cbee5SMike Rapoport (Microsoft) 
64a93cbee5SMike Rapoport (Microsoft) 	read(fd, &pageflags, sizeof(pageflags));
65a93cbee5SMike Rapoport (Microsoft) 	if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) {
66a93cbee5SMike Rapoport (Microsoft) 		close(fd);
6740e9e3d7SMike Rapoport (Microsoft) 		ksft_print_msg("Head page flags (%lx) is invalid\n", pageflags);
68a93cbee5SMike Rapoport (Microsoft) 		return -1;
69a93cbee5SMike Rapoport (Microsoft) 	}
70a93cbee5SMike Rapoport (Microsoft) 
71a93cbee5SMike Rapoport (Microsoft) 	/*
72a93cbee5SMike Rapoport (Microsoft) 	 * pages other than the first page must be tail and shouldn't be head;
73a93cbee5SMike Rapoport (Microsoft) 	 * this also verifies kernel has correctly set the fake page_head to tail
74a93cbee5SMike Rapoport (Microsoft) 	 * while hugetlb_free_vmemmap is enabled.
75a93cbee5SMike Rapoport (Microsoft) 	 */
76a93cbee5SMike Rapoport (Microsoft) 	for (i = 1; i < maplength / pagesize; i++) {
77a93cbee5SMike Rapoport (Microsoft) 		read(fd, &pageflags, sizeof(pageflags));
78a93cbee5SMike Rapoport (Microsoft) 		if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS ||
79a93cbee5SMike Rapoport (Microsoft) 		    (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) {
80a93cbee5SMike Rapoport (Microsoft) 			close(fd);
8140e9e3d7SMike Rapoport (Microsoft) 			ksft_print_msg("Tail page flags (%lx) is invalid\n", pageflags);
82a93cbee5SMike Rapoport (Microsoft) 			return -1;
83a93cbee5SMike Rapoport (Microsoft) 		}
84a93cbee5SMike Rapoport (Microsoft) 	}
85a93cbee5SMike Rapoport (Microsoft) 
86a93cbee5SMike Rapoport (Microsoft) 	close(fd);
87a93cbee5SMike Rapoport (Microsoft) 
88a93cbee5SMike Rapoport (Microsoft) 	return 0;
89a93cbee5SMike Rapoport (Microsoft) }
90a93cbee5SMike Rapoport (Microsoft) 
91a93cbee5SMike Rapoport (Microsoft) int main(int argc, char **argv)
92a93cbee5SMike Rapoport (Microsoft) {
93a93cbee5SMike Rapoport (Microsoft) 	void *addr;
94a93cbee5SMike Rapoport (Microsoft) 	unsigned long pfn;
9540e9e3d7SMike Rapoport (Microsoft) 	int ret;
9640e9e3d7SMike Rapoport (Microsoft) 
9740e9e3d7SMike Rapoport (Microsoft) 	ksft_print_header();
9840e9e3d7SMike Rapoport (Microsoft) 	ksft_set_plan(1);
99a93cbee5SMike Rapoport (Microsoft) 
100*496a662fSMike Rapoport (Microsoft) 	if (!hugetlb_setup_default(1))
101*496a662fSMike Rapoport (Microsoft) 		ksft_exit_skip("Not enough free huge pages\n");
102*496a662fSMike Rapoport (Microsoft) 
103a93cbee5SMike Rapoport (Microsoft) 	pagesize  = psize();
104a93cbee5SMike Rapoport (Microsoft) 	maplength = default_huge_page_size();
10540e9e3d7SMike Rapoport (Microsoft) 	if (!maplength)
10640e9e3d7SMike Rapoport (Microsoft) 		ksft_exit_skip("Unable to determine huge page size\n");
107a93cbee5SMike Rapoport (Microsoft) 
108a93cbee5SMike Rapoport (Microsoft) 	addr = mmap(NULL, maplength, PROT_READ | PROT_WRITE,
109a93cbee5SMike Rapoport (Microsoft) 			MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
11040e9e3d7SMike Rapoport (Microsoft) 	if (addr == MAP_FAILED)
11140e9e3d7SMike Rapoport (Microsoft) 		ksft_exit_fail_perror("mmap");
112a93cbee5SMike Rapoport (Microsoft) 
113a93cbee5SMike Rapoport (Microsoft) 	/* Trigger allocation of HugeTLB page. */
114a93cbee5SMike Rapoport (Microsoft) 	write_bytes(addr, maplength);
115a93cbee5SMike Rapoport (Microsoft) 
116a93cbee5SMike Rapoport (Microsoft) 	pfn = virt_to_pfn(addr);
117a93cbee5SMike Rapoport (Microsoft) 	if (pfn == -1UL) {
11840e9e3d7SMike Rapoport (Microsoft) 		ksft_perror("virt_to_pfn");
119a93cbee5SMike Rapoport (Microsoft) 		munmap(addr, maplength);
12040e9e3d7SMike Rapoport (Microsoft) 		ksft_exit_fail();
121a93cbee5SMike Rapoport (Microsoft) 	}
122a93cbee5SMike Rapoport (Microsoft) 
12340e9e3d7SMike Rapoport (Microsoft) 	ksft_print_msg("Returned address is %p whose pfn is %lx\n", addr, pfn);
124a93cbee5SMike Rapoport (Microsoft) 
12540e9e3d7SMike Rapoport (Microsoft) 	ret = check_page_flags(pfn);
126a93cbee5SMike Rapoport (Microsoft) 
12740e9e3d7SMike Rapoport (Microsoft) 	if (munmap(addr, maplength))
12840e9e3d7SMike Rapoport (Microsoft) 		ksft_exit_fail_perror("munmap");
129a93cbee5SMike Rapoport (Microsoft) 
13040e9e3d7SMike Rapoport (Microsoft) 	ksft_test_result(!ret, "HugeTLB vmemmap page flags\n");
13140e9e3d7SMike Rapoport (Microsoft) 	ksft_finished();
132a93cbee5SMike Rapoport (Microsoft) }
133