From 0dfb8a1b9e7fd3c4bb85e0487a83726f08167654 Mon Sep 17 00:00:00 2001 From: Jiri Denemark Date: Fri, 19 Jul 2013 15:08:29 +0200 Subject: [PATCH] qemu: Unplug devices that disappeared when libvirtd was down 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 | 56 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_driver.c | 6 +++++ src/qemu/qemu_process.c | 43 +++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) 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); -- 2.39.5