162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * A test case of using hugepage memory in a user application using the 462306a36Sopenharmony_ci * mmap system call with MAP_HUGETLB flag. Before running this program 562306a36Sopenharmony_ci * make sure the administrator has allocated enough default sized huge 662306a36Sopenharmony_ci * pages to cover the 2 MB allocation. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <stdlib.h> 962306a36Sopenharmony_ci#include <stdio.h> 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci#include <sys/mman.h> 1262306a36Sopenharmony_ci#include <fcntl.h> 1362306a36Sopenharmony_ci#include "vm_util.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define PAGE_COMPOUND_HEAD (1UL << 15) 1662306a36Sopenharmony_ci#define PAGE_COMPOUND_TAIL (1UL << 16) 1762306a36Sopenharmony_ci#define PAGE_HUGE (1UL << 17) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define HEAD_PAGE_FLAGS (PAGE_COMPOUND_HEAD | PAGE_HUGE) 2062306a36Sopenharmony_ci#define TAIL_PAGE_FLAGS (PAGE_COMPOUND_TAIL | PAGE_HUGE) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define PM_PFRAME_BITS 55 2362306a36Sopenharmony_ci#define PM_PFRAME_MASK ~((1UL << PM_PFRAME_BITS) - 1) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * For ia64 architecture, Linux kernel reserves Region number 4 for hugepages. 2762306a36Sopenharmony_ci * That means the addresses starting with 0x800000... will need to be 2862306a36Sopenharmony_ci * specified. Specifying a fixed address is not required on ppc64, i386 2962306a36Sopenharmony_ci * or x86_64. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#ifdef __ia64__ 3262306a36Sopenharmony_ci#define MAP_ADDR (void *)(0x8000000000000000UL) 3362306a36Sopenharmony_ci#define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED) 3462306a36Sopenharmony_ci#else 3562306a36Sopenharmony_ci#define MAP_ADDR NULL 3662306a36Sopenharmony_ci#define MAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB) 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic size_t pagesize; 4062306a36Sopenharmony_cistatic size_t maplength; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void write_bytes(char *addr, size_t length) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci unsigned long i; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci for (i = 0; i < length; i++) 4762306a36Sopenharmony_ci *(addr + i) = (char)i; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic unsigned long virt_to_pfn(void *addr) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int fd; 5362306a36Sopenharmony_ci unsigned long pagemap; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci fd = open("/proc/self/pagemap", O_RDONLY); 5662306a36Sopenharmony_ci if (fd < 0) 5762306a36Sopenharmony_ci return -1UL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci lseek(fd, (unsigned long)addr / pagesize * sizeof(pagemap), SEEK_SET); 6062306a36Sopenharmony_ci read(fd, &pagemap, sizeof(pagemap)); 6162306a36Sopenharmony_ci close(fd); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return pagemap & ~PM_PFRAME_MASK; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int check_page_flags(unsigned long pfn) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int fd, i; 6962306a36Sopenharmony_ci unsigned long pageflags; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci fd = open("/proc/kpageflags", O_RDONLY); 7262306a36Sopenharmony_ci if (fd < 0) 7362306a36Sopenharmony_ci return -1; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci lseek(fd, pfn * sizeof(pageflags), SEEK_SET); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci read(fd, &pageflags, sizeof(pageflags)); 7862306a36Sopenharmony_ci if ((pageflags & HEAD_PAGE_FLAGS) != HEAD_PAGE_FLAGS) { 7962306a36Sopenharmony_ci close(fd); 8062306a36Sopenharmony_ci printf("Head page flags (%lx) is invalid\n", pageflags); 8162306a36Sopenharmony_ci return -1; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * pages other than the first page must be tail and shouldn't be head; 8662306a36Sopenharmony_ci * this also verifies kernel has correctly set the fake page_head to tail 8762306a36Sopenharmony_ci * while hugetlb_free_vmemmap is enabled. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci for (i = 1; i < maplength / pagesize; i++) { 9062306a36Sopenharmony_ci read(fd, &pageflags, sizeof(pageflags)); 9162306a36Sopenharmony_ci if ((pageflags & TAIL_PAGE_FLAGS) != TAIL_PAGE_FLAGS || 9262306a36Sopenharmony_ci (pageflags & HEAD_PAGE_FLAGS) == HEAD_PAGE_FLAGS) { 9362306a36Sopenharmony_ci close(fd); 9462306a36Sopenharmony_ci printf("Tail page flags (%lx) is invalid\n", pageflags); 9562306a36Sopenharmony_ci return -1; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci close(fd); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint main(int argc, char **argv) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci void *addr; 10762306a36Sopenharmony_ci unsigned long pfn; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci pagesize = psize(); 11062306a36Sopenharmony_ci maplength = default_huge_page_size(); 11162306a36Sopenharmony_ci if (!maplength) { 11262306a36Sopenharmony_ci printf("Unable to determine huge page size\n"); 11362306a36Sopenharmony_ci exit(1); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci addr = mmap(MAP_ADDR, maplength, PROT_READ | PROT_WRITE, MAP_FLAGS, -1, 0); 11762306a36Sopenharmony_ci if (addr == MAP_FAILED) { 11862306a36Sopenharmony_ci perror("mmap"); 11962306a36Sopenharmony_ci exit(1); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Trigger allocation of HugeTLB page. */ 12362306a36Sopenharmony_ci write_bytes(addr, maplength); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci pfn = virt_to_pfn(addr); 12662306a36Sopenharmony_ci if (pfn == -1UL) { 12762306a36Sopenharmony_ci munmap(addr, maplength); 12862306a36Sopenharmony_ci perror("virt_to_pfn"); 12962306a36Sopenharmony_ci exit(1); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci printf("Returned address is %p whose pfn is %lx\n", addr, pfn); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (check_page_flags(pfn) < 0) { 13562306a36Sopenharmony_ci munmap(addr, maplength); 13662306a36Sopenharmony_ci perror("check_page_flags"); 13762306a36Sopenharmony_ci exit(1); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* munmap() length of MAP_HUGETLB memory must be hugepage aligned */ 14162306a36Sopenharmony_ci if (munmap(addr, maplength)) { 14262306a36Sopenharmony_ci perror("munmap"); 14362306a36Sopenharmony_ci exit(1); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 148