To cope with the QEMU binary being changed while a VM is running,
it is neccessary to persist the original qemu capabilities at the
time the VM is booted.

* src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: Add
  an enum for a string rep of every capability
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: Support for
  storing capabilities in the domain status XML
* src/qemu/qemu_process.c: Populate & free QEMU capabilities at
  domain startup
---
 src/qemu/qemu_capabilities.c |   78 ++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_capabilities.h |    1 +
 src/qemu/qemu_domain.c       |   45 ++++++++++++++++++++++++
 src/qemu/qemu_domain.h       |    3 ++
 src/qemu/qemu_process.c      |   42 +++++++++++-----------
 5 files changed, 148 insertions(+), 21 deletions(-)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 63486cc..620143e 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -43,6 +43,84 @@
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
+/* While not public, these strings must not change. They
+ * are used in domain status files which are read on
+ * daemon restarts
+ */
+VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
+              "kqemu",  /* 0 */
+              "vnc-colon",
+              "no-reboot",
+              "drive",
+              "drive-boot",
+
+              "name", /* 5 */
+              "uuid",
+              "domid",
+              "vnet-hdr",
+              "migrate-kvm-stdio",
+
+              "migrate-qemu-tcp", /* 10 */
+              "migrate-qemu-exec",
+              "drive-cache-v2",
+              "kvm",
+              "drive-format",
+
+              "vga", /* 15 */
+              "0.10",
+              "pci-device",
+              "mem-path",
+              "drive-serial",
+
+              "xen-domid", /* 20 */
+              "migrate-qemu-unix",
+              "chardev",
+              "enable-kvm",
+              "monitor-json",
+
+              "balloon", /* 25 */
+              "device",
+              "sdl",
+              "smp-topology",
+              "netdev",
+
+              "rtc", /* 30 */
+              "vnet-host",
+              "rtc-td-hack",
+              "no-hpet",
+              "no-kvm-pit",
+
+              "tdf", /* 35 */
+              "pci-configfd",
+              "nodefconfig",
+              "boot-menu",
+              "enable-kqemu",
+
+              "fsdev", /* 40 */
+              "nesting",
+              "name-process",
+              "drive-readonly",
+              "smbios-type",
+
+              "vga-qxl", /* 45 */
+              "spice",
+              "vga-none",
+              "migrate-qemu-fd",
+              "boot-index",
+
+              "hda-duplex", /* 50 */
+              "drive-aio",
+              "pci-multibus",
+              "pci-bootindex",
+              "ccid-emulated",
+
+              "ccid-passthru", /* 55 */
+              "chardev-spicevmc",
+              "device-spicevmc",
+              "virtio-tx-alg",
+              "device-qxl-vga",
+    );
+
 struct qemu_feature_flags {
     const char *name;
     const int default_on;
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 68c5958..ab47f22 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -141,5 +141,6 @@ int qemuCapsParseHelpStr(const char *qemu,
 int qemuCapsParseDeviceStr(const char *str,
                            virBitmapPtr qemuCaps);
 
+VIR_ENUM_DECL(qemuCaps);
 
 #endif /* __QEMU_CAPABILITIES_H__*/
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index a947b4e..3033ff5 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -25,6 +25,7 @@
 
 #include "qemu_domain.h"
 #include "qemu_command.h"
+#include "qemu_capabilities.h"
 #include "memory.h"
 #include "logging.h"
 #include "virterror_internal.h"
@@ -113,6 +114,8 @@ static void qemuDomainObjPrivateFree(void *data)
 {
     qemuDomainObjPrivatePtr priv = data;
 
+    qemuCapsFree(priv->qemuCaps);
+
     qemuDomainPCIAddressSetFree(priv->pciaddrs);
     virDomainChrSourceDefFree(priv->monConfig);
     VIR_FREE(priv->vcpupids);
@@ -160,6 +163,18 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, 
void *data)
         virBufferAddLit(buf, "  </vcpus>\n");
     }
 
+    if (priv->qemuCaps) {
+        int i;
+        virBufferAddLit(buf, "  <qemuCaps>\n");
+        for (i = 0 ; i < QEMU_CAPS_LAST ; i++) {
+            if (qemuCapsGet(priv->qemuCaps, i)) {
+                virBufferVSprintf(buf, "    <flag name='%s'/>\n",
+                                  qemuCapsTypeToString(i));
+            }
+        }
+        virBufferAddLit(buf, "  </qemuCaps>\n");
+    }
+
     return 0;
 }
 
@@ -170,6 +185,7 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr 
ctxt, void *data)
     char *tmp;
     int n, i;
     xmlNodePtr *nodes = NULL;
+    virBitmapPtr qemuCaps = NULL;
 
     if (VIR_ALLOC(priv->monConfig) < 0) {
         virReportOOMError();
@@ -235,12 +251,41 @@ static int 
qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data)
         VIR_FREE(nodes);
     }
 
+    if ((n = virXPathNodeSet("./qemuCaps/flag", ctxt, &nodes)) < 0) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        "%s", _("failed to parse qemu capabilities flags"));
+        goto error;
+    }
+    if (n > 0) {
+        if (!(qemuCaps = qemuCapsNew()))
+            goto error;
+
+        for (i = 0 ; i < n ; i++) {
+            char *str = virXMLPropString(nodes[i], "name");
+            if (str) {
+                int flag = qemuCapsTypeFromString(str);
+                VIR_FREE(str);
+                if (flag < 0) {
+                    qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                    _("Unknown qemu capabilities flag %s"), 
str);
+                    goto error;
+                }
+                qemuCapsSet(qemuCaps, flag);
+            }
+        }
+
+        priv->qemuCaps = qemuCaps;
+    }
+    VIR_FREE(nodes);
+
+
     return 0;
 
 error:
     virDomainChrSourceDefFree(priv->monConfig);
     priv->monConfig = NULL;
     VIR_FREE(nodes);
+    qemuCapsFree(qemuCaps);
     return -1;
 }
 
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 8258900..b0ecc5a 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -28,6 +28,7 @@
 # include "domain_conf.h"
 # include "qemu_monitor.h"
 # include "qemu_conf.h"
+# include "bitmap.h"
 
 /* Only 1 job is allowed at any time
  * A job includes *all* monitor commands, even those just querying
@@ -77,6 +78,8 @@ struct _qemuDomainObjPrivate {
 
     qemuDomainPCIAddressSetPtr pciaddrs;
     int persistentAddrs;
+
+    virBitmapPtr qemuCaps;
 };
 
 struct qemuDomainWatchdogEvent
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 7691cbe..eb2050d 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1288,8 +1288,7 @@ qemuProcessSetVcpuAffinites(virConnectPtr conn,
 static int
 qemuProcessInitPasswords(virConnectPtr conn,
                          struct qemud_driver *driver,
-                         virDomainObjPtr vm,
-                         virBitmapPtr qemuCaps)
+                         virDomainObjPtr vm)
 {
     int ret = 0;
     qemuDomainObjPrivatePtr priv = vm->privateData;
@@ -1311,7 +1310,7 @@ qemuProcessInitPasswords(virConnectPtr conn,
     if (ret < 0)
         goto cleanup;
 
-    if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+    if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
         int i;
 
         for (i = 0 ; i < vm->def->ndisks ; i++) {
@@ -1965,7 +1964,6 @@ qemuProcessReconnect(void *payload, const void *name 
ATTRIBUTE_UNUSED, void *opa
     struct qemuProcessReconnectData *data = opaque;
     struct qemud_driver *driver = data->driver;
     qemuDomainObjPrivatePtr priv;
-    virBitmapPtr qemuCaps = NULL;
     virConnectPtr conn = data->conn;
 
     virDomainObjLock(obj);
@@ -1986,13 +1984,16 @@ qemuProcessReconnect(void *payload, const void *name 
ATTRIBUTE_UNUSED, void *opa
         goto error;
     }
 
-    /* XXX we should be persisting the original flags in the XML
-     * not re-detecting them, since the binary may have changed
-     * since launch time */
-    if (qemuCapsExtractVersionInfo(obj->def->emulator, obj->def->os.arch,
+    /* If upgrading from old QEMU we won't have found any
+     * caps in the domain status, so re-query them
+     */
+    if (!priv->qemuCaps &&
+        qemuCapsExtractVersionInfo(obj->def->emulator, obj->def->os.arch,
                                    NULL,
-                                   &qemuCaps) >= 0 &&
-        qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+                                   &priv->qemuCaps) < 0)
+        goto error;
+
+    if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
         priv->persistentAddrs = 1;
 
         if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(obj->def)) ||
@@ -2012,11 +2013,9 @@ qemuProcessReconnect(void *payload, const void *name 
ATTRIBUTE_UNUSED, void *opa
     if (virDomainObjUnref(obj) > 0)
         virDomainObjUnlock(obj);
 
-    qemuCapsFree(qemuCaps);
     return;
 
 error:
-    qemuCapsFree(qemuCaps);
     if (!virDomainObjIsActive(obj)) {
         if (virDomainObjUnref(obj) > 0)
             virDomainObjUnlock(obj);
@@ -2058,7 +2057,6 @@ int qemuProcessStart(virConnectPtr conn,
                      enum virVMOperationType vmop)
 {
     int ret;
-    virBitmapPtr qemuCaps = NULL;
     off_t pos = -1;
     char ebuf[1024];
     char *pidfile = NULL;
@@ -2204,9 +2202,11 @@ int qemuProcessStart(virConnectPtr conn,
         goto cleanup;
 
     VIR_DEBUG0("Determining emulator version");
+    qemuCapsFree(priv->qemuCaps);
+    priv->qemuCaps = NULL;
     if (qemuCapsExtractVersionInfo(vm->def->emulator, vm->def->os.arch,
                                    NULL,
-                                   &qemuCaps) < 0)
+                                   &priv->qemuCaps) < 0)
         goto cleanup;
 
     VIR_DEBUG0("Setting up domain cgroup (if required)");
@@ -2223,7 +2223,7 @@ int qemuProcessStart(virConnectPtr conn,
         goto cleanup;
 
 #if HAVE_YAJL
-    if (qemuCapsGet(qemuCaps, QEMU_CAPS_MONITOR_JSON))
+    if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_MONITOR_JSON))
         priv->monJSON = 1;
     else
 #endif
@@ -2252,7 +2252,7 @@ int qemuProcessStart(virConnectPtr conn,
      * we also need to populate the PCi address set cache for later
      * use in hotplug
      */
-    if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+    if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
         VIR_DEBUG0("Assigning domain PCI addresses");
         /* Populate cache with current addresses */
         if (priv->pciaddrs) {
@@ -2274,7 +2274,7 @@ int qemuProcessStart(virConnectPtr conn,
 
     VIR_DEBUG0("Building emulator command line");
     if (!(cmd = qemuBuildCommandLine(conn, driver, vm->def, priv->monConfig,
-                                     priv->monJSON != 0, qemuCaps,
+                                     priv->monJSON != 0, priv->qemuCaps,
                                      migrateFrom, stdin_fd,
                                      vm->current_snapshot, vmop)))
         goto cleanup;
@@ -2385,12 +2385,12 @@ int qemuProcessStart(virConnectPtr conn,
         goto cleanup;
 
     VIR_DEBUG0("Setting any required VM passwords");
-    if (qemuProcessInitPasswords(conn, driver, vm, qemuCaps) < 0)
+    if (qemuProcessInitPasswords(conn, driver, vm) < 0)
         goto cleanup;
 
     /* If we have -device, then addresses are assigned explicitly.
      * If not, then we have to detect dynamic ones here */
-    if (!qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+    if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
         VIR_DEBUG0("Determining domain device PCI addresses");
         if (qemuProcessInitPCIAddresses(driver, vm) < 0)
             goto cleanup;
@@ -2421,7 +2421,6 @@ int qemuProcessStart(virConnectPtr conn,
     if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
         goto cleanup;
 
-    qemuCapsFree(qemuCaps);
     virCommandFree(cmd);
     VIR_FORCE_CLOSE(logfile);
 
@@ -2431,7 +2430,6 @@ cleanup:
     /* We jump here if we failed to start the VM for any reason, or
      * if we failed to initialize the now running VM. kill it off and
      * pretend we never started it */
-    qemuCapsFree(qemuCaps);
     virCommandFree(cmd);
     VIR_FORCE_CLOSE(logfile);
     qemuProcessStop(driver, vm, 0);
@@ -2602,6 +2600,8 @@ retry:
     vm->state = VIR_DOMAIN_SHUTOFF;
     VIR_FREE(priv->vcpupids);
     priv->nvcpupids = 0;
+    qemuCapsFree(priv->qemuCaps);
+    priv->qemuCaps = NULL;
 
     /* The "release" hook cleans up additional resources */
     if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
-- 
1.7.4.4

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to