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 #include "vm_util.h" 14 #include "hugepage_settings.h" 15 16 #define PAGE_COMPOUND_HEAD (1UL << 15) 17 #define PAGE_COMPOUND_TAIL (1UL << 16) 18 #define PAGE_HUGE (1UL << 17) 19 20 #define HEAD_PAGE_FLAGS (PAGE_COMPOUND_HEAD | PAGE_HUGE) 21 #define TAIL_PAGE_FLAGS (PAGE_COMPOUND_TAIL | PAGE_HUGE) 22 23 #define PM_PFRAME_BITS 55 24 #define PM_PFRAME_MASK ~((1UL << PM_PFRAME_BITS) - 1) 25 26 static size_t pagesize; 27 static size_t maplength; 28 29 static void write_bytes(char *addr, size_t length) 30 { 31 unsigned long i; 32 33 for (i = 0; i < length; i++) 34 *(addr + i) = (char)i; 35 } 36 37 static unsigned long virt_to_pfn(void *addr) 38 { 39 int fd; 40 unsigned long pagemap; 41 42 fd = open("/proc/self/pagemap", O_RDONLY); 43 if (fd < 0) 44 return -1UL; 45 46 lseek(fd, (unsigned long)addr / pagesize * sizeof(pagemap), SEEK_SET); 47 read(fd, &pagemap, sizeof(pagemap)); 48 close(fd); 49 50 return pagemap & ~PM_PFRAME_MASK; 51 } 52 53 static int check_page_flags(unsigned long pfn) 54 { 55 int fd, i; 56 unsigned long pageflags; 57 58 fd = open("/proc/kpageflags", O_RDONLY); 59 if (fd < 0) 60 return -1; 61 62 lseek(fd, pfn * sizeof(pageflags), SEEK_SET); 63 64 read(fd, &pageflags, sizeof(pageflags)); 65 if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) { 66 close(fd); 67 ksft_print_msg("Head page flags (%lx) is invalid\n", pageflags); 68 return -1; 69 } 70 71 /* 72 * pages other than the first page must be tail and shouldn't be head; 73 * this also verifies kernel has correctly set the fake page_head to tail 74 * while hugetlb_free_vmemmap is enabled. 75 */ 76 for (i = 1; i < maplength / pagesize; i++) { 77 read(fd, &pageflags, sizeof(pageflags)); 78 if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS || 79 (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) { 80 close(fd); 81 ksft_print_msg("Tail page flags (%lx) is invalid\n", pageflags); 82 return -1; 83 } 84 } 85 86 close(fd); 87 88 return 0; 89 } 90 91 int main(int argc, char **argv) 92 { 93 void *addr; 94 unsigned long pfn; 95 int ret; 96 97 ksft_print_header(); 98 ksft_set_plan(1); 99 100 if (!hugetlb_setup_default(1)) 101 ksft_exit_skip("Not enough free huge pages\n"); 102 103 pagesize = psize(); 104 maplength = default_huge_page_size(); 105 if (!maplength) 106 ksft_exit_skip("Unable to determine huge page size\n"); 107 108 addr = mmap(NULL, maplength, PROT_READ | PROT_WRITE, 109 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); 110 if (addr == MAP_FAILED) 111 ksft_exit_fail_perror("mmap"); 112 113 /* Trigger allocation of HugeTLB page. */ 114 write_bytes(addr, maplength); 115 116 pfn = virt_to_pfn(addr); 117 if (pfn == -1UL) { 118 ksft_perror("virt_to_pfn"); 119 munmap(addr, maplength); 120 ksft_exit_fail(); 121 } 122 123 ksft_print_msg("Returned address is %p whose pfn is %lx\n", addr, pfn); 124 125 ret = check_page_flags(pfn); 126 127 if (munmap(addr, maplength)) 128 ksft_exit_fail_perror("munmap"); 129 130 ksft_test_result(!ret, "HugeTLB vmemmap page flags\n"); 131 ksft_finished(); 132 } 133