]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
qemu: Unplug devices that disappeared when libvirtd was down
authorJiri Denemark <jdenemar@redhat.com>
Fri, 19 Jul 2013 13:08:29 +0000 (15:08 +0200)
committerJiri Denemark <jdenemar@redhat.com>
Fri, 19 Jul 2013 16:45:48 +0000 (18:45 +0200)
In case libvirtd is asked to unplug a device but the device is actually
unplugged later when libvirtd is not running, we need to detect that and
remove such device when libvirtd starts again and reconnects to running
domains.

src/qemu/qemu_domain.c
src/qemu/qemu_domain.h
src/qemu/qemu_driver.c
src/qemu/qemu_process.c

index 257f4db82efe7b1a6a84e8f025109451ffbdafa7..da3b7682f20c3e315eca35a5efb61b9620a3c008 100644 (file)
@@ -339,6 +339,16 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data)
     if (priv->fakeReboot)
         virBufferAddLit(buf, "  <fakereboot/>\n");
 
+    if (priv->qemuDevices && *priv->qemuDevices) {
+        char **tmp = priv->qemuDevices;
+        virBufferAddLit(buf, "  <devices>\n");
+        while (*tmp) {
+            virBufferAsprintf(buf, "    <device alias='%s'/>\n", *tmp);
+            tmp++;
+        }
+        virBufferAddLit(buf, "  </devices>\n");
+    }
+
     return 0;
 }
 
@@ -479,12 +489,35 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data)
 
     priv->fakeReboot = virXPathBoolean("boolean(./fakereboot)", ctxt) == 1;
 
+    if ((n = virXPathNodeSet("./devices/device", ctxt, &nodes)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to parse qemu device list"));
+        goto error;
+    }
+    if (n > 0) {
+        /* NULL-terminated list */
+        if (VIR_ALLOC_N(priv->qemuDevices, n + 1) < 0)
+            goto error;
+
+        for (i = 0; i < n; i++) {
+            priv->qemuDevices[i] = virXMLPropString(nodes[i], "alias");
+            if (!priv->qemuDevices[i]) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("failed to parse qemu device list"));
+                goto error;
+            }
+        }
+    }
+    VIR_FREE(nodes);
+
     return 0;
 
 error:
     virDomainChrSourceDefFree(priv->monConfig);
     priv->monConfig = NULL;
     VIR_FREE(nodes);
+    virStringFreeList(priv->qemuDevices);
+    priv->qemuDevices = NULL;
     virObjectUnref(qemuCaps);
     return -1;
 }
@@ -2214,3 +2247,26 @@ qemuDomainMemoryLimit(virDomainDefPtr def)
 
     return mem;
 }
+
+
+int
+qemuDomainUpdateDeviceList(virQEMUDriverPtr driver,
+                           virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    char **aliases;
+
+    if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT))
+        return 0;
+
+    qemuDomainObjEnterMonitor(driver, vm);
+    if (qemuMonitorGetDeviceAliases(priv->mon, &aliases) < 0) {
+        qemuDomainObjExitMonitor(driver, vm);
+        return -1;
+    }
+    qemuDomainObjExitMonitor(driver, vm);
+
+    virStringFreeList(priv->qemuDevices);
+    priv->qemuDevices = aliases;
+    return 0;
+}
index 2f3b141e918e96cf62f98dee3448ad6e13b4cfdd..9a959d6ec0e83884cbe707ce90947502ac880127 100644 (file)
@@ -171,6 +171,7 @@ struct _qemuDomainObjPrivate {
 
     virCond unplugFinished; /* signals that unpluggingDevice was unplugged */
     const char *unpluggingDevice; /* alias of the device that is being unplugged */
+    char **qemuDevices; /* NULL-terminated list of devices aliases known to QEMU */
 };
 
 typedef enum {
@@ -363,4 +364,7 @@ extern virDomainDefParserConfig virQEMUDriverDomainDefParserConfig;
 
 unsigned long long qemuDomainMemoryLimit(virDomainDefPtr def);
 
+int qemuDomainUpdateDeviceList(virQEMUDriverPtr driver,
+                               virDomainObjPtr vm);
+
 #endif /* __QEMU_DOMAIN_H__ */
index dab0513fe5be16dd9de93fc9399b77f5b987463a..899c6179619a02a73a81f85eddad51daaeb4c48b 100644 (file)
@@ -6494,6 +6494,9 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
         break;
     }
 
+    if (ret == 0)
+        qemuDomainUpdateDeviceList(driver, vm);
+
     return ret;
 }
 
@@ -6603,6 +6606,9 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
         break;
     }
 
+    if (ret == 0)
+        qemuDomainUpdateDeviceList(driver, vm);
+
     return ret;
 }
 
index 33839d18120de42734e1521f98aa5a8ab00a3586..24ee6b1c9125a9d8a585db1570295307825df649 100644 (file)
@@ -2969,6 +2969,39 @@ qemuProcessRecoverJob(virQEMUDriverPtr driver,
     return 0;
 }
 
+static int
+qemuProcessUpdateDevices(virQEMUDriverPtr driver,
+                         virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    virDomainDeviceDef dev;
+    char **old;
+    char **tmp;
+    int ret = -1;
+
+    if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT))
+        return 0;
+
+    old = priv->qemuDevices;
+    priv->qemuDevices = NULL;
+    if (qemuDomainUpdateDeviceList(driver, vm) < 0)
+        goto cleanup;
+
+    if ((tmp = old)) {
+        while (*tmp) {
+            if (!virStringArrayHasString(priv->qemuDevices, *tmp) &&
+                virDomainDefFindDevice(vm->def, *tmp, &dev, false) == 0)
+                qemuDomainRemoveDevice(driver, vm, &dev);
+            tmp++;
+        }
+    }
+    ret = 0;
+
+cleanup:
+    virStringFreeList(old);
+    return ret;
+}
+
 struct qemuProcessReconnectData {
     virConnectPtr conn;
     virQEMUDriverPtr driver;
@@ -3105,6 +3138,9 @@ qemuProcessReconnect(void *opaque)
     if (qemuProcessRecoverJob(driver, obj, conn, &oldjob) < 0)
         goto error;
 
+    if (qemuProcessUpdateDevices(driver, obj) < 0)
+        goto error;
+
     /* update domain state XML with possibly updated state in virDomainObj */
     if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, obj) < 0)
         goto error;
@@ -3905,6 +3941,10 @@ int qemuProcessStart(virConnectPtr conn,
 
     qemuDomainObjExitMonitor(driver, vm);
 
+    VIR_DEBUG("Fetching list of active devices");
+    if (qemuDomainUpdateDeviceList(driver, vm) < 0)
+        goto cleanup;
+
     /* Technically, qemuProcessStart can be called from inside
      * QEMU_ASYNC_JOB_MIGRATION_IN, but we are okay treating this like
      * a sync job since no other job can call into the domain until
@@ -4166,6 +4206,9 @@ void qemuProcessStop(virQEMUDriverPtr driver,
         VIR_FREE(vm->def->seclabels[i]->imagelabel);
     }
 
+    virStringFreeList(priv->qemuDevices);
+    priv->qemuDevices = NULL;
+
     virDomainDefClearDeviceAliases(vm->def);
     if (!priv->persistentAddrs) {
         virDomainDefClearPCIAddresses(vm->def);