hostmem: add a new memory backend based on POSIX shm_open()

shm_open() creates and opens a new POSIX shared memory object.
A POSIX shared memory object allows creating memory backend with an
associated file descriptor that can be shared with external processes
(e.g. vhost-user).

The new `memory-backend-shm` can be used as an alternative when
`memory-backend-memfd` is not available (Linux only), since shm_open()
should be provided by any POSIX-compliant operating system.

This backend mimics memfd, allocating memory that is practically
anonymous. In theory shm_open() requires a name, but this is allocated
for a short time interval and shm_unlink() is called right after
shm_open(). After that, only fd is shared with external processes
(e.g., vhost-user) as if it were associated with anonymous memory.

In the future we may also allow the user to specify the name to be
passed to shm_open(), but for now we keep the backend simple, mimicking
anonymous memory such as memfd.

Acked-by: David Hildenbrand <david@redhat.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Acked-by: Markus Armbruster <armbru@redhat.com> (QAPI schema)
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Message-Id: <20240618100519.145853-1-sgarzare@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Stefano Garzarella 2024-06-18 12:05:19 +02:00 committed by Michael S. Tsirkin
parent 5ab04420c3
commit 4e647fa085
5 changed files with 164 additions and 4 deletions

123
backends/hostmem-shm.c Normal file
View File

@ -0,0 +1,123 @@
/*
* QEMU host POSIX shared memory object backend
*
* Copyright (C) 2024 Red Hat Inc
*
* Authors:
* Stefano Garzarella <sgarzare@redhat.com>
*
* 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 "sysemu/hostmem.h"
#include "qapi/error.h"
#define TYPE_MEMORY_BACKEND_SHM "memory-backend-shm"
OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendShm, MEMORY_BACKEND_SHM)
struct HostMemoryBackendShm {
HostMemoryBackend parent_obj;
};
static bool
shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{
g_autoptr(GString) shm_name = g_string_new(NULL);
g_autofree char *backend_name = NULL;
uint32_t ram_flags;
int fd, oflag;
mode_t mode;
if (!backend->size) {
error_setg(errp, "can't create shm backend with size 0");
return false;
}
if (!backend->share) {
error_setg(errp, "can't create shm backend with `share=off`");
return false;
}
/*
* Let's use `mode = 0` because we don't want other processes to open our
* memory unless we share the file descriptor with them.
*/
mode = 0;
oflag = O_RDWR | O_CREAT | O_EXCL;
backend_name = host_memory_backend_get_name(backend);
/*
* Some operating systems allow creating anonymous POSIX shared memory
* objects (e.g. FreeBSD provides the SHM_ANON constant), but this is not
* defined by POSIX, so let's create a unique name.
*
* From Linux's shm_open(3) man-page:
* For portable use, a shared memory object should be identified
* by a name of the form /somename;"
*/
g_string_printf(shm_name, "/qemu-" FMT_pid "-shm-%s", getpid(),
backend_name);
fd = shm_open(shm_name->str, oflag, mode);
if (fd < 0) {
error_setg_errno(errp, errno,
"failed to create POSIX shared memory");
return false;
}
/*
* We have the file descriptor, so we no longer need to expose the
* POSIX shared memory object. However it will remain allocated as long as
* there are file descriptors pointing to it.
*/
shm_unlink(shm_name->str);
if (ftruncate(fd, backend->size) == -1) {
error_setg_errno(errp, errno,
"failed to resize POSIX shared memory to %" PRIu64,
backend->size);
close(fd);
return false;
}
ram_flags = RAM_SHARED;
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
backend_name, backend->size,
ram_flags, fd, 0, errp);
}
static void
shm_backend_instance_init(Object *obj)
{
HostMemoryBackendShm *m = MEMORY_BACKEND_SHM(obj);
MEMORY_BACKEND(m)->share = true;
}
static void
shm_backend_class_init(ObjectClass *oc, void *data)
{
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
bc->alloc = shm_backend_memory_alloc;
}
static const TypeInfo shm_backend_info = {
.name = TYPE_MEMORY_BACKEND_SHM,
.parent = TYPE_MEMORY_BACKEND,
.instance_init = shm_backend_instance_init,
.class_init = shm_backend_class_init,
.instance_size = sizeof(HostMemoryBackendShm),
};
static void register_types(void)
{
type_register_static(&shm_backend_info);
}
type_init(register_types);

View File

@ -13,6 +13,7 @@ system_ss.add([files(
if host_os != 'windows' if host_os != 'windows'
system_ss.add(files('rng-random.c')) system_ss.add(files('rng-random.c'))
system_ss.add(files('hostmem-file.c')) system_ss.add(files('hostmem-file.c'))
system_ss.add([files('hostmem-shm.c'), rt])
endif endif
if host_os == 'linux' if host_os == 'linux'
system_ss.add(files('hostmem-memfd.c')) system_ss.add(files('hostmem-memfd.c'))

View File

@ -98,8 +98,9 @@ Shared memory object
In order for the daemon to access the VirtIO queues to process the In order for the daemon to access the VirtIO queues to process the
requests it needs access to the guest's address space. This is requests it needs access to the guest's address space. This is
achieved via the ``memory-backend-file`` or ``memory-backend-memfd`` achieved via the ``memory-backend-file``, ``memory-backend-memfd``, or
objects. A reference to a file-descriptor which can access this object ``memory-backend-shm`` objects.
A reference to a file-descriptor which can access this object
will be passed via the socket as part of the protocol negotiation. will be passed via the socket as part of the protocol negotiation.
Currently the shared memory object needs to match the size of the main Currently the shared memory object needs to match the size of the main

View File

@ -601,8 +601,8 @@
# #
# @share: if false, the memory is private to QEMU; if true, it is # @share: if false, the memory is private to QEMU; if true, it is
# shared (default false for backends memory-backend-file and # shared (default false for backends memory-backend-file and
# memory-backend-ram, true for backends memory-backend-epc and # memory-backend-ram, true for backends memory-backend-epc,
# memory-backend-memfd) # memory-backend-memfd, and memory-backend-shm)
# #
# @reserve: if true, reserve swap space (or huge pages) if applicable # @reserve: if true, reserve swap space (or huge pages) if applicable
# (default: true) (since 6.1) # (default: true) (since 6.1)
@ -721,6 +721,21 @@
'*hugetlbsize': 'size', '*hugetlbsize': 'size',
'*seal': 'bool' } } '*seal': 'bool' } }
##
# @MemoryBackendShmProperties:
#
# Properties for memory-backend-shm objects.
#
# This memory backend supports only shared memory, which is the
# default.
#
# Since: 9.1
##
{ 'struct': 'MemoryBackendShmProperties',
'base': 'MemoryBackendProperties',
'data': { },
'if': 'CONFIG_POSIX' }
## ##
# @MemoryBackendEpcProperties: # @MemoryBackendEpcProperties:
# #
@ -1049,6 +1064,8 @@
{ 'name': 'memory-backend-memfd', { 'name': 'memory-backend-memfd',
'if': 'CONFIG_LINUX' }, 'if': 'CONFIG_LINUX' },
'memory-backend-ram', 'memory-backend-ram',
{ 'name': 'memory-backend-shm',
'if': 'CONFIG_POSIX' },
'pef-guest', 'pef-guest',
{ 'name': 'pr-manager-helper', { 'name': 'pr-manager-helper',
'if': 'CONFIG_LINUX' }, 'if': 'CONFIG_LINUX' },
@ -1121,6 +1138,8 @@
'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties', 'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties',
'if': 'CONFIG_LINUX' }, 'if': 'CONFIG_LINUX' },
'memory-backend-ram': 'MemoryBackendProperties', 'memory-backend-ram': 'MemoryBackendProperties',
'memory-backend-shm': { 'type': 'MemoryBackendShmProperties',
'if': 'CONFIG_POSIX' },
'pr-manager-helper': { 'type': 'PrManagerHelperProperties', 'pr-manager-helper': { 'type': 'PrManagerHelperProperties',
'if': 'CONFIG_LINUX' }, 'if': 'CONFIG_LINUX' },
'qtest': 'QtestProperties', 'qtest': 'QtestProperties',

View File

@ -5240,6 +5240,22 @@ SRST
The ``share`` boolean option is on by default with memfd. The ``share`` boolean option is on by default with memfd.
``-object memory-backend-shm,id=id,merge=on|off,dump=on|off,share=on|off,prealloc=on|off,size=size,host-nodes=host-nodes,policy=default|preferred|bind|interleave``
Creates a POSIX shared memory backend object, which allows
QEMU to share the memory with an external process (e.g. when
using vhost-user).
``memory-backend-shm`` is a more portable and less featureful version
of ``memory-backend-memfd``. It can then be used in any POSIX system,
especially when memfd is not supported.
Please refer to ``memory-backend-file`` for a description of the
options.
The ``share`` boolean option is on by default with shm. Setting it to
off will cause a failure during allocation because it is not supported
by this backend.
``-object iommufd,id=id[,fd=fd]`` ``-object iommufd,id=id[,fd=fd]``
Creates an iommufd backend which allows control of DMA mapping Creates an iommufd backend which allows control of DMA mapping
through the ``/dev/iommu`` device. through the ``/dev/iommu`` device.