Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * hugepage-mremap:
0004  *
0005  * Example of remapping huge page memory in a user application using the
0006  * mremap system call.  The path to a file in a hugetlbfs filesystem must
0007  * be passed as the last argument to this test.  The amount of memory used
0008  * by this test in MBs can optionally be passed as an argument.  If no memory
0009  * amount is passed, the default amount is 10MB.
0010  *
0011  * To make sure the test triggers pmd sharing and goes through the 'unshare'
0012  * path in the mremap code use 1GB (1024) or more.
0013  */
0014 
0015 #define _GNU_SOURCE
0016 #include <stdlib.h>
0017 #include <stdio.h>
0018 #include <unistd.h>
0019 #include <sys/mman.h>
0020 #include <errno.h>
0021 #include <fcntl.h> /* Definition of O_* constants */
0022 #include <sys/syscall.h> /* Definition of SYS_* constants */
0023 #include <linux/userfaultfd.h>
0024 #include <sys/ioctl.h>
0025 
0026 #define DEFAULT_LENGTH_MB 10UL
0027 #define MB_TO_BYTES(x) (x * 1024 * 1024)
0028 
0029 #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC)
0030 #define FLAGS (MAP_SHARED | MAP_ANONYMOUS)
0031 
0032 static void check_bytes(char *addr)
0033 {
0034     printf("First hex is %x\n", *((unsigned int *)addr));
0035 }
0036 
0037 static void write_bytes(char *addr, size_t len)
0038 {
0039     unsigned long i;
0040 
0041     for (i = 0; i < len; i++)
0042         *(addr + i) = (char)i;
0043 }
0044 
0045 static int read_bytes(char *addr, size_t len)
0046 {
0047     unsigned long i;
0048 
0049     check_bytes(addr);
0050     for (i = 0; i < len; i++)
0051         if (*(addr + i) != (char)i) {
0052             printf("Mismatch at %lu\n", i);
0053             return 1;
0054         }
0055     return 0;
0056 }
0057 
0058 static void register_region_with_uffd(char *addr, size_t len)
0059 {
0060     long uffd; /* userfaultfd file descriptor */
0061     struct uffdio_api uffdio_api;
0062     struct uffdio_register uffdio_register;
0063 
0064     /* Create and enable userfaultfd object. */
0065 
0066     uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
0067     if (uffd == -1) {
0068         perror("userfaultfd");
0069         exit(1);
0070     }
0071 
0072     uffdio_api.api = UFFD_API;
0073     uffdio_api.features = 0;
0074     if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) {
0075         perror("ioctl-UFFDIO_API");
0076         exit(1);
0077     }
0078 
0079     /* Create a private anonymous mapping. The memory will be
0080      * demand-zero paged--that is, not yet allocated. When we
0081      * actually touch the memory, it will be allocated via
0082      * the userfaultfd.
0083      */
0084 
0085     addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
0086             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0087     if (addr == MAP_FAILED) {
0088         perror("mmap");
0089         exit(1);
0090     }
0091 
0092     printf("Address returned by mmap() = %p\n", addr);
0093 
0094     /* Register the memory range of the mapping we just created for
0095      * handling by the userfaultfd object. In mode, we request to track
0096      * missing pages (i.e., pages that have not yet been faulted in).
0097      */
0098 
0099     uffdio_register.range.start = (unsigned long)addr;
0100     uffdio_register.range.len = len;
0101     uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
0102     if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) {
0103         perror("ioctl-UFFDIO_REGISTER");
0104         exit(1);
0105     }
0106 }
0107 
0108 int main(int argc, char *argv[])
0109 {
0110     size_t length = 0;
0111 
0112     if (argc != 2 && argc != 3) {
0113         printf("Usage: %s [length_in_MB] <hugetlb_file>\n", argv[0]);
0114         exit(1);
0115     }
0116 
0117     /* Read memory length as the first arg if valid, otherwise fallback to
0118      * the default length.
0119      */
0120     if (argc == 3)
0121         length = argc > 2 ? (size_t)atoi(argv[1]) : 0UL;
0122 
0123     length = length > 0 ? length : DEFAULT_LENGTH_MB;
0124     length = MB_TO_BYTES(length);
0125 
0126     int ret = 0;
0127 
0128     /* last arg is the hugetlb file name */
0129     int fd = open(argv[argc-1], O_CREAT | O_RDWR, 0755);
0130 
0131     if (fd < 0) {
0132         perror("Open failed");
0133         exit(1);
0134     }
0135 
0136     /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */
0137     unsigned long suggested_addr = 0x7eaa40000000;
0138     void *haddr = mmap((void *)suggested_addr, length, PROTECTION,
0139                MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
0140     printf("Map haddr: Returned address is %p\n", haddr);
0141     if (haddr == MAP_FAILED) {
0142         perror("mmap1");
0143         exit(1);
0144     }
0145 
0146     /* mmap again to a dummy address to hopefully trigger pmd sharing. */
0147     suggested_addr = 0x7daa40000000;
0148     void *daddr = mmap((void *)suggested_addr, length, PROTECTION,
0149                MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
0150     printf("Map daddr: Returned address is %p\n", daddr);
0151     if (daddr == MAP_FAILED) {
0152         perror("mmap3");
0153         exit(1);
0154     }
0155 
0156     suggested_addr = 0x7faa40000000;
0157     void *vaddr =
0158         mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0);
0159     printf("Map vaddr: Returned address is %p\n", vaddr);
0160     if (vaddr == MAP_FAILED) {
0161         perror("mmap2");
0162         exit(1);
0163     }
0164 
0165     register_region_with_uffd(haddr, length);
0166 
0167     void *addr = mremap(haddr, length, length,
0168                 MREMAP_MAYMOVE | MREMAP_FIXED, vaddr);
0169     if (addr == MAP_FAILED) {
0170         perror("mremap");
0171         exit(1);
0172     }
0173 
0174     printf("Mremap: Returned address is %p\n", addr);
0175     check_bytes(addr);
0176     write_bytes(addr, length);
0177     ret = read_bytes(addr, length);
0178 
0179     munmap(addr, length);
0180 
0181     addr = mremap(addr, length, length, 0);
0182     if (addr != MAP_FAILED) {
0183         printf("mremap: Expected failure, but call succeeded\n");
0184         exit(1);
0185     }
0186 
0187     close(fd);
0188     unlink(argv[argc-1]);
0189 
0190     return ret;
0191 }