hugepage-mremap.c (4858B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * hugepage-mremap: 4 * 5 * Example of remapping huge page memory in a user application using the 6 * mremap system call. The path to a file in a hugetlbfs filesystem must 7 * be passed as the last argument to this test. The amount of memory used 8 * by this test in MBs can optionally be passed as an argument. If no memory 9 * amount is passed, the default amount is 10MB. 10 * 11 * To make sure the test triggers pmd sharing and goes through the 'unshare' 12 * path in the mremap code use 1GB (1024) or more. 13 */ 14 15#define _GNU_SOURCE 16#include <stdlib.h> 17#include <stdio.h> 18#include <unistd.h> 19#include <sys/mman.h> 20#include <errno.h> 21#include <fcntl.h> /* Definition of O_* constants */ 22#include <sys/syscall.h> /* Definition of SYS_* constants */ 23#include <linux/userfaultfd.h> 24#include <sys/ioctl.h> 25 26#define DEFAULT_LENGTH_MB 10UL 27#define MB_TO_BYTES(x) (x * 1024 * 1024) 28 29#define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC) 30#define FLAGS (MAP_SHARED | MAP_ANONYMOUS) 31 32static void check_bytes(char *addr) 33{ 34 printf("First hex is %x\n", *((unsigned int *)addr)); 35} 36 37static void write_bytes(char *addr, size_t len) 38{ 39 unsigned long i; 40 41 for (i = 0; i < len; i++) 42 *(addr + i) = (char)i; 43} 44 45static int read_bytes(char *addr, size_t len) 46{ 47 unsigned long i; 48 49 check_bytes(addr); 50 for (i = 0; i < len; i++) 51 if (*(addr + i) != (char)i) { 52 printf("Mismatch at %lu\n", i); 53 return 1; 54 } 55 return 0; 56} 57 58static void register_region_with_uffd(char *addr, size_t len) 59{ 60 long uffd; /* userfaultfd file descriptor */ 61 struct uffdio_api uffdio_api; 62 struct uffdio_register uffdio_register; 63 64 /* Create and enable userfaultfd object. */ 65 66 uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); 67 if (uffd == -1) { 68 perror("userfaultfd"); 69 exit(1); 70 } 71 72 uffdio_api.api = UFFD_API; 73 uffdio_api.features = 0; 74 if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) { 75 perror("ioctl-UFFDIO_API"); 76 exit(1); 77 } 78 79 /* Create a private anonymous mapping. The memory will be 80 * demand-zero paged--that is, not yet allocated. When we 81 * actually touch the memory, it will be allocated via 82 * the userfaultfd. 83 */ 84 85 addr = mmap(NULL, len, PROT_READ | PROT_WRITE, 86 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 87 if (addr == MAP_FAILED) { 88 perror("mmap"); 89 exit(1); 90 } 91 92 printf("Address returned by mmap() = %p\n", addr); 93 94 /* Register the memory range of the mapping we just created for 95 * handling by the userfaultfd object. In mode, we request to track 96 * missing pages (i.e., pages that have not yet been faulted in). 97 */ 98 99 uffdio_register.range.start = (unsigned long)addr; 100 uffdio_register.range.len = len; 101 uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; 102 if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) { 103 perror("ioctl-UFFDIO_REGISTER"); 104 exit(1); 105 } 106} 107 108int main(int argc, char *argv[]) 109{ 110 size_t length; 111 112 if (argc != 2 && argc != 3) { 113 printf("Usage: %s [length_in_MB] <hugetlb_file>\n", argv[0]); 114 exit(1); 115 } 116 117 /* Read memory length as the first arg if valid, otherwise fallback to 118 * the default length. 119 */ 120 if (argc == 3) 121 length = argc > 2 ? (size_t)atoi(argv[1]) : 0UL; 122 123 length = length > 0 ? length : DEFAULT_LENGTH_MB; 124 length = MB_TO_BYTES(length); 125 126 int ret = 0; 127 128 /* last arg is the hugetlb file name */ 129 int fd = open(argv[argc-1], O_CREAT | O_RDWR, 0755); 130 131 if (fd < 0) { 132 perror("Open failed"); 133 exit(1); 134 } 135 136 /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */ 137 unsigned long suggested_addr = 0x7eaa40000000; 138 void *haddr = mmap((void *)suggested_addr, length, PROTECTION, 139 MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); 140 printf("Map haddr: Returned address is %p\n", haddr); 141 if (haddr == MAP_FAILED) { 142 perror("mmap1"); 143 exit(1); 144 } 145 146 /* mmap again to a dummy address to hopefully trigger pmd sharing. */ 147 suggested_addr = 0x7daa40000000; 148 void *daddr = mmap((void *)suggested_addr, length, PROTECTION, 149 MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); 150 printf("Map daddr: Returned address is %p\n", daddr); 151 if (daddr == MAP_FAILED) { 152 perror("mmap3"); 153 exit(1); 154 } 155 156 suggested_addr = 0x7faa40000000; 157 void *vaddr = 158 mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0); 159 printf("Map vaddr: Returned address is %p\n", vaddr); 160 if (vaddr == MAP_FAILED) { 161 perror("mmap2"); 162 exit(1); 163 } 164 165 register_region_with_uffd(haddr, length); 166 167 void *addr = mremap(haddr, length, length, 168 MREMAP_MAYMOVE | MREMAP_FIXED, vaddr); 169 if (addr == MAP_FAILED) { 170 perror("mremap"); 171 exit(1); 172 } 173 174 printf("Mremap: Returned address is %p\n", addr); 175 check_bytes(addr); 176 write_bytes(addr, length); 177 ret = read_bytes(addr, length); 178 179 munmap(addr, length); 180 181 addr = mremap(addr, length, length, 0); 182 if (addr != MAP_FAILED) { 183 printf("mremap: Expected failure, but call succeeded\n"); 184 exit(1); 185 } 186 187 close(fd); 188 unlink(argv[argc-1]); 189 190 return ret; 191}