diff --git a/backends/Makefile.objs b/backends/Makefile.objs index 0400799efd..67eeeba5fc 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -8,3 +8,5 @@ common-obj-$(CONFIG_LINUX) += hostmem-file.o common-obj-y += cryptodev.o common-obj-y += cryptodev-builtin.o + +common-obj-$(CONFIG_LINUX) += hostmem-memfd.o diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c new file mode 100644 index 0000000000..1e20fe0ba8 --- /dev/null +++ b/backends/hostmem-memfd.c @@ -0,0 +1,170 @@ +/* + * QEMU host memfd memory backend + * + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/hostmem.h" +#include "sysemu/sysemu.h" +#include "qom/object_interfaces.h" +#include "qemu/memfd.h" +#include "qapi/error.h" + +#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" + +#define MEMORY_BACKEND_MEMFD(obj) \ + OBJECT_CHECK(HostMemoryBackendMemfd, (obj), TYPE_MEMORY_BACKEND_MEMFD) + +typedef struct HostMemoryBackendMemfd HostMemoryBackendMemfd; + +struct HostMemoryBackendMemfd { + HostMemoryBackend parent_obj; + + bool hugetlb; + uint64_t hugetlbsize; + bool seal; +}; + +static void +memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend); + char *name; + int fd; + + if (!backend->size) { + error_setg(errp, "can't create backend with size 0"); + return; + } + + if (host_memory_backend_mr_inited(backend)) { + return; + } + + backend->force_prealloc = mem_prealloc; + fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, + m->hugetlb, m->hugetlbsize, m->seal ? + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0, + errp); + if (fd == -1) { + return; + } + + name = object_get_canonical_path(OBJECT(backend)); + memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), + name, backend->size, true, fd, errp); + g_free(name); +} + +static bool +memfd_backend_get_hugetlb(Object *o, Error **errp) +{ + return MEMORY_BACKEND_MEMFD(o)->hugetlb; +} + +static void +memfd_backend_set_hugetlb(Object *o, bool value, Error **errp) +{ + MEMORY_BACKEND_MEMFD(o)->hugetlb = value; +} + +static void +memfd_backend_set_hugetlbsize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); + Error *local_err = NULL; + uint64_t value; + + if (host_memory_backend_mr_inited(MEMORY_BACKEND(obj))) { + error_setg(&local_err, "cannot change property value"); + goto out; + } + + visit_type_size(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (!value) { + error_setg(&local_err, "Property '%s.%s' doesn't take value '%" + PRIu64 "'", object_get_typename(obj), name, value); + goto out; + } + m->hugetlbsize = value; +out: + error_propagate(errp, local_err); +} + +static void +memfd_backend_get_hugetlbsize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); + uint64_t value = m->hugetlbsize; + + visit_type_size(v, name, &value, errp); +} + +static bool +memfd_backend_get_seal(Object *o, Error **errp) +{ + return MEMORY_BACKEND_MEMFD(o)->seal; +} + +static void +memfd_backend_set_seal(Object *o, bool value, Error **errp) +{ + MEMORY_BACKEND_MEMFD(o)->seal = value; +} + +static void +memfd_backend_instance_init(Object *obj) +{ + HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj); + + /* default to sealed file */ + m->seal = true; +} + +static void +memfd_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = memfd_backend_memory_alloc; + + object_class_property_add_bool(oc, "hugetlb", + memfd_backend_get_hugetlb, + memfd_backend_set_hugetlb, + &error_abort); + object_class_property_add(oc, "hugetlbsize", "int", + memfd_backend_get_hugetlbsize, + memfd_backend_set_hugetlbsize, + NULL, NULL, &error_abort); + object_class_property_add_bool(oc, "seal", + memfd_backend_get_seal, + memfd_backend_set_seal, + &error_abort); +} + +static const TypeInfo memfd_backend_info = { + .name = TYPE_MEMORY_BACKEND_MEMFD, + .parent = TYPE_MEMORY_BACKEND, + .instance_init = memfd_backend_instance_init, + .class_init = memfd_backend_class_init, + .instance_size = sizeof(HostMemoryBackendMemfd), +}; + +static void register_types(void) +{ + type_register_static(&memfd_backend_info); +} + +type_init(register_types); diff --git a/qemu-options.hx b/qemu-options.hx index 8ce427da78..265cf9fa38 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4024,6 +4024,28 @@ Memory backend objects offer more control than the @option{-m} option that is traditionally used to define guest RAM. Please refer to @option{memory-backend-file} for a description of the options. +@item -object memory-backend-memfd,id=@var{id},merge=@var{on|off},dump=@var{on|off},prealloc=@var{on|off},size=@var{size},host-nodes=@var{host-nodes},policy=@var{default|preferred|bind|interleave},seal=@var{on|off},hugetlb=@var{on|off},hugetlbsize=@var{size} + +Creates an anonymous memory file backend object, which allows QEMU to +share the memory with an external process (e.g. when using +vhost-user). The memory is allocated with memfd and optional +sealing. (Linux only) + +The @option{seal} option creates a sealed-file, that will block +further resizing the memory ('on' by default). + +The @option{hugetlb} option specify the file to be created resides in +the hugetlbfs filesystem (since Linux 4.14). Used in conjunction with +the @option{hugetlb} option, the @option{hugetlbsize} option specify +the hugetlb page size on systems that support multiple hugetlb page +sizes (it must be a power of 2 value supported by the system). + +In some versions of Linux, the @option{hugetlb} option is incompatible +with the @option{seal} option (requires at least Linux 4.16). + +Please refer to @option{memory-backend-file} for a description of the +other options. + @item -object rng-random,id=@var{id},filename=@var{/dev/random} Creates a random number generator backend which obtains entropy from