]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
qemu: add dbus-vmstate helper migration support
authorMarc-André Lureau <marcandre.lureau@redhat.com>
Tue, 25 Feb 2020 09:55:11 +0000 (10:55 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Tue, 24 Mar 2020 14:57:33 +0000 (15:57 +0100)
Helper processes may have their state migrated with QEMU data stream
thanks to the QEMU "dbus-vmstate".

libvirt maintains the list of helpers to be migrated. The
"dbus-vmstate" is added when required, and given the list of helper
Ids that must be migrated, on save & load sides.

See also:
https://git.qemu.org/?p=qemu.git;a=blob;f=docs/interop/dbus-vmstate.rst

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
15 files changed:
src/qemu/qemu_alias.c
src/qemu/qemu_alias.h
src/qemu/qemu_command.c
src/qemu/qemu_command.h
src/qemu/qemu_dbus.c
src/qemu/qemu_dbus.h
src/qemu/qemu_domain.c
src/qemu/qemu_domain.h
src/qemu/qemu_hotplug.c
src/qemu/qemu_hotplug.h
src/qemu/qemu_migration.c
src/qemu/qemu_monitor.c
src/qemu/qemu_monitor.h
src/qemu/qemu_monitor_json.c
src/qemu/qemu_monitor_json.h

index 36b312f4d470a40c6b275d7bded9236cf834f4f7..b0ea62af39a3d665eabeb51e9af7bd248233c057 100644 (file)
@@ -829,3 +829,10 @@ qemuDomainGetUnmanagedPRAlias(const char *parentalias)
 
     return ret;
 }
+
+
+const char *
+qemuDomainGetDBusVMStateAlias(void)
+{
+    return "dbus-vmstate0";
+}
index 856d2146b0eda399fc89f0f4a62463adae555ccc..239747beb1485308e2c5450f95424cb29fc70d55 100644 (file)
@@ -95,3 +95,5 @@ char *qemuAliasChardevFromDevAlias(const char *devAlias)
 const char *qemuDomainGetManagedPRAlias(void);
 
 char *qemuDomainGetUnmanagedPRAlias(const char *parentalias);
+
+const char *qemuDomainGetDBusVMStateAlias(void);
index 13d2cb7ac4ccdd0ec60a54534dd8bdabb398f68c..3a772fa3f3fb6d4c764f1d6aa2503b1e3b280a2e 100644 (file)
@@ -24,6 +24,7 @@
 #include "qemu_command.h"
 #include "qemu_hostdev.h"
 #include "qemu_capabilities.h"
+#include "qemu_dbus.h"
 #include "qemu_interface.h"
 #include "qemu_alias.h"
 #include "qemu_security.h"
@@ -9570,6 +9571,56 @@ qemuBuildPflashBlockdevCommandLine(virCommandPtr cmd,
 }
 
 
+virJSONValuePtr
+qemuBuildDBusVMStateInfoProps(virQEMUDriverPtr driver,
+                              virDomainObjPtr vm)
+{
+    virJSONValuePtr ret = NULL;
+    const char *alias = qemuDomainGetDBusVMStateAlias();
+    g_autofree char *addr = qemuDBusGetAddress(driver, vm);
+
+    if (!addr)
+        return NULL;
+
+    qemuMonitorCreateObjectProps(&ret,
+                                 "dbus-vmstate", alias,
+                                 "s:addr", addr, NULL);
+    return ret;
+}
+
+
+static int
+qemuBuildDBusVMStateCommandLine(virCommandPtr cmd,
+                                virQEMUDriverPtr driver,
+                                virDomainObjPtr vm)
+{
+    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+    g_autoptr(virJSONValue) props = NULL;
+    qemuDomainObjPrivatePtr priv = QEMU_DOMAIN_PRIVATE(vm);
+
+    if (virStringListLength((const char **)priv->dbusVMStateIds) == 0)
+        return 0;
+
+    if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
+        VIR_INFO("dbus-vmstate object is not supported by this QEMU binary");
+        return 0;
+    }
+
+    if (!(props = qemuBuildDBusVMStateInfoProps(driver, vm)))
+        return -1;
+
+    if (virQEMUBuildObjectCommandlineFromJSON(&buf, props) < 0)
+        return -1;
+
+    virCommandAddArg(cmd, "-object");
+    virCommandAddArgBuffer(cmd, &buf);
+
+    priv->dbusVMState = true;
+
+    return 0;
+}
+
+
 /**
  * qemuBuildCommandLineValidate:
  *
@@ -9801,6 +9852,9 @@ qemuBuildCommandLine(virQEMUDriverPtr driver,
     if (qemuBuildMasterKeyCommandLine(cmd, priv) < 0)
         return NULL;
 
+    if (qemuBuildDBusVMStateCommandLine(cmd, driver, vm) < 0)
+        return NULL;
+
     if (qemuBuildManagedPRCommandLine(cmd, def, priv) < 0)
         return NULL;
 
index d4927d2191883ca8bfdc534a678411cd4e24fa04..bc3ba44fb3e47ebe3002c0b7a32b3a7a81c6c57f 100644 (file)
@@ -59,6 +59,9 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver,
 virJSONValuePtr qemuBuildPRManagerInfoProps(virStorageSourcePtr src);
 virJSONValuePtr qemuBuildPRManagedManagerInfoProps(qemuDomainObjPrivatePtr priv);
 
+virJSONValuePtr qemuBuildDBusVMStateInfoProps(virQEMUDriverPtr driver,
+                                              virDomainObjPtr vm);
+
 /* Generate the object properties for a secret */
 int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo,
                              virJSONValuePtr *propsret);
index 4826f773c7800edccbea4dc538730acfcc12c7a6..f3e6f3ee3704f61f3ceecc1794ed9e6ec47eb03b 100644 (file)
@@ -273,3 +273,17 @@ qemuDBusStart(virQEMUDriverPtr driver,
     }
     return ret;
 }
+
+
+int
+qemuDBusVMStateAdd(virDomainObjPtr vm, const char *id)
+{
+    return virStringListAdd(&QEMU_DOMAIN_PRIVATE(vm)->dbusVMStateIds, id);
+}
+
+
+void
+qemuDBusVMStateRemove(virDomainObjPtr vm, const char *id)
+{
+    virStringListRemove(&QEMU_DOMAIN_PRIVATE(vm)->dbusVMStateIds, id);
+}
index d6cb1bc84a190312f8579780e04f9079bfddb2e0..a96f19ac0dcbdd5a716dca22dec7d917c6d328ac 100644 (file)
@@ -31,3 +31,7 @@ int qemuDBusStart(virQEMUDriverPtr driver,
 
 void qemuDBusStop(virQEMUDriverPtr driver,
                   virDomainObjPtr vm);
+
+int qemuDBusVMStateAdd(virDomainObjPtr vm, const char *id);
+
+void qemuDBusVMStateRemove(virDomainObjPtr vm, const char *id);
index 51082ce5ccad6b755f0c3141a7b01627bf7fd5d1..2c9fb47d17ace86f1add6d696f83c0d5eccf60fe 100644 (file)
@@ -2297,6 +2297,13 @@ qemuDomainObjPrivateDataClear(qemuDomainObjPrivatePtr priv)
 
     /* reset node name allocator */
     qemuDomainStorageIdReset(priv);
+
+    priv->dbusDaemonRunning = false;
+
+    virStringListFree(priv->dbusVMStateIds);
+    priv->dbusVMStateIds = NULL;
+
+    priv->dbusVMState = false;
 }
 
 
@@ -2974,6 +2981,9 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf,
     if (priv->dbusDaemonRunning)
         virBufferAddLit(buf, "<dbusDaemon/>\n");
 
+    if (priv->dbusVMState)
+        virBufferAddLit(buf, "<dbusVMState/>\n");
+
     if (priv->namespaces) {
         ssize_t ns = -1;
 
@@ -3761,6 +3771,8 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,
 
     priv->dbusDaemonRunning = virXPathBoolean("boolean(./dbusDaemon)", ctxt) > 0;
 
+    priv->dbusVMState = virXPathBoolean("boolean(./dbusVMState)", ctxt) > 0;
+
     if ((node = virXPathNode("./namespaces", ctxt))) {
         xmlNodePtr next;
 
index c99a41807e42b1771631c34c3487dc47402a92e4..cf19f4d101180355d922d5194674fe536a3a20fd 100644 (file)
@@ -423,6 +423,11 @@ struct _qemuDomainObjPrivate {
     virDomainBackupDefPtr backup;
 
     bool dbusDaemonRunning;
+
+    /* list of Ids to migrate */
+    char **dbusVMStateIds;
+    /* true if -object dbus-vmstate was added */
+    bool dbusVMState;
 };
 
 #define QEMU_DOMAIN_PRIVATE(vm) \
index 4d588f27dc53fcc5d89d3b2ef2aff63644f3f723..14654a17d719d105da34e99d504f38ebcb3c8d39 100644 (file)
@@ -311,6 +311,88 @@ qemuDomainChangeMediaLegacy(virQEMUDriverPtr driver,
 }
 
 
+/**
+ * qemuHotplugAttachDBusVMState:
+ * @driver: QEMU driver object
+ * @vm: domain object
+ * @asyncJob: asynchronous job identifier
+ *
+ * Add -object dbus-vmstate if necessary.
+ *
+ * Returns: 0 on success, -1 on error.
+ */
+int
+qemuHotplugAttachDBusVMState(virQEMUDriverPtr driver,
+                             virDomainObjPtr vm,
+                             qemuDomainAsyncJob asyncJob)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    g_autoptr(virJSONValue) props = NULL;
+    int ret;
+
+    if (priv->dbusVMState)
+        return 0;
+
+    if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
+        VIR_DEBUG("dbus-vmstate object is not supported by this QEMU binary");
+        return 0;
+    }
+
+    if (!(props = qemuBuildDBusVMStateInfoProps(driver, vm)))
+        return -1;
+
+    if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
+        return -1;
+
+    ret = qemuMonitorAddObject(priv->mon, &props, NULL);
+
+    if (ret == 0)
+        priv->dbusVMState = true;
+
+    if (qemuDomainObjExitMonitor(driver, vm) < 0)
+        return -1;
+
+    return ret;
+}
+
+
+/**
+ * qemuHotplugRemoveDBusVMState:
+ * @driver: QEMU driver object
+ * @vm: domain object
+ * @asyncJob: asynchronous job identifier
+ *
+ * Remove -object dbus-vmstate from @vm if the configuration does not require
+ * it any more.
+ *
+ * Returns: 0 on success, -1 on error.
+ */
+int
+qemuHotplugRemoveDBusVMState(virQEMUDriverPtr driver,
+                             virDomainObjPtr vm,
+                             qemuDomainAsyncJob asyncJob)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    int ret;
+
+    if (!priv->dbusVMState)
+        return 0;
+
+    if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
+        return -1;
+
+    ret = qemuMonitorDelObject(priv->mon, qemuDomainGetDBusVMStateAlias(), true);
+
+    if (ret == 0)
+        priv->dbusVMState = false;
+
+    if (qemuDomainObjExitMonitor(driver, vm) < 0)
+        return -1;
+
+    return ret;
+}
+
+
 /**
  * qemuHotplugAttachManagedPR:
  * @driver: QEMU driver object
index 6605a6a3e099c8a69dc803d17ee0946aa5f6cae2..4a49e04a15064f577833043133d4456183606794 100644 (file)
@@ -152,3 +152,11 @@ int qemuDomainSetVcpuInternal(virQEMUDriverPtr driver,
                               bool state);
 
 unsigned long long qemuDomainGetUnplugTimeout(virDomainObjPtr vm);
+
+int qemuHotplugAttachDBusVMState(virQEMUDriverPtr driver,
+                                 virDomainObjPtr vm,
+                                 qemuDomainAsyncJob asyncJob);
+
+int qemuHotplugRemoveDBusVMState(virQEMUDriverPtr driver,
+                                 virDomainObjPtr vm,
+                                 qemuDomainAsyncJob asyncJob);
index 2ff4df9d74c5456693cc061db7ae97cfe00ba7fd..8a1801d408c6cdf59f7a7f9e71e585b5117629ce 100644 (file)
@@ -1169,6 +1169,7 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver,
                           bool remote,
                           unsigned int flags)
 {
+    qemuDomainObjPrivatePtr priv = vm->privateData;
     int nsnapshots;
     int pauseReason;
     size_t i;
@@ -1283,6 +1284,13 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver,
                 return false;
             }
         }
+
+        if (virStringListLength((const char **)priv->dbusVMStateIds) > 0 &&
+            !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
+            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                           _("cannot migrate this domain without dbus-vmstate support"));
+            return false;
+        }
     }
 
     return true;
@@ -1948,8 +1956,14 @@ qemuMigrationDstRun(virQEMUDriverPtr driver,
     if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
         return -1;
 
+    rv = qemuMonitorSetDBusVMStateIdList(priv->mon,
+                                         (const char **)priv->dbusVMStateIds);
+    if (rv < 0)
+        goto exit_monitor;
+
     rv = qemuMonitorMigrateIncoming(priv->mon, uri);
 
+ exit_monitor:
     if (qemuDomainObjExitMonitor(driver, vm) < 0 || rv < 0)
         return -1;
 
@@ -3420,6 +3434,37 @@ qemuMigrationSrcContinue(virQEMUDriverPtr driver,
 }
 
 
+static int
+qemuMigrationSetDBusVMState(virQEMUDriverPtr driver,
+                            virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    if (virStringListLength((const char **)priv->dbusVMStateIds) > 0) {
+        int rv;
+
+        if (qemuHotplugAttachDBusVMState(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
+            return -1;
+
+        if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
+            return -1;
+
+        rv = qemuMonitorSetDBusVMStateIdList(priv->mon,
+                                             (const char **)priv->dbusVMStateIds);
+
+        if (qemuDomainObjExitMonitor(driver, vm) < 0)
+            rv = -1;
+
+        return rv;
+    } else {
+        if (qemuHotplugRemoveDBusVMState(driver, vm, QEMU_ASYNC_JOB_NONE) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
 static int
 qemuMigrationSrcRun(virQEMUDriverPtr driver,
                     virDomainObjPtr vm,
@@ -3572,6 +3617,9 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver,
         }
     }
 
+    if (qemuMigrationSetDBusVMState(driver, vm) < 0)
+        goto exit_monitor;
+
     /* Before EnterMonitor, since already qemuProcessStopCPUs does that */
     if (!(flags & VIR_MIGRATE_LIVE) &&
         virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) {
@@ -5257,6 +5305,9 @@ qemuMigrationSrcToFile(virQEMUDriverPtr driver, virDomainObjPtr vm,
     char *errbuf = NULL;
     virErrorPtr orig_err = NULL;
 
+    if (qemuMigrationSetDBusVMState(driver, vm) < 0)
+        return -1;
+
     /* Increase migration bandwidth to unlimited since target is a file.
      * Failure to change migration speed is not fatal. */
     if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) == 0) {
index 3ac78016e2e3e03c545134c3fc279733847d11ed..a62fed845e57fb7aed17ada0f928c4b259e293b4 100644 (file)
@@ -26,6 +26,7 @@
 #include <fcntl.h>
 #include <gio/gio.h>
 
+#include "qemu_alias.h"
 #include "qemu_monitor.h"
 #include "qemu_monitor_text.h"
 #include "qemu_monitor_json.h"
@@ -2361,6 +2362,26 @@ qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon,
 }
 
 
+int
+qemuMonitorSetDBusVMStateIdList(qemuMonitorPtr mon,
+                                const char **list)
+{
+    g_autofree char *path = NULL;
+
+    VIR_DEBUG("list=%p", list);
+
+    if (virStringListLength(list) == 0)
+        return 0;
+
+    path = g_strdup_printf("/objects/%s",
+                           qemuDomainGetDBusVMStateAlias());
+
+    QEMU_CHECK_MONITOR(mon);
+
+    return qemuMonitorJSONSetDBusVMStateIdList(mon, path, list);
+}
+
+
 int
 qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon,
                              unsigned long bandwidth)
index e2cc12bd0f27b67e90cc8f9158afdf18e75cb47f..68e21dcaee28fb27765025b60ee7935a7f821653 100644 (file)
@@ -737,6 +737,9 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon,
                                   unsigned long long length,
                                   const char *path);
 
+int qemuMonitorSetDBusVMStateIdList(qemuMonitorPtr mon,
+                                    const char **list);
+
 int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon,
                                  unsigned long bandwidth);
 
index a18ab477ee76fdaf5edf3fc407ef3b75f50fad4c..619717eae571714b69d92badf337560a289e54f7 100644 (file)
@@ -2361,6 +2361,21 @@ qemuMonitorJSONSetMemoryStatsPeriod(qemuMonitorPtr mon,
 }
 
 
+int
+qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon,
+                                    const char *vmstatepath,
+                                    const char **list)
+{
+    g_autofree char *str = virStringListJoin(list, ",");
+    qemuMonitorJSONObjectProperty prop = {
+        .type = QEMU_MONITOR_OBJECT_PROPERTY_STRING,
+        .val.str = str,
+    };
+
+    return qemuMonitorJSONSetObjectProperty(mon, vmstatepath, "id-list", &prop);
+}
+
+
 /* qemuMonitorJSONQueryBlock:
  * @mon: Monitor pointer
  *
index 46dc22f1a40e5afd21a986ab057d06af6365ac5f..05a46b4fe21421792f6230e18762ae9f8ccd444b 100644 (file)
@@ -686,3 +686,8 @@ qemuMonitorJSONTransactionBackup(virJSONValuePtr actions,
                                  const char *target,
                                  const char *bitmap,
                                  qemuMonitorTransactionBackupSyncMode syncmode);
+
+int qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon,
+                                        const char *vmstatepath,
+                                        const char **list)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);