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);