This commit adds hotplug support for PCI devices on S390 guests.
There's no need to implement hot unplug for zPCI as QEMU implements
an unplug callback which will unplug both PCI and zPCI device in a
cascaded way.
Currently, the following PCI devices are supported:
virtio-blk-pci
virtio-net-pci
virtio-rng-pci
virtio-input-host-pci
virtio-keyboard-pci
virtio-mouse-pci
virtio-tablet-pci
vfio-pci
SCSIVhost device
Signed-off-by: Yi Min Zhao <zyimin(a)linux.ibm.com>
Reviewed-by: Boris Fiuczynski <fiuczy(a)linux.vnet.ibm.com>
Reviewed-by: Stefan Zimmermann <stzi(a)linux.ibm.com>
Reviewed-by: Bjoern Walk <bwalk(a)linux.vnet.ibm.com>
---
src/qemu/qemu_hotplug.c | 182 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 172 insertions(+), 10 deletions(-)
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 23f6d1daba..e5c193905c 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -154,6 +154,84 @@ qemuHotplugPrepareDiskAccess(virQEMUDriverPtr driver,
}
+static int
+qemuDomainAttachZPCIDevice(qemuMonitorPtr mon,
+ virDomainDeviceInfoPtr info)
+{
+ int ret = -1;
+ char *devstr_zpci = NULL;
+
+ if (!(devstr_zpci = qemuBuildZPCIDevStr(info)))
+ goto cleanup;
+
+ if (qemuMonitorAddDevice(mon, devstr_zpci) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(devstr_zpci);
+ return ret;
+}
+
+
+static int
+qemuDomainDetachZPCIDevice(qemuMonitorPtr mon,
+ virDomainDeviceInfoPtr info)
+{
+ char *zpciAlias = NULL;
+ int ret = -1;
+
+ if (virAsprintf(&zpciAlias, "zpci%d",
info->addr.pci.zpci->zpci_uid) < 0)
+ goto cleanup;
+
+ if (qemuMonitorDelDevice(mon, zpciAlias) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(zpciAlias);
+ return ret;
+}
+
+
+static int
+qemuDomainAttachExtensionDevice(qemuMonitorPtr mon,
+ virDomainDeviceInfoPtr info,
+ virQEMUCapsPtr qemuCaps)
+{
+ if (qemuCheckDeviceIsZPCI(info)) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("This QEMU doesn't support zpci devices"));
+ return -1;
+ }
+ return qemuDomainAttachZPCIDevice(mon, info);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDetachExtensionDevice(qemuMonitorPtr mon,
+ virDomainDeviceInfoPtr info,
+ virQEMUCapsPtr qemuCaps)
+{
+ if (qemuCheckDeviceIsZPCI(info)) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("This QEMU doesn't support zpci devices"));
+ return -1;
+ }
+ return qemuDomainDetachZPCIDevice(mon, info);
+ }
+
+ return 0;
+}
+
+
static int
qemuHotplugWaitForTrayEject(virQEMUDriverPtr driver,
virDomainObjPtr vm,
@@ -403,9 +481,16 @@ qemuDomainAttachDiskGeneric(virQEMUDriverPtr driver,
if (qemuBlockStorageSourceAttachApply(priv->mon, data) < 0)
goto exit_monitor;
- if (qemuMonitorAddDevice(priv->mon, devstr) < 0)
+ if (qemuDomainAttachExtensionDevice(priv->mon, &disk->info,
+ priv->qemuCaps) < 0)
goto exit_monitor;
+ if (qemuMonitorAddDevice(priv->mon, devstr) < 0) {
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &disk->info,
+ priv->qemuCaps));
+ goto exit_monitor;
+ }
+
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
ret = -2;
goto error;
@@ -519,7 +604,17 @@ int qemuDomainAttachControllerDevice(virQEMUDriverPtr driver,
goto cleanup;
qemuDomainObjEnterMonitor(driver, vm);
- ret = qemuMonitorAddDevice(priv->mon, devstr);
+
+ if (qemuDomainAttachExtensionDevice(priv->mon, &controller->info,
+ priv->qemuCaps) < 0) {
+ ignore_value(qemuDomainObjExitMonitor(driver, vm));
+ goto cleanup;
+ }
+
+ if ((ret = qemuMonitorAddDevice(priv->mon, devstr)) < 0)
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon,
&controller->info,
+ priv->qemuCaps));
+
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
releaseaddr = false;
ret = -1;
@@ -961,7 +1056,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
goto cleanup;
}
- if (qemuDomainIsS390CCW(vm->def) &&
+ if (net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
+ qemuDomainIsS390CCW(vm->def) &&
virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_CCW)) {
net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW;
if (!(ccwaddrs = virDomainCCWAddressSetCreateFromDomain(vm->def)))
@@ -1031,7 +1127,17 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
goto try_remove;
qemuDomainObjEnterMonitor(driver, vm);
+
+ if (qemuDomainAttachExtensionDevice(priv->mon, &net->info,
+ priv->qemuCaps) < 0) {
+ ignore_value(qemuDomainObjExitMonitor(driver, vm));
+ virDomainAuditNet(vm, NULL, net, "attach", false);
+ goto try_remove;
+ }
+
if (qemuMonitorAddDevice(priv->mon, nicstr) < 0) {
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &net->info,
+ priv->qemuCaps));
ignore_value(qemuDomainObjExitMonitor(driver, vm));
virDomainAuditNet(vm, NULL, net, "attach", false);
goto try_remove;
@@ -1247,8 +1353,17 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver,
goto error;
qemuDomainObjEnterMonitor(driver, vm);
- ret = qemuMonitorAddDeviceWithFd(priv->mon, devstr,
- configfd, configfd_name);
+
+ if ((ret = qemuDomainAttachExtensionDevice(priv->mon, hostdev->info,
+ priv->qemuCaps)) < 0)
+ goto exit_monitor;
+
+ if ((ret = qemuMonitorAddDeviceWithFd(priv->mon, devstr,
+ configfd, configfd_name)) < 0)
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, hostdev->info,
+ priv->qemuCaps));
+
+ exit_monitor:
if (qemuDomainObjExitMonitor(driver, vm) < 0)
goto error;
@@ -1904,9 +2019,16 @@ qemuDomainAttachRNGDevice(virQEMUDriverPtr driver,
if (qemuMonitorAddObject(priv->mon, &props, &objAlias) < 0)
goto exit_monitor;
- if (qemuMonitorAddDevice(priv->mon, devstr) < 0)
+ if (qemuDomainAttachExtensionDevice(priv->mon, &rng->info,
+ priv->qemuCaps) < 0)
goto exit_monitor;
+ if (qemuMonitorAddDevice(priv->mon, devstr) < 0) {
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &rng->info,
+ priv->qemuCaps));
+ goto exit_monitor;
+ }
+
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
releaseaddr = false;
goto cleanup;
@@ -2398,8 +2520,18 @@ qemuDomainAttachSCSIVHostDevice(virQEMUDriverPtr driver,
qemuDomainObjEnterMonitor(driver, vm);
- ret = qemuMonitorAddDeviceWithFd(priv->mon, devstr, vhostfd, vhostfdName);
+ if ((ret = qemuDomainAttachExtensionDevice(priv->mon, hostdev->info,
+ priv->qemuCaps)) < 0)
+ goto exit_monitor;
+
+ if ((ret = qemuMonitorAddDeviceWithFd(priv->mon, devstr, vhostfd,
+ vhostfdName)) < 0) {
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, hostdev->info,
+ priv->qemuCaps));
+ goto exit_monitor;
+ }
+ exit_monitor:
if (qemuDomainObjExitMonitor(driver, vm) < 0 || ret < 0)
goto audit;
@@ -2644,8 +2776,15 @@ qemuDomainAttachShmemDevice(virQEMUDriverPtr driver,
release_backing = true;
- if (qemuMonitorAddDevice(priv->mon, shmstr) < 0)
+ if (qemuDomainAttachExtensionDevice(priv->mon, &shmem->info,
+ priv->qemuCaps) < 0)
+ goto exit_monitor;
+
+ if (qemuMonitorAddDevice(priv->mon, shmstr) < 0) {
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &shmem->info,
+ priv->qemuCaps));
goto exit_monitor;
+ }
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
release_address = false;
@@ -2818,8 +2957,19 @@ qemuDomainAttachInputDevice(virQEMUDriverPtr driver,
goto cleanup;
qemuDomainObjEnterMonitor(driver, vm);
- if (qemuMonitorAddDevice(priv->mon, devstr) < 0)
+
+ if (input->bus == VIR_DOMAIN_INPUT_BUS_VIRTIO) {
+ if (qemuDomainAttachExtensionDevice(priv->mon, &input->info,
+ priv->qemuCaps) < 0)
+ goto exit_monitor;
+ }
+
+ if (qemuMonitorAddDevice(priv->mon, devstr) < 0) {
+ if (input->bus == VIR_DOMAIN_INPUT_BUS_VIRTIO)
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon,
&input->info,
+ priv->qemuCaps));
goto exit_monitor;
+ }
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
releaseaddr = false;
@@ -2897,9 +3047,16 @@ qemuDomainAttachVsockDevice(virQEMUDriverPtr driver,
goto cleanup;
qemuDomainObjEnterMonitor(driver, vm);
- if (qemuMonitorAddDeviceWithFd(priv->mon, devstr, vsockPriv->vhostfd, fdname)
< 0)
+
+ if (qemuDomainAttachExtensionDevice(priv->mon, &vsock->info,
priv->qemuCaps) < 0)
goto exit_monitor;
+ if (qemuMonitorAddDeviceWithFd(priv->mon, devstr, vsockPriv->vhostfd, fdname)
< 0) {
+ ignore_value(qemuDomainDetachExtensionDevice(priv->mon, &vsock->info,
+ priv->qemuCaps));
+ goto exit_monitor;
+ }
+
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
releaseaddr = false;
goto cleanup;
@@ -4917,6 +5074,11 @@ int qemuDomainDetachControllerDevice(virQEMUDriverPtr driver,
qemuDomainMarkDeviceForRemoval(vm, &detach->info);
qemuDomainObjEnterMonitor(driver, vm);
+ if (detach->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
+ qemuDomainDetachExtensionDevice(priv->mon, &detach->info,
priv->qemuCaps)) {
+ ignore_value(qemuDomainObjExitMonitor(driver, vm));
+ goto cleanup;
+ }
if (qemuMonitorDelDevice(priv->mon, detach->info.alias)) {
ignore_value(qemuDomainObjExitMonitor(driver, vm));
goto cleanup;
--
Yi Min