[libvirt] [PATCH 0/4] Misc hotplug fixes for QEMU

Previously hot-unplug could not be supported for USB devices in QEMU, since usb_del required the guest visible address which libvirt never knows. With 'device_del' command we can now unplug based on device alias, so support that. * src/qemu/qemu_driver.c: Use device_del to remove USB devices --- src/qemu/qemu_driver.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0bc723d..b520ce0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6633,9 +6633,81 @@ static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, return ret; } +static int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + virDomainHostdevDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + int i, ret; + + for (i = 0 ; i < vm->def->nhostdevs ; i++) { + if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + vm->def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + unsigned bus = vm->def->hostdevs[i]->source.subsys.u.usb.bus; + unsigned device = vm->def->hostdevs[i]->source.subsys.u.usb.device; + + if (dev->data.hostdev->source.subsys.u.usb.bus == bus && + dev->data.hostdev->source.subsys.u.usb.device == device) { + detach = vm->def->hostdevs[i]; + break; + } + } + + if (!detach) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("host usb device %03d.%03d not found"), + dev->data.hostdev->source.subsys.u.usb.bus, + dev->data.hostdev->source.subsys.u.usb.device); + return -1; + } + + if (!detach->info.alias) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); + return -1; + } + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached with this QEMU version")); + return -1; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + ret = 0; + + if (vm->def->nhostdevs > 1) { + memmove(vm->def->hostdevs + i, + vm->def->hostdevs + i + 1, + sizeof(*vm->def->hostdevs) * + (vm->def->nhostdevs - (i + 1))); + vm->def->nhostdevs--; + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) { + /* ignore, harmless */ + } + } else { + VIR_FREE(vm->def->hostdevs); + vm->def->nhostdevs = 0; + } + virDomainHostdevDefFree(detach); + + return ret; +} + static int qemudDomainDetachHostDevice(struct qemud_driver *driver, virDomainObjPtr vm, - virDomainDeviceDefPtr dev) + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) { virDomainHostdevDefPtr hostdev = dev->data.hostdev; int ret; @@ -6651,6 +6723,9 @@ static int qemudDomainDetachHostDevice(struct qemud_driver *driver, case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: ret = qemudDomainDetachHostPciDevice(driver, vm, dev); break; + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + ret = qemudDomainDetachHostUsbDevice(driver, vm, dev, qemuCmdFlags); + break; default: qemuReportError(VIR_ERR_NO_SUPPORT, _("hostdev subsys type '%s' not supported"), @@ -6720,7 +6795,7 @@ static int qemudDomainDetachDevice(virDomainPtr dom, /* fallthrough */ } } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { - ret = qemudDomainDetachHostDevice(driver, vm, dev); + ret = qemudDomainDetachHostDevice(driver, vm, dev, qemuCmdFlags); } else { qemuReportError(VIR_ERR_NO_SUPPORT, "%s", _("This type of device cannot be hot unplugged")); -- 1.6.5.2

The QEMU hotunplug code for PCI devices was looking at host devices in the guest config without first filtering non PCI devices. This means it was reading garbage * src/qemu/qemu_driver.c: Filter out non-PCI devices --- src/qemu/qemu_driver.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b520ce0..4dd5843 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6559,6 +6559,10 @@ static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, pciDevice *pci; for (i = 0 ; i < vm->def->nhostdevs ; i++) { + if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + vm->def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + unsigned domain = vm->def->hostdevs[i]->source.subsys.u.pci.domain; unsigned bus = vm->def->hostdevs[i]->source.subsys.u.pci.bus; unsigned slot = vm->def->hostdevs[i]->source.subsys.u.pci.slot; -- 1.6.5.2

The pci_del command is not being ported to QMP. Convert all the QEMU hotplug code over to use device_del whenever it is available to avoid the pci_del problem * src/qemu/qemu_driver.c: Convert unplug code to device_del --- src/qemu/qemu_driver.c | 60 +++++++++++++++++++++++++++++++++-------------- 1 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4dd5843..c811fba 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6312,7 +6312,8 @@ static int qemudDomainAttachDeviceFlags(virDomainPtr dom, static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, virDomainObjPtr vm, - virDomainDeviceDefPtr dev) + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) { int i, ret = -1; virDomainDiskDefPtr detach = NULL; @@ -6339,10 +6340,17 @@ static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, } qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } } qemuDomainObjExitMonitorWithDriver(driver, vm); @@ -6453,7 +6461,8 @@ cleanup: static int qemudDomainDetachNetDevice(struct qemud_driver *driver, virDomainObjPtr vm, - virDomainDeviceDefPtr dev) + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) { int i, ret = -1; virDomainNetDefPtr detach = NULL; @@ -6498,10 +6507,17 @@ qemudDomainDetachNetDevice(struct qemud_driver *driver, } qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - goto cleanup; + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto cleanup; + } } if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) { @@ -6551,7 +6567,8 @@ cleanup: static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, virDomainObjPtr vm, - virDomainDeviceDefPtr dev) + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) { virDomainHostdevDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; @@ -6595,10 +6612,17 @@ static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, } qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + return -1; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1; + } } qemuDomainObjExitMonitorWithDriver(driver, vm); @@ -6725,7 +6749,7 @@ static int qemudDomainDetachHostDevice(struct qemud_driver *driver, switch (hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - ret = qemudDomainDetachHostPciDevice(driver, vm, dev); + ret = qemudDomainDetachHostPciDevice(driver, vm, dev, qemuCmdFlags); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: ret = qemudDomainDetachHostUsbDevice(driver, vm, dev, qemuCmdFlags); @@ -6785,9 +6809,9 @@ static int qemudDomainDetachDevice(virDomainPtr dom, if (dev->type == VIR_DOMAIN_DEVICE_DISK && dev->data.disk->device == VIR_DOMAIN_DISK_DEVICE_DISK && dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { - ret = qemudDomainDetachPciDiskDevice(driver, vm, dev); + ret = qemudDomainDetachPciDiskDevice(driver, vm, dev, qemuCmdFlags); } else if (dev->type == VIR_DOMAIN_DEVICE_NET) { - ret = qemudDomainDetachNetDevice(driver, vm, dev); + ret = qemudDomainDetachNetDevice(driver, vm, dev, qemuCmdFlags); } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) { if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { ret = qemudDomainDetachPciControllerDevice(driver, vm, dev, -- 1.6.5.2

The code to remove the cgroup after QEMU failed to startup could be obscuring a real error from earlier on. It is not neccessary to raise an error in this case, so tell cgroups to keep quiet * src/qemu/qemu_driver.c: Don't raise cgroups error in QEMU start cleanup code. --- src/qemu/qemu_driver.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c811fba..ef3cd6c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2899,7 +2899,7 @@ cleanup: if (driver->securityDriver && driver->securityDriver->domainReleaseSecurityLabel) driver->securityDriver->domainReleaseSecurityLabel(vm); - qemuRemoveCgroup(driver, vm, 0); + qemuRemoveCgroup(driver, vm, 1); if ((vm->def->ngraphics == 1) && vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && vm->def->graphics[0]->data.vnc.autoport) -- 1.6.5.2
participants (1)
-
Daniel P. Berrange