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;
}
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;
}
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;
+}
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 {
unsigned long long qemuDomainMemoryLimit(virDomainDefPtr def);
+int qemuDomainUpdateDeviceList(virQEMUDriverPtr driver,
+ virDomainObjPtr vm);
+
#endif /* __QEMU_DOMAIN_H__ */
break;
}
+ if (ret == 0)
+ qemuDomainUpdateDeviceList(driver, vm);
+
return ret;
}
break;
}
+ if (ret == 0)
+ qemuDomainUpdateDeviceList(driver, vm);
+
return ret;
}
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;
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;
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
VIR_FREE(vm->def->seclabels[i]->imagelabel);
}
+ virStringFreeList(priv->qemuDevices);
+ priv->qemuDevices = NULL;
+
virDomainDefClearDeviceAliases(vm->def);
if (!priv->persistentAddrs) {
virDomainDefClearPCIAddresses(vm->def);