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