qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

memfd.c (5083B)


      1 /*
      2  * memfd.c
      3  *
      4  * Copyright (c) 2015 Red Hat, Inc.
      5  *
      6  * QEMU library functions on POSIX which are shared between QEMU and
      7  * the QEMU tools.
      8  *
      9  * Permission is hereby granted, free of charge, to any person obtaining a copy
     10  * of this software and associated documentation files (the "Software"), to deal
     11  * in the Software without restriction, including without limitation the rights
     12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     13  * copies of the Software, and to permit persons to whom the Software is
     14  * furnished to do so, subject to the following conditions:
     15  *
     16  * The above copyright notice and this permission notice shall be included in
     17  * all copies or substantial portions of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     25  * THE SOFTWARE.
     26  */
     27 
     28 #include "qemu/osdep.h"
     29 
     30 #include "qapi/error.h"
     31 #include "qemu/memfd.h"
     32 #include "qemu/host-utils.h"
     33 
     34 #if defined CONFIG_LINUX && !defined CONFIG_MEMFD
     35 #include <sys/syscall.h>
     36 #include <asm/unistd.h>
     37 
     38 int memfd_create(const char *name, unsigned int flags)
     39 {
     40 #ifdef __NR_memfd_create
     41     return syscall(__NR_memfd_create, name, flags);
     42 #else
     43     errno = ENOSYS;
     44     return -1;
     45 #endif
     46 }
     47 #endif
     48 
     49 int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
     50                       uint64_t hugetlbsize, unsigned int seals, Error **errp)
     51 {
     52     int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0;
     53 
     54     if (htsize && 1ULL << htsize != hugetlbsize) {
     55         error_setg(errp, "Hugepage size must be a power of 2");
     56         return -1;
     57     }
     58 
     59     htsize = htsize << MFD_HUGE_SHIFT;
     60 
     61 #ifdef CONFIG_LINUX
     62     int mfd = -1;
     63     unsigned int flags = MFD_CLOEXEC;
     64 
     65     if (seals) {
     66         flags |= MFD_ALLOW_SEALING;
     67     }
     68     if (hugetlb) {
     69         flags |= MFD_HUGETLB;
     70         flags |= htsize;
     71     }
     72     mfd = memfd_create(name, flags);
     73     if (mfd < 0) {
     74         error_setg_errno(errp, errno,
     75                          "failed to create memfd with flags 0x%x", flags);
     76         goto err;
     77     }
     78 
     79     if (ftruncate(mfd, size) == -1) {
     80         error_setg_errno(errp, errno, "failed to resize memfd to %zu", size);
     81         goto err;
     82     }
     83 
     84     if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
     85         error_setg_errno(errp, errno, "failed to add seals 0x%x", seals);
     86         goto err;
     87     }
     88 
     89     return mfd;
     90 
     91 err:
     92     if (mfd >= 0) {
     93         close(mfd);
     94     }
     95 #else
     96     error_setg_errno(errp, ENOSYS, "failed to create memfd");
     97 #endif
     98     return -1;
     99 }
    100 
    101 /*
    102  * This is a best-effort helper for shared memory allocation, with
    103  * optional sealing. The helper will do his best to allocate using
    104  * memfd with sealing, but may fallback on other methods without
    105  * sealing.
    106  */
    107 void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
    108                        int *fd, Error **errp)
    109 {
    110     void *ptr;
    111     int mfd = qemu_memfd_create(name, size, false, 0, seals, NULL);
    112 
    113     /* some systems have memfd without sealing */
    114     if (mfd == -1) {
    115         mfd = qemu_memfd_create(name, size, false, 0, 0, NULL);
    116     }
    117 
    118     if (mfd == -1) {
    119         const char *tmpdir = g_get_tmp_dir();
    120         gchar *fname;
    121 
    122         fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir);
    123         mfd = mkstemp(fname);
    124         unlink(fname);
    125         g_free(fname);
    126 
    127         if (mfd == -1 ||
    128             ftruncate(mfd, size) == -1) {
    129             goto err;
    130         }
    131     }
    132 
    133     ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
    134     if (ptr == MAP_FAILED) {
    135         goto err;
    136     }
    137 
    138     *fd = mfd;
    139     return ptr;
    140 
    141 err:
    142     error_setg_errno(errp, errno, "failed to allocate shared memory");
    143     if (mfd >= 0) {
    144         close(mfd);
    145     }
    146     return NULL;
    147 }
    148 
    149 void qemu_memfd_free(void *ptr, size_t size, int fd)
    150 {
    151     if (ptr) {
    152         munmap(ptr, size);
    153     }
    154 
    155     if (fd != -1) {
    156         close(fd);
    157     }
    158 }
    159 
    160 enum {
    161     MEMFD_KO,
    162     MEMFD_OK,
    163     MEMFD_TODO
    164 };
    165 
    166 /**
    167  * qemu_memfd_alloc_check():
    168  *
    169  * Check if qemu_memfd_alloc() can allocate, including using a
    170  * fallback implementation when host doesn't support memfd.
    171  */
    172 bool qemu_memfd_alloc_check(void)
    173 {
    174     static int memfd_check = MEMFD_TODO;
    175 
    176     if (memfd_check == MEMFD_TODO) {
    177         int fd;
    178         void *ptr;
    179 
    180         fd = -1;
    181         ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL);
    182         memfd_check = ptr ? MEMFD_OK : MEMFD_KO;
    183         qemu_memfd_free(ptr, 4096, fd);
    184     }
    185 
    186     return memfd_check == MEMFD_OK;
    187 }
    188 
    189 /**
    190  * qemu_memfd_check():
    191  *
    192  * Check if host supports memfd.
    193  */
    194 bool qemu_memfd_check(unsigned int flags)
    195 {
    196 #ifdef CONFIG_LINUX
    197     int mfd = memfd_create("test", flags | MFD_CLOEXEC);
    198 
    199     if (mfd >= 0) {
    200         close(mfd);
    201         return true;
    202     }
    203 #endif
    204 
    205     return false;
    206 }