[libvirt] [PATCH 7/8] qemu: add dbus-vmstate helper migration support

marcandre.lureau at redhat.com marcandre.lureau at redhat.com
Tue Jan 14 13:46:45 UTC 2020


From: Marc-André Lureau <marcandre.lureau at redhat.com>

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 at redhat.com>
---
 src/qemu/qemu_alias.c        |  7 +++
 src/qemu/qemu_alias.h        |  2 +
 src/qemu/qemu_command.c      | 62 +++++++++++++++++++++++++++
 src/qemu/qemu_command.h      |  3 ++
 src/qemu/qemu_dbus.c         | 14 ++++++
 src/qemu/qemu_dbus.h         |  4 ++
 src/qemu/qemu_domain.c       | 10 +++++
 src/qemu/qemu_domain.h       |  5 +++
 src/qemu/qemu_hotplug.c      | 82 ++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_hotplug.h      |  8 ++++
 src/qemu/qemu_migration.c    | 51 ++++++++++++++++++++++
 src/qemu/qemu_monitor.c      | 21 +++++++++
 src/qemu/qemu_monitor.h      |  3 ++
 src/qemu/qemu_monitor_json.c | 15 +++++++
 src/qemu/qemu_monitor_json.h |  5 +++
 15 files changed, 292 insertions(+)

diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c
index 61f8ce05c9..d2e1ce155e 100644
--- a/src/qemu/qemu_alias.c
+++ b/src/qemu/qemu_alias.c
@@ -840,3 +840,10 @@ qemuDomainGetUnmanagedPRAlias(const char *parentalias)
 
     return ret;
 }
+
+
+const char *
+qemuDomainGetDBusVMStateAlias(void)
+{
+    return "dbus-vmstate0";
+}
diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h
index aaac09a1d1..e3492116c5 100644
--- a/src/qemu/qemu_alias.h
+++ b/src/qemu/qemu_alias.h
@@ -95,3 +95,5 @@ char *qemuAliasChardevFromDevAlias(const char *devAlias)
 const char *qemuDomainGetManagedPRAlias(void);
 
 char *qemuDomainGetUnmanagedPRAlias(const char *parentalias);
+
+const char *qemuDomainGetDBusVMStateAlias(void);
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 7429a0b7f5..53051a8726 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -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"
@@ -9458,6 +9459,64 @@ qemuBuildPflashBlockdevCommandLine(virCommandPtr cmd,
 }
 
 
+static virJSONValuePtr
+qemuBuildDBusVMStateInfoPropsInternal(const char *alias,
+                                      const char *addr)
+{
+    virJSONValuePtr ret = NULL;
+
+    if (qemuMonitorCreateObjectProps(&ret,
+                                     "dbus-vmstate", alias,
+                                     "s:addr", addr, NULL) < 0)
+        return NULL;
+
+    return ret;
+}
+
+
+virJSONValuePtr
+qemuBuildDBusVMStateInfoProps(virQEMUDriverPtr driver,
+                              virDomainObjPtr vm)
+{
+    g_autofree char *addr = qemuDBusGetAddress(driver, vm);
+
+    return qemuBuildDBusVMStateInfoPropsInternal(qemuDomainGetDBusVMStateAlias(),
+                                                 addr);
+}
+
+
+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:
  *
@@ -9689,6 +9748,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;
 
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index cb8c394fb6..239d3daede 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -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);
diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c
index 9c8a03c947..911713944b 100644
--- a/src/qemu/qemu_dbus.c
+++ b/src/qemu/qemu_dbus.c
@@ -297,3 +297,17 @@ qemuDBusSetupCgroup(virQEMUDriverPtr driver,
 
     return 0;
 }
+
+
+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);
+}
diff --git a/src/qemu/qemu_dbus.h b/src/qemu/qemu_dbus.h
index 8728824bd7..e86134b2a1 100644
--- a/src/qemu/qemu_dbus.h
+++ b/src/qemu/qemu_dbus.h
@@ -38,3 +38,7 @@ void qemuDBusStop(virQEMUDriverPtr driver,
 int qemuDBusSetupCgroup(virQEMUDriverPtr driver,
                         virDomainDefPtr def,
                         virCgroupPtr cgroup);
+
+int qemuDBusVMStateAdd(virDomainObjPtr vm, const char *id);
+
+void qemuDBusVMStateRemove(virDomainObjPtr vm, const char *id);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index dda3cb781f..81dcedcc04 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2266,6 +2266,11 @@ qemuDomainObjPrivateDataClear(qemuDomainObjPrivatePtr priv)
     qemuDomainStorageIdReset(priv);
 
     priv->dbusDaemonRunning = false;
+
+    virStringListFree(priv->dbusVMStateIds);
+    priv->dbusVMStateIds = NULL;
+
+    priv->dbusVMState = false;
 }
 
 
@@ -2919,6 +2924,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;
 
@@ -3702,6 +3710,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;
 
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 02c792ea2a..8de436fc9a 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -420,6 +420,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) \
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index c5a4db3e79..4563e3c77a 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -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_INFO("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());
+
+    if (ret == 0)
+        priv->dbusVMState = false;
+
+    if (qemuDomainObjExitMonitor(driver, vm) < 0)
+        return -1;
+
+    return ret;
+}
+
+
 /**
  * qemuHotplugAttachManagedPR:
  * @driver: QEMU driver object
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
index 6605a6a3e0..4a49e04a15 100644
--- a/src/qemu/qemu_hotplug.h
+++ b/src/qemu/qemu_hotplug.h
@@ -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);
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 71d0bb0879..8c281f3a70 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1125,10 +1125,18 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver,
                           bool remote,
                           unsigned int flags)
 {
+    qemuDomainObjPrivatePtr priv = vm->privateData;
     int nsnapshots;
     int pauseReason;
     size_t i;
 
+    if (virStringListLength((const char **)priv->dbusVMStateIds) &&
+        !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
+        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                       _("cannot migrate this domain without dbus-vmstate support"));
+        return false;
+    }
+
     /* perform these checks only when migrating to remote hosts */
     if (remote) {
         nsnapshots = virDomainSnapshotObjListNum(vm->snapshots, NULL, 0);
@@ -1893,8 +1901,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;
 
@@ -3369,6 +3383,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,
@@ -3521,6 +3566,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) {
@@ -5204,6 +5252,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) {
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index ccd20b3740..191dbe4d02 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "qemu_alias.h"
 #include "qemu_monitor.h"
 #include "qemu_monitor_text.h"
 #include "qemu_monitor_json.h"
@@ -2407,6 +2408,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)
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 3f3b81cddd..9d82d263c3 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -740,6 +740,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);
 
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index e5164d218a..42795b4227 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -2342,6 +2342,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
  *
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 61f5b0061d..e7f02d1131 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -679,3 +679,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);
-- 
2.25.0.rc2.1.g09a9a1a997




More information about the libvir-list mailing list