1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * A test case of using hugepage memory in a user application using the 4 * mmap system call with MAP_HUGETLB flag. Before running this program 5 * make sure the administrator has allocated enough default sized huge 6 * pages to cover the 2 MB allocation. 7 */ 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include <unistd.h> 11 #include <sys/mman.h> 12 #include <fcntl.h> 13 14 #define MAP_LENGTH (2UL * 1024 * 1024) 15 16 #define PAGE_SIZE 4096 17 18 #define PAGE_COMPOUND_HEAD (1UL << 15) 19 #define PAGE_COMPOUND_TAIL (1UL << 16) 20 #define PAGE_HUGE (1UL << 17) 21 22 #define HEAD_PAGE_FLAGS (PAGE_COMPOUND_HEAD | PAGE_HUGE) 23 #define TAIL_PAGE_FLAGS (PAGE_COMPOUND_TAIL | PAGE_HUGE) 24 25 #define PM_PFRAME_BITS 55 26 #define PM_PFRAME_MASK ~((1UL << PM_PFRAME_BITS) - 1) 27 28 /* 29 * For ia64 architecture, Linux kernel reserves Region number 4 for hugepages. 30 * That means the addresses starting with 0x800000... will need to be 31 * specified. Specifying a fixed address is not required on ppc64, i386 32 * or x86_64. 33 */ 34 #ifdef __ia64__ 35 #define MAP_ADDR (void *)(0x8000000000000000UL) 36 #define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED) 37 #else 38 #define MAP_ADDR NULL 39 #define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB) 40 #endif 41 42 static void write_bytes(char *addr, size_t length) 43 { 44 unsigned long i; 45 46 for (i = 0; i < length; i++) 47 *(addr + i) = (char)i; 48 } 49 50 static unsigned long virt_to_pfn(void *addr) 51 { 52 int fd; 53 unsigned long pagemap; 54 55 fd = open("/proc/self/pagemap", O_RDONLY); 56 if (fd < 0) 57 return -1UL; 58 59 lseek(fd, (unsigned long)addr / PAGE_SIZE * sizeof(pagemap), SEEK_SET); 60 read(fd, &pagemap, sizeof(pagemap)); 61 close(fd); 62 63 return pagemap & ~PM_PFRAME_MASK; 64 } 65 66 static int check_page_flags(unsigned long pfn) 67 { 68 int fd, i; 69 unsigned long pageflags; 70 71 fd = open("/proc/kpageflags", O_RDONLY); 72 if (fd < 0) 73 return -1; 74 75 lseek(fd, pfn * sizeof(pageflags), SEEK_SET); 76 77 read(fd, &pageflags, sizeof(pageflags)); 78 if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) { 79 close(fd); 80 printf("Head page flags (%lx) is invalid\n", pageflags); 81 return -1; 82 } 83 84 /* 85 * pages other than the first page must be tail and shouldn't be head; 86 * this also verifies kernel has correctly set the fake page_head to tail 87 * while hugetlb_free_vmemmap is enabled. 88 */ 89 for (i = 1; i < MAP_LENGTH / PAGE_SIZE; i++) { 90 read(fd, &pageflags, sizeof(pageflags)); 91 if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS || 92 (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) { 93 close(fd); 94 printf("Tail page flags (%lx) is invalid\n", pageflags); 95 return -1; 96 } 97 } 98 99 close(fd); 100 101 return 0; 102 } 103 104 int main(int argc, char **argv) 105 { 106 void *addr; 107 unsigned long pfn; 108 109 addr = mmap(MAP_ADDR, MAP_LENGTH, PROT_READ | PROT_WRITE, MAP_FLAGS, -1, 0); 110 if (addr == MAP_FAILED) { 111 perror("mmap"); 112 exit(1); 113 } 114 115 /* Trigger allocation of HugeTLB page. */ 116 write_bytes(addr, MAP_LENGTH); 117 118 pfn = virt_to_pfn(addr); 119 if (pfn == -1UL) { 120 munmap(addr, MAP_LENGTH); 121 perror("virt_to_pfn"); 122 exit(1); 123 } 124 125 printf("Returned address is %p whose pfn is %lx\n", addr, pfn); 126 127 if (check_page_flags(pfn) < 0) { 128 munmap(addr, MAP_LENGTH); 129 perror("check_page_flags"); 130 exit(1); 131 } 132 133 /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ 134 if (munmap(addr, MAP_LENGTH)) { 135 perror("munmap"); 136 exit(1); 137 } 138 139 return 0; 140 } 141