mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/elmarco/tags/dbus-vmstate7-pull-request' into staging
Add dbus-vmstate Hi, With external processes or helpers participating to the VM support, it becomes necessary to handle their migration. Various options exist to transfer their state: 1) as the VM memory, RAM or devices (we could say that's how vhost-user devices can be handled today, they are expected to restore from ring state) 2) other "vmstate" (as with TPM emulator state blobs) 3) left to be handled by management layer 1) is not practical, since an external processes may legitimatelly need arbitrary state date to back a device or a service, or may not even have an associated device. 2) needs ad-hoc code for each helper, but is simple and working 3) is complicated for management layer, QEMU has the migration timing The proposed "dbus-vmstate" object will connect to a given D-Bus address, and save/load from org.qemu.VMState1 owners on migration. Thus helpers can easily have their state migrated with QEMU, without implementing ad-hoc support (such as done for TPM emulation) D-Bus is ubiquitous on Linux (it is systemd IPC), and can be made to work on various other OSes. There are several implementations and good bindings for various languages. (the tests/dbus-vmstate-test.c is a good example of how simple the implementation of services can be, even in C) dbus-vmstate is put into use by the libvirt series "[PATCH 00/23] Use a slirp helper process". v2: - fix build with broken mingw-glib # gpg: Signature made Mon 06 Jan 2020 14:43:35 GMT # gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5 # gpg: issuer "marcandre.lureau@redhat.com" # gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full] # gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full] # Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5 * remotes/elmarco/tags/dbus-vmstate7-pull-request: tests: add dbus-vmstate-test tests: add migration-helpers unit dockerfiles: add dbus-daemon to some of latest distributions configure: add GDBUS_CODEGEN Add dbus-vmstate object util: add dbus helper unit docs: start a document to describe D-Bus usage vmstate: replace DeviceState with VMStateIf vmstate: add qom interface to get id Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+12
@@ -2196,6 +2196,8 @@ Migration
|
||||
M: Juan Quintela <quintela@redhat.com>
|
||||
M: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/core/vmstate-if.c
|
||||
F: include/hw/vmstate-if.h
|
||||
F: include/migration/
|
||||
F: migration/
|
||||
F: scripts/vmstate-static-checker.py
|
||||
@@ -2204,6 +2206,16 @@ F: tests/migration-test.c
|
||||
F: docs/devel/migration.rst
|
||||
F: qapi/migration.json
|
||||
|
||||
D-Bus
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
S: Maintained
|
||||
F: backends/dbus-vmstate.c
|
||||
F: tests/dbus-vmstate*
|
||||
F: util/dbus.c
|
||||
F: include/qemu/dbus.h
|
||||
F: docs/interop/dbus.rst
|
||||
F: docs/interop/dbus-vmstate.rst
|
||||
|
||||
Seccomp
|
||||
M: Eduardo Otubo <otubo@redhat.com>
|
||||
S: Supported
|
||||
|
||||
@@ -128,6 +128,7 @@ vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
|
||||
trace-events-subdirs =
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += backends
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += monitor
|
||||
ifeq ($(CONFIG_USER_ONLY),y)
|
||||
|
||||
@@ -17,3 +17,7 @@ endif
|
||||
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
|
||||
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
|
||||
|
||||
common-obj-$(CONFIG_GIO) += dbus-vmstate.o
|
||||
dbus-vmstate.o-cflags = $(GIO_CFLAGS)
|
||||
dbus-vmstate.o-libs = $(GIO_LIBS)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# dbus-vmstate.c
|
||||
dbus_vmstate_pre_save(void)
|
||||
dbus_vmstate_post_load(int version_id) "version_id: %d"
|
||||
dbus_vmstate_loading(const char *id) "id: %s"
|
||||
dbus_vmstate_saving(const char *id) "id: %s"
|
||||
@@ -3701,10 +3701,16 @@ if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then
|
||||
gio=yes
|
||||
gio_cflags=$($pkg_config --cflags gio-2.0)
|
||||
gio_libs=$($pkg_config --libs gio-2.0)
|
||||
gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0)
|
||||
else
|
||||
gio=no
|
||||
fi
|
||||
|
||||
if $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then
|
||||
gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)"
|
||||
gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)"
|
||||
fi
|
||||
|
||||
# Sanity check that the current size_t matches the
|
||||
# size that glib thinks it should be. This catches
|
||||
# problems on multi-arch where people try to build
|
||||
@@ -6904,6 +6910,7 @@ if test "$gio" = "yes" ; then
|
||||
echo "CONFIG_GIO=y" >> $config_host_mak
|
||||
echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak
|
||||
echo "GIO_LIBS=$gio_libs" >> $config_host_mak
|
||||
echo "GDBUS_CODEGEN=$gdbus_codegen" >> $config_host_mak
|
||||
fi
|
||||
echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak
|
||||
if test "$gnutls" = "yes" ; then
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
=============
|
||||
D-Bus VMState
|
||||
=============
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The QEMU dbus-vmstate object's aim is to migrate helpers' data running
|
||||
on a QEMU D-Bus bus. (refer to the :doc:`dbus` document for
|
||||
some recommendations on D-Bus usage)
|
||||
|
||||
Upon migration, QEMU will go through the queue of
|
||||
``org.qemu.VMState1`` D-Bus name owners and query their ``Id``. It
|
||||
must be unique among the helpers.
|
||||
|
||||
It will then save arbitrary data of each Id to be transferred in the
|
||||
migration stream and restored/loaded at the corresponding destination
|
||||
helper.
|
||||
|
||||
For now, the data amount to be transferred is arbitrarily limited to
|
||||
1Mb. The state must be saved quickly (a fraction of a second). (D-Bus
|
||||
imposes a time limit on reply anyway, and migration would fail if data
|
||||
isn't given quickly enough.)
|
||||
|
||||
dbus-vmstate object can be configured with the expected list of
|
||||
helpers by setting its ``id-list`` property, with a comma-separated
|
||||
``Id`` list.
|
||||
|
||||
Interface
|
||||
=========
|
||||
|
||||
On object path ``/org/qemu/VMState1``, the following
|
||||
``org.qemu.VMState1`` interface should be implemented:
|
||||
|
||||
.. code:: xml
|
||||
|
||||
<interface name="org.qemu.VMState1">
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<method name="Load">
|
||||
<arg type="ay" name="data" direction="in"/>
|
||||
</method>
|
||||
<method name="Save">
|
||||
<arg type="ay" name="data" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
|
||||
"Id" property
|
||||
-------------
|
||||
|
||||
A string that identifies the helper uniquely. (maximum 256 bytes
|
||||
including terminating NUL byte)
|
||||
|
||||
.. note::
|
||||
|
||||
The helper ID namespace is a separate namespace. In particular, it is not
|
||||
related to QEMU "id" used in -object/-device objects.
|
||||
|
||||
Load(in u8[] bytes) method
|
||||
--------------------------
|
||||
|
||||
The method called on destination with the state to restore.
|
||||
|
||||
The helper may be initially started in a waiting state (with
|
||||
an --incoming argument for example), and it may resume on success.
|
||||
|
||||
An error may be returned to the caller.
|
||||
|
||||
Save(out u8[] bytes) method
|
||||
---------------------------
|
||||
|
||||
The method called on the source to get the current state to be
|
||||
migrated. The helper should continue to run normally.
|
||||
|
||||
An error may be returned to the caller.
|
||||
@@ -0,0 +1,110 @@
|
||||
=====
|
||||
D-Bus
|
||||
=====
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
QEMU may be running with various helper processes involved:
|
||||
- vhost-user* processes (gpu, virtfs, input, etc...)
|
||||
- TPM emulation (or other devices)
|
||||
- user networking (slirp)
|
||||
- network services (DHCP/DNS, samba/ftp etc)
|
||||
- background tasks (compression, streaming etc)
|
||||
- client UI
|
||||
- admin & cli
|
||||
|
||||
Having several processes allows stricter security rules, as well as
|
||||
greater modularity.
|
||||
|
||||
While QEMU itself uses QMP as primary IPC (and Spice/VNC for remote
|
||||
display), D-Bus is the de facto IPC of choice on Unix systems. The
|
||||
wire format is machine friendly, good bindings exist for various
|
||||
languages, and there are various tools available.
|
||||
|
||||
Using a bus, helper processes can discover and communicate with each
|
||||
other easily, without going through QEMU. The bus topology is also
|
||||
easier to apprehend and debug than a mesh. However, it is wise to
|
||||
consider the security aspects of it.
|
||||
|
||||
Security
|
||||
========
|
||||
|
||||
A QEMU D-Bus bus should be private to a single VM. Thus, only
|
||||
cooperative tasks are running on the same bus to serve the VM.
|
||||
|
||||
D-Bus, the protocol and standard, doesn't have mechanisms to enforce
|
||||
security between peers once the connection is established. Peers may
|
||||
have additional mechanisms to enforce security rules, based for
|
||||
example on UNIX credentials.
|
||||
|
||||
The daemon can control which peers can send/recv messages using
|
||||
various metadata attributes, however, this is alone is not generally
|
||||
sufficient to make the deployment secure. The semantics of the actual
|
||||
methods implemented using D-Bus are just as critical. Peers need to
|
||||
carefully validate any information they received from a peer with a
|
||||
different trust level.
|
||||
|
||||
dbus-daemon policy
|
||||
------------------
|
||||
|
||||
dbus-daemon can enforce various policies based on the UID/GID of the
|
||||
processes that are connected to it. It is thus a good idea to run
|
||||
helpers as different UID from QEMU and set appropriate policies.
|
||||
|
||||
Depending on the use case, you may choose different scenarios:
|
||||
|
||||
- Everything the same UID
|
||||
|
||||
- Convenient for developers
|
||||
- Improved reliability - crash of one part doens't take
|
||||
out entire VM
|
||||
- No security benefit over traditional QEMU, unless additional
|
||||
unless additional controls such as SELinux or AppArmor are
|
||||
applied
|
||||
|
||||
- Two UIDs, one for QEMU, one for dbus & helpers
|
||||
|
||||
- Moderately improved user based security isolation
|
||||
|
||||
- Many UIDs, one for QEMU one for dbus and one for each helpers
|
||||
|
||||
- Best user based security isolation
|
||||
- Complex to manager distinct UIDs needed for each VM
|
||||
|
||||
For example, to allow only ``qemu`` user to talk to ``qemu-helper``
|
||||
``org.qemu.Helper1`` service, a dbus-daemon policy may contain:
|
||||
|
||||
.. code:: xml
|
||||
|
||||
<policy user="qemu">
|
||||
<allow send_destination="org.qemu.Helper1"/>
|
||||
<allow receive_sender="org.qemu.Helper1"/>
|
||||
</policy>
|
||||
|
||||
<policy user="qemu-helper">
|
||||
<allow own="org.qemu.Helper1"/>
|
||||
</policy>
|
||||
|
||||
|
||||
dbus-daemon can also perfom SELinux checks based on the security
|
||||
context of the source and the target. For example, ``virtiofs_t``
|
||||
could be allowed to send a message to ``svirt_t``, but ``virtiofs_t``
|
||||
wouldn't be allowed to send a message to ``virtiofs_t``.
|
||||
|
||||
See dbus-daemon man page for details.
|
||||
|
||||
Guidelines
|
||||
==========
|
||||
|
||||
When implementing new D-Bus interfaces, it is recommended to follow
|
||||
the "D-Bus API Design Guidelines":
|
||||
https://dbus.freedesktop.org/doc/dbus-api-design.html
|
||||
|
||||
The "org.qemu.*" prefix is reserved for services implemented &
|
||||
distributed by the QEMU project.
|
||||
|
||||
QEMU Interfaces
|
||||
===============
|
||||
|
||||
:doc:`dbus-vmstate`
|
||||
@@ -13,6 +13,8 @@ Contents:
|
||||
:maxdepth: 2
|
||||
|
||||
bitmaps
|
||||
dbus
|
||||
dbus-vmstate
|
||||
live-block-operations
|
||||
pr-helper
|
||||
qemu-ga
|
||||
|
||||
+1
-1
@@ -822,7 +822,7 @@ static void onenand_realize(DeviceState *dev, Error **errp)
|
||||
onenand_mem_setup(s);
|
||||
sysbus_init_irq(sbd, &s->intr);
|
||||
sysbus_init_mmio(sbd, &s->container);
|
||||
vmstate_register(dev,
|
||||
vmstate_register(VMSTATE_IF(dev),
|
||||
((s->shift & 0x7f) << 24)
|
||||
| ((s->id.man & 0xff) << 16)
|
||||
| ((s->id.dev & 0xff) << 8)
|
||||
|
||||
@@ -9,6 +9,7 @@ common-obj-y += hotplug.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += nmi.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += vm-change-state-handler.o
|
||||
common-obj-y += cpu.o
|
||||
common-obj-y += vmstate-if.o
|
||||
|
||||
common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
|
||||
common-obj-$(CONFIG_XILINX_AXI) += stream.o
|
||||
|
||||
+18
-3
@@ -889,7 +889,8 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||
dev->canonical_path = object_get_canonical_path(OBJECT(dev));
|
||||
|
||||
if (qdev_get_vmsd(dev)) {
|
||||
if (vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev,
|
||||
if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
|
||||
-1, qdev_get_vmsd(dev), dev,
|
||||
dev->instance_id_alias,
|
||||
dev->alias_required_for_version,
|
||||
&local_err) < 0) {
|
||||
@@ -923,7 +924,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||
local_err ? NULL : &local_err);
|
||||
}
|
||||
if (qdev_get_vmsd(dev)) {
|
||||
vmstate_unregister(dev, qdev_get_vmsd(dev), dev);
|
||||
vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
|
||||
}
|
||||
if (dc->unrealize) {
|
||||
dc->unrealize(dev, local_err ? NULL : &local_err);
|
||||
@@ -947,7 +948,7 @@ child_realize_fail:
|
||||
}
|
||||
|
||||
if (qdev_get_vmsd(dev)) {
|
||||
vmstate_unregister(dev, qdev_get_vmsd(dev), dev);
|
||||
vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
|
||||
}
|
||||
|
||||
post_realize_fail:
|
||||
@@ -1087,9 +1088,18 @@ static void device_unparent(Object *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
device_vmstate_if_get_id(VMStateIf *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
return qdev_get_dev_path(dev);
|
||||
}
|
||||
|
||||
static void device_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(class);
|
||||
VMStateIfClass *vc = VMSTATE_IF_CLASS(class);
|
||||
|
||||
class->unparent = device_unparent;
|
||||
|
||||
@@ -1101,6 +1111,7 @@ static void device_class_init(ObjectClass *class, void *data)
|
||||
*/
|
||||
dc->hotpluggable = true;
|
||||
dc->user_creatable = true;
|
||||
vc->get_id = device_vmstate_if_get_id;
|
||||
}
|
||||
|
||||
void device_class_set_parent_reset(DeviceClass *dc,
|
||||
@@ -1158,6 +1169,10 @@ static const TypeInfo device_type_info = {
|
||||
.class_init = device_class_init,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(DeviceClass),
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_VMSTATE_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void qdev_register_types(void)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* VMState interface
|
||||
*
|
||||
* Copyright (c) 2009-2019 Red Hat Inc
|
||||
* 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 "hw/vmstate-if.h"
|
||||
|
||||
static const TypeInfo vmstate_if_info = {
|
||||
.name = TYPE_VMSTATE_IF,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(VMStateIfClass),
|
||||
};
|
||||
|
||||
static void vmstate_register_types(void)
|
||||
{
|
||||
type_register_static(&vmstate_if_info);
|
||||
}
|
||||
|
||||
type_init(vmstate_register_types);
|
||||
+1
-1
@@ -302,7 +302,7 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp)
|
||||
}
|
||||
g_free(irq);
|
||||
|
||||
vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d);
|
||||
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d);
|
||||
qemu_register_reset(cmd646_reset, d);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp)
|
||||
ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2);
|
||||
isa_init_irq(isadev, &s->irq, s->isairq);
|
||||
ide_init2(&s->bus, s->irq);
|
||||
vmstate_register(dev, 0, &vmstate_ide_isa, s);
|
||||
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s);
|
||||
ide_register_restart_cb(&s->bus);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ static void pci_piix_ide_realize(PCIDevice *dev, Error **errp)
|
||||
bmdma_setup_bar(d);
|
||||
pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar);
|
||||
|
||||
vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d);
|
||||
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d);
|
||||
|
||||
pci_piix_init_ports(d);
|
||||
}
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ static void via_ide_realize(PCIDevice *dev, Error **errp)
|
||||
bmdma_setup_bar(d);
|
||||
pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar);
|
||||
|
||||
vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d);
|
||||
vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2);
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ static int max111x_init(SSISlave *d, int inputs)
|
||||
s->input[7] = 0x80;
|
||||
s->com = 0;
|
||||
|
||||
vmstate_register(dev, -1, &vmstate_max111x, s);
|
||||
vmstate_register(VMSTATE_IF(dev), -1, &vmstate_max111x, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1815,7 +1815,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
|
||||
{
|
||||
EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
|
||||
|
||||
vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
|
||||
vmstate_unregister(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s);
|
||||
g_free(s->vmstate);
|
||||
eeprom93xx_free(&pci_dev->qdev, s->eeprom);
|
||||
qemu_del_nic(s->nic);
|
||||
@@ -1874,7 +1874,7 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp)
|
||||
|
||||
s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100));
|
||||
s->vmstate->name = qemu_get_queue(s->nic)->model;
|
||||
vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
|
||||
vmstate_register(VMSTATE_IF(&pci_dev->qdev), -1, s->vmstate, s);
|
||||
}
|
||||
|
||||
static void eepro100_instance_init(Object *obj)
|
||||
|
||||
+2
-1
@@ -2853,7 +2853,8 @@ static void virtio_net_handle_migration_primary(VirtIONet *n,
|
||||
|
||||
if (migration_in_setup(s) && !should_be_hidden) {
|
||||
if (failover_unplug_primary(n)) {
|
||||
vmstate_unregister(n->primary_dev, qdev_get_vmsd(n->primary_dev),
|
||||
vmstate_unregister(VMSTATE_IF(n->primary_dev),
|
||||
qdev_get_vmsd(n->primary_dev),
|
||||
n->primary_dev);
|
||||
qapi_event_send_unplug_primary(n->primary_device_id);
|
||||
atomic_set(&n->primary_should_be_hidden, true);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user