diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 257f4db82e..da3b7682f2 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -339,6 +339,16 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data)
if (priv->fakeReboot)
virBufferAddLit(buf, " \n");
+ if (priv->qemuDevices && *priv->qemuDevices) {
+ char **tmp = priv->qemuDevices;
+ virBufferAddLit(buf, " \n");
+ while (*tmp) {
+ virBufferAsprintf(buf, " \n", *tmp);
+ tmp++;
+ }
+ virBufferAddLit(buf, " \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;
+}
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 2f3b141e91..9a959d6ec0 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -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__ */
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index dab0513fe5..899c617961 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -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;
}
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 33839d1812..24ee6b1c91 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -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);