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