qemu/util/memfd.c
<<
>>
Prefs
   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/error-report.h"
  32#include "qemu/memfd.h"
  33#include "qemu/host-utils.h"
  34
  35#if defined CONFIG_LINUX && !defined CONFIG_MEMFD
  36#include <sys/syscall.h>
  37#include <asm/unistd.h>
  38
  39int memfd_create(const char *name, unsigned int flags)
  40{
  41#ifdef __NR_memfd_create
  42    return syscall(__NR_memfd_create, name, flags);
  43#else
  44    errno = ENOSYS;
  45    return -1;
  46#endif
  47}
  48#endif
  49
  50int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
  51                      uint64_t hugetlbsize, unsigned int seals, Error **errp)
  52{
  53    int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0;
  54
  55    if (htsize && 1ULL << htsize != hugetlbsize) {
  56        error_setg(errp, "Hugepage size must be a power of 2");
  57        return -1;
  58    }
  59
  60    htsize = htsize << MFD_HUGE_SHIFT;
  61
  62#ifdef CONFIG_LINUX
  63    int mfd = -1;
  64    unsigned int flags = MFD_CLOEXEC;
  65
  66    if (seals) {
  67        flags |= MFD_ALLOW_SEALING;
  68    }
  69    if (hugetlb) {
  70        flags |= MFD_HUGETLB;
  71        flags |= htsize;
  72    }
  73    mfd = memfd_create(name, flags);
  74    if (mfd < 0) {
  75        error_setg_errno(errp, errno,
  76                         "failed to create memfd with flags 0x%x", flags);
  77        goto err;
  78    }
  79
  80    if (ftruncate(mfd, size) == -1) {
  81        error_setg_errno(errp, errno, "failed to resize memfd to %zu", size);
  82        goto err;
  83    }
  84
  85    if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
  86        error_setg_errno(errp, errno, "failed to add seals 0x%x", seals);
  87        goto err;
  88    }
  89
  90    return mfd;
  91
  92err:
  93    if (mfd >= 0) {
  94        close(mfd);
  95    }
  96#else
  97    error_setg_errno(errp, ENOSYS, "failed to create memfd");
  98#endif
  99    return -1;
 100}
 101
 102/*
 103 * This is a best-effort helper for shared memory allocation, with
 104 * optional sealing. The helper will do his best to allocate using
 105 * memfd with sealing, but may fallback on other methods without
 106 * sealing.
 107 */
 108void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
 109                       int *fd, Error **errp)
 110{
 111    void *ptr;
 112    int mfd = qemu_memfd_create(name, size, false, 0, seals, NULL);
 113
 114    /* some systems have memfd without sealing */
 115    if (mfd == -1) {
 116        mfd = qemu_memfd_create(name, size, false, 0, 0, NULL);
 117    }
 118
 119    if (mfd == -1) {
 120        const char *tmpdir = g_get_tmp_dir();
 121        gchar *fname;
 122
 123        fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir);
 124        mfd = mkstemp(fname);
 125        unlink(fname);
 126        g_free(fname);
 127
 128        if (mfd == -1 ||
 129            ftruncate(mfd, size) == -1) {
 130            goto err;
 131        }
 132    }
 133
 134    ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
 135    if (ptr == MAP_FAILED) {
 136        goto err;
 137    }
 138
 139    *fd = mfd;
 140    return ptr;
 141
 142err:
 143    error_setg_errno(errp, errno, "failed to allocate shared memory");
 144    if (mfd >= 0) {
 145        close(mfd);
 146    }
 147    return NULL;
 148}
 149
 150void qemu_memfd_free(void *ptr, size_t size, int fd)
 151{
 152    if (ptr) {
 153        if (munmap(ptr, size) != 0) {
 154            error_report("memfd munmap() failed: %s", strerror(errno));
 155        }
 156    }
 157
 158    if (fd != -1) {
 159        if (close(fd) != 0) {
 160            error_report("memfd close() failed: %s", strerror(errno));
 161        }
 162    }
 163}
 164
 165enum {
 166    MEMFD_KO,
 167    MEMFD_OK,
 168    MEMFD_TODO
 169};
 170
 171/**
 172 * qemu_memfd_alloc_check():
 173 *
 174 * Check if qemu_memfd_alloc() can allocate, including using a
 175 * fallback implementation when host doesn't support memfd.
 176 */
 177bool qemu_memfd_alloc_check(void)
 178{
 179    static int memfd_check = MEMFD_TODO;
 180
 181    if (memfd_check == MEMFD_TODO) {
 182        int fd;
 183        void *ptr;
 184
 185        fd = -1;
 186        ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL);
 187        memfd_check = ptr ? MEMFD_OK : MEMFD_KO;
 188        qemu_memfd_free(ptr, 4096, fd);
 189    }
 190
 191    return memfd_check == MEMFD_OK;
 192}
 193
 194/**
 195 * qemu_memfd_check():
 196 *
 197 * Check if host supports memfd.  Cache the answer for the common case flags=0.
 198 */
 199bool qemu_memfd_check(unsigned int flags)
 200{
 201#ifdef CONFIG_LINUX
 202    int mfd;
 203    static int memfd_check = MEMFD_TODO;
 204
 205    if (!flags && memfd_check != MEMFD_TODO) {
 206        return memfd_check;
 207    }
 208
 209    mfd = memfd_create("test", flags | MFD_CLOEXEC);
 210    if (mfd >= 0) {
 211        close(mfd);
 212    }
 213    if (!flags) {
 214        memfd_check = (mfd >= 0) ? MEMFD_OK : MEMFD_KO;
 215    }
 216    return (mfd >= 0);
 217
 218#endif
 219
 220    return false;
 221}
 222