[PATCH v3 00/21] PCI Multifunction hotplug/unplug support

This is the v3 of the same support sent to the mailing list a few months ago. No major changes were made. Refer to the v2 cover letter for a general view of the design used across the patches. Changes in v3: - rebase to master at d265171b5784 - minor typo fixes link to v2: https://www.redhat.com/archives/libvir-list/2020-January/msg01377.html link to v1: https://www.redhat.com/archives/libvir-list/2020-January/msg01377.html Daniel Henrique Barboza (4): utils: PCI multifunction detection helpers qemu_hotplug.c: tune unplugTimeout for multifunction detach qemu_hotplug: do not hotplug/hotunplug 'unassigned' hostdevs qemu_hotplug.c: use enhanced multifunction unplug if available Shivaprasad G Bhat (17): qemu: address: Separate the slots into multiple aggregates virhostdev: Introduce virHostdevPCIDevicesBelongToSameSlot qemu: address: Enable auto addressing multifunction cards conf: qemu: validate multifunction hostdevice domain configs conf: Add helper to get active functions of a slot of domain qemu: hostdev: Move the hostdev preparation to a separate function qemu: hotplug: Move the detach of PCI device to the beginning of live hotplug qemu: hotplug: move assignment outside qemuDomainAttachHostPCIDevice Introduce virDomainDeviceDefParseXMLMany Introduce qemuDomainDeviceParseXMLMany qemu: refactor qemuDomain[Attach|Detach]DeviceConfig qemu: refactor qemuDomain[Attach|Detach]DeviceLive qemu: hotplug: Queue and wait for multiple devices domain: addr: Introduce virDomainPCIAddressEnsureMultifunctionAddress qemu: hotplug: Implement multifunction device hotplug qemu: hotplug: Prevent updates to multifunction device qemu: hotplug: Implement multifunction device unplug src/conf/device_conf.h | 7 + src/conf/domain_addr.c | 130 +++++- src/conf/domain_addr.h | 43 +- src/conf/domain_conf.c | 224 ++++++++- src/conf/domain_conf.h | 38 ++ src/hypervisor/virhostdev.c | 29 ++ src/hypervisor/virhostdev.h | 2 + src/libvirt_private.syms | 10 + src/qemu/qemu_domain.c | 75 +++ src/qemu/qemu_domain.h | 21 +- src/qemu/qemu_domain_address.c | 311 ++++++++++++- src/qemu/qemu_domain_address.h | 17 + src/qemu/qemu_driver.c | 243 +++++++--- src/qemu/qemu_hotplug.c | 431 +++++++++++++++--- src/qemu/qemu_hotplug.h | 14 + src/qemu/qemu_validate.c | 56 +++ src/qemu/qemu_validate.h | 1 + src/util/virpci.c | 17 + src/util/virpci.h | 4 + tests/qemuhotplugtest.c | 66 ++- ...emuhotplug-multifunction-hostdev-pci-2.xml | 14 + ...plug-multifunction-hostdev-pci-partial.xml | 27 ++ .../qemuhotplug-multifunction-hostdev-pci.xml | 26 ++ ...live+multifunction-hostdev-pci-partial.xml | 82 ++++ ...ug-base-live+multifunction-hostdev-pci.xml | 82 ++++ ...-base-live+multifunction-hostdev-pci-2.xml | 59 +++ ...es-base-live+multifunction-hostdev-pci.xml | 69 +++ .../hostdev-pci-address-unassigned.args | 9 +- .../hostdev-pci-multifunction.args | 18 +- .../hostdev-pci-multifunction.xml | 8 +- .../hostdev-pci-no-primary-function.xml | 23 + .../hostdev-pci-validate.args | 30 ++ .../qemuxml2argvdata/hostdev-pci-validate.xml | 29 ++ .../qemuxml2argvdata/pseries-hostdevs-1.args | 5 +- .../qemuxml2argvdata/pseries-hostdevs-3.args | 5 +- tests/qemuxml2argvtest.c | 14 +- .../hostdev-pci-address-unassigned.xml | 8 +- .../hostdev-pci-multifunction.xml | 24 +- .../qemuxml2xmloutdata/pseries-hostdevs-1.xml | 4 +- .../qemuxml2xmloutdata/pseries-hostdevs-3.xml | 4 +- tests/virpcitestdata/0005-90-01.1.config | Bin 256 -> 256 bytes tests/virpcitestdata/0005-90-01.2.config | Bin 256 -> 256 bytes tests/virpcitestdata/0005-90-01.3.config | Bin 0 -> 256 bytes 43 files changed, 2022 insertions(+), 257 deletions(-) create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-partial.xml create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci-partial.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml create mode 100644 tests/qemuxml2argvdata/hostdev-pci-no-primary-function.xml create mode 100644 tests/qemuxml2argvdata/hostdev-pci-validate.args create mode 100644 tests/qemuxml2argvdata/hostdev-pci-validate.xml create mode 100644 tests/virpcitestdata/0005-90-01.3.config -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Today's aggregate flag with the slot being true for pcie-root-ports is not enough as there will more number of aggregates depending on the number of Multifunction PCI cards assigned to the domain. The aggregate is changed to unsigned int. Zero means Not Applicable, 1 is reserved for the pcie-root-ports and >= 2 for the the PCI Multifunction cards(coming..). Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/conf/device_conf.h | 1 + src/conf/domain_addr.c | 46 +++++++++++++------ src/conf/domain_addr.h | 38 +++++++-------- src/qemu/qemu_domain_address.c | 84 ++++++++++++++++++++++++++++------ src/qemu/qemu_domain_address.h | 9 ++++ 5 files changed, 128 insertions(+), 50 deletions(-) diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h index d7395f2201..cc1707c1d1 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -169,6 +169,7 @@ struct _virDomainDeviceInfo { */ int pciAddrExtFlags; /* enum virDomainPCIAddressExtensionFlags */ char *loadparm; + unsigned int aggregateSlotIdx; /* Used when the aggregate flag is set */ /* PCI devices will only be automatically placed on a PCI bus * that shares the same isolation group */ diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 8623e79daf..5c8fc62725 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -297,7 +297,7 @@ virDomainPCIControllerModelToConnectType(virDomainControllerModelPCI model) return VIR_PCI_CONNECT_TYPE_PCIE_TO_PCI_BRIDGE; case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: - return VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT | VIR_PCI_CONNECT_AGGREGATE_SLOT; + return VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT; case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: return VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT; @@ -843,6 +843,7 @@ virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr addrs, virPCIDeviceAddressPtr addr, virDomainPCIConnectFlags flags, unsigned int isolationGroup, + unsigned int aggregateSlotIdx, bool fromConfig) { g_autofree char *addrStr = NULL; @@ -874,9 +875,14 @@ virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr addrs, * the device it's being reserved for can aggregate multiples on a * slot, set the slot's aggregate flag. */ - if (!bus->slot[addr->slot].functions && - flags & VIR_PCI_CONNECT_AGGREGATE_SLOT) { - bus->slot[addr->slot].aggregate = true; + if (!bus->slot[addr->slot].functions && aggregateSlotIdx > 0) { + bus->slot[addr->slot].aggregateSlotIdx = aggregateSlotIdx; + } else if (bus->slot[addr->slot].aggregateSlotIdx != + aggregateSlotIdx && fromConfig) { + bus->slot[addr->slot].aggregateSlotIdx = 0; + VIR_DEBUG("PCI functions of %.4x:%.2x is aggregated to slot %u" + "because of user assigned address %s", + addr->domain, addr->bus, aggregateSlotIdx, addrStr); } if (virDomainPCIAddressBusIsEmpty(bus) && !bus->isolationGroupLocked) { @@ -901,8 +907,8 @@ virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr addrs, /* mark the requested function as reserved */ bus->slot[addr->slot].functions |= (1 << addr->function); - VIR_DEBUG("Reserving PCI address %s (aggregate='%s')", addrStr, - bus->slot[addr->slot].aggregate ? "true" : "false"); + VIR_DEBUG("Reserving PCI address %s (aggregateSlotIdx='%d')", + addrStr, bus->slot[addr->slot].aggregateSlotIdx); return 0; } @@ -912,10 +918,12 @@ int virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs, virPCIDeviceAddressPtr addr, virDomainPCIConnectFlags flags, - unsigned int isolationGroup) + unsigned int isolationGroup, + unsigned int aggregateSlotIdx) { return virDomainPCIAddressReserveAddrInternal(addrs, addr, flags, - isolationGroup, true); + isolationGroup, + aggregateSlotIdx, true); } int @@ -956,6 +964,7 @@ virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs, if (virDomainPCIAddressReserveAddrInternal(addrs, &dev->addr.pci, flags, dev->isolationGroup, + dev->aggregateSlotIdx, true) < 0) { return -1; } @@ -1112,6 +1121,7 @@ virDomainPCIAddressSetFree(virDomainPCIAddressSetPtr addrs) static int virDomainPCIAddressFindUnusedFunctionOnBus(virDomainPCIAddressBusPtr bus, virPCIDeviceAddressPtr searchAddr, + unsigned int aggregateSlotIdx, int function, virDomainPCIConnectFlags flags, bool *found) @@ -1134,8 +1144,8 @@ virDomainPCIAddressFindUnusedFunctionOnBus(virDomainPCIAddressBusPtr bus, break; } - if (flags & VIR_PCI_CONNECT_AGGREGATE_SLOT && - bus->slot[searchAddr->slot].aggregate) { + if (bus->slot[searchAddr->slot].aggregateSlotIdx > 0 && + bus->slot[searchAddr->slot].aggregateSlotIdx == aggregateSlotIdx) { /* slot and device are okay with aggregating devices */ if ((bus->slot[searchAddr->slot].functions & (1 << searchAddr->function)) == 0) { @@ -1176,6 +1186,7 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs, virPCIDeviceAddressPtr next_addr, virDomainPCIConnectFlags flags, unsigned int isolationGroup, + unsigned int aggregateSlotIdx, int function) { virPCIDeviceAddress a = { 0 }; @@ -1204,7 +1215,9 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs, a.slot = bus->minSlot; - if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, function, + if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, + aggregateSlotIdx, + function, flags, &found) < 0) { return -1; } @@ -1228,7 +1241,9 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs, a.slot = bus->minSlot; - if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, function, + if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, + aggregateSlotIdx, + function, flags, &found) < 0) { return -1; } @@ -1286,12 +1301,13 @@ virDomainPCIAddressReserveNextAddr(virDomainPCIAddressSetPtr addrs, { virPCIDeviceAddress addr; - if (virDomainPCIAddressGetNextAddr(addrs, &addr, flags, - dev->isolationGroup, function) < 0) + if (virDomainPCIAddressGetNextAddr(addrs, &addr, flags, dev->isolationGroup, + dev->aggregateSlotIdx, function) < 0) return -1; if (virDomainPCIAddressReserveAddrInternal(addrs, &addr, flags, - dev->isolationGroup, false) < 0) + dev->isolationGroup, + dev->aggregateSlotIdx, false) < 0) return -1; addr.extFlags = dev->addr.pci.extFlags; diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index c1363c1490..ddcff36e51 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -35,24 +35,19 @@ typedef enum { VIR_PCI_CONNECT_AUTOASSIGN = 1 << 0, /* okay to autoassign a device to this controller */ VIR_PCI_CONNECT_HOTPLUGGABLE = 1 << 1, /* is hotplug needed/supported */ - /* set for devices that can share a single slot in auto-assignment - * (by assigning one device to each of the 8 functions on the slot) - */ - VIR_PCI_CONNECT_AGGREGATE_SLOT = 1 << 2, - /* kinds of devices as a bitmap so they can be combined (some PCI * controllers permit connecting multiple types of devices) */ - VIR_PCI_CONNECT_TYPE_PCI_DEVICE = 1 << 3, - VIR_PCI_CONNECT_TYPE_PCIE_DEVICE = 1 << 4, - VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT = 1 << 5, - VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT = 1 << 6, - VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_DOWNSTREAM_PORT = 1 << 7, - VIR_PCI_CONNECT_TYPE_DMI_TO_PCI_BRIDGE = 1 << 8, - VIR_PCI_CONNECT_TYPE_PCI_EXPANDER_BUS = 1 << 9, - VIR_PCI_CONNECT_TYPE_PCIE_EXPANDER_BUS = 1 << 10, - VIR_PCI_CONNECT_TYPE_PCI_BRIDGE = 1 << 11, - VIR_PCI_CONNECT_TYPE_PCIE_TO_PCI_BRIDGE = 1 << 12, + VIR_PCI_CONNECT_TYPE_PCI_DEVICE = 1 << 2, + VIR_PCI_CONNECT_TYPE_PCIE_DEVICE = 1 << 3, + VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT = 1 << 4, + VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT = 1 << 5, + VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_DOWNSTREAM_PORT = 1 << 6, + VIR_PCI_CONNECT_TYPE_DMI_TO_PCI_BRIDGE = 1 << 7, + VIR_PCI_CONNECT_TYPE_PCI_EXPANDER_BUS = 1 << 8, + VIR_PCI_CONNECT_TYPE_PCIE_EXPANDER_BUS = 1 << 9, + VIR_PCI_CONNECT_TYPE_PCI_BRIDGE = 1 << 10, + VIR_PCI_CONNECT_TYPE_PCIE_TO_PCI_BRIDGE = 1 << 11, } virDomainPCIConnectFlags; /* a combination of all bits that describe the type of connections @@ -86,12 +81,12 @@ typedef struct { */ uint8_t functions; - /* aggregate is true if this slot has only devices with - * VIR_PCI_CONNECT_AGGREGATE assigned to its functions (meaning - * that other devices with the same flags could also be - * auto-assigned to the other functions) + /* aggregate is greater than zero if this slot has only devices with + * VIR_PCI_CONNECT_AGGREGATE assigned to its functions and + * that other devices with the same aggregateSlotIdx could also be + * auto-assigned to the other functions on this slot) */ - bool aggregate; + unsigned int aggregateSlotIdx; } virDomainPCIAddressSlot; typedef struct { @@ -170,7 +165,8 @@ int virDomainPCIAddressExtensionReserveNextAddr(virDomainPCIAddressSetPtr addrs, int virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs, virPCIDeviceAddressPtr addr, virDomainPCIConnectFlags flags, - unsigned int isolationGroup) + unsigned int isolationGroup, + unsigned int agregateSlotIdx) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int virDomainPCIAddressReserveNextAddr(virDomainPCIAddressSetPtr addrs, diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 07431343ed..f86c85eaa9 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -24,6 +24,7 @@ #include "qemu_domain_address.h" #include "qemu_domain.h" #include "viralloc.h" +#include "virhostdev.h" #include "virerror.h" #include "virlog.h" @@ -1419,6 +1420,54 @@ qemuDomainSetupIsolationGroups(virDomainDefPtr def) } +void +qemuDomainSetDeviceSlotAggregateIdx(virDomainDefPtr def G_GNUC_UNUSED, + virDomainDeviceDefPtr dev) +{ + virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(dev); + + if (!info) + return; + + info->aggregateSlotIdx = 0; + + if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) { + virDomainControllerDefPtr cont = dev->data.controller; + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI && + cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT) { + info->aggregateSlotIdx = 1; + } + } + + return; +} + + +static int +qemuDomainFillDeviceSlotAggregationIter(virDomainDefPtr def, + virDomainDeviceDefPtr dev, + virDomainDeviceInfoPtr info G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED) +{ + qemuDomainSetDeviceSlotAggregateIdx(def, dev); + + return 0; +} + + +static int +qemuDomainSetupSlotAggregation(virDomainDefPtr def) +{ + if (virDomainDeviceInfoIterate(def, + qemuDomainFillDeviceSlotAggregationIter, + NULL) < 0) { + return -1; + } + + return 0; +} + + /** * qemuDomainFillDevicePCIConnectFlags: * @@ -1590,7 +1639,8 @@ qemuDomainCollectPCIAddress(virDomainDefPtr def G_GNUC_UNUSED, if (virDomainPCIAddressReserveAddr(addrs, addr, info->pciConnectFlags, - info->isolationGroup) < 0) { + info->isolationGroup, + info->aggregateSlotIdx) < 0) { return -1; } @@ -1787,7 +1837,7 @@ qemuDomainValidateDevicePCISlotsPIIX3(virDomainDefPtr def, continue; } if (addrs->nbuses && - virDomainPCIAddressReserveAddr(addrs, &cont->info.addr.pci, flags, 0) < 0) + virDomainPCIAddressReserveAddr(addrs, &cont->info.addr.pci, flags, 0, 0) < 0) return -1; } @@ -1796,11 +1846,11 @@ qemuDomainValidateDevicePCISlotsPIIX3(virDomainDefPtr def, memset(&tmp_addr, 0, sizeof(tmp_addr)); tmp_addr.slot = 1; /* ISA Bridge at 00:01.0 */ - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; /* Bridge at 00:01.3 */ tmp_addr.function = 3; - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; } @@ -1841,7 +1891,7 @@ qemuDomainValidateDevicePCISlotsPIIX3(virDomainDefPtr def, return -1; } } else { - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; primaryVideo->info.addr.pci = tmp_addr; primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; @@ -1864,7 +1914,7 @@ qemuDomainValidateDevicePCISlotsPIIX3(virDomainDefPtr def, VIR_DEBUG("PCI address 0:0:2.0 in use, future addition of a video" " device will not be possible without manual" " intervention"); - } else if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) { + } else if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) { return -1; } } @@ -1934,7 +1984,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, assign = true; } if (assign) { - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; cont->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; @@ -1957,7 +2007,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, memset(&tmp_addr, 0, sizeof(tmp_addr)); tmp_addr.slot = 0x1E; if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; cont->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; @@ -1981,12 +2031,12 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, tmp_addr.slot = 0x1F; tmp_addr.function = 0; tmp_addr.multi = VIR_TRISTATE_SWITCH_ON; - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; tmp_addr.function = 3; tmp_addr.multi = VIR_TRISTATE_SWITCH_ABSENT; - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; } @@ -2023,7 +2073,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, return -1; } } else { - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; primaryVideo->info.addr.pci = tmp_addr; @@ -2049,7 +2099,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, " device will not be possible without manual" " intervention"); virResetLastError(); - } else if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) { + } else if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) { return -1; } } @@ -2070,7 +2120,7 @@ qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, !virDeviceInfoPCIAddressIsWanted(&sound->info)) { continue; } - if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0) < 0) + if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, 0, 0) < 0) return -1; sound->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; @@ -2272,7 +2322,8 @@ qemuDomainAssignDevicePCISlots(virDomainDefPtr def, /* Reserve this function on the slot we found */ if (virDomainPCIAddressReserveAddr(addrs, &addr, cont->info.pciConnectFlags, - cont->info.isolationGroup) < 0) { + cont->info.isolationGroup, + cont->info.aggregateSlotIdx) < 0) { return -1; } @@ -2646,6 +2697,9 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, if (qemuDomainSetupIsolationGroups(def) < 0) goto cleanup; + if (qemuDomainSetupSlotAggregation(def) < 0) + goto cleanup; + if (nbuses > 0) { /* 1st pass to figure out how many PCI bridges we need */ if (!(addrs = qemuDomainPCIAddressSetCreate(def, qemuCaps, nbuses, true))) @@ -2766,6 +2820,7 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, dev.data.controller = def->controllers[contIndex]; /* set connect flags so it will be properly addressed */ qemuDomainFillDevicePCIConnectFlags(def, &dev, qemuCaps, driver); + qemuDomainSetDeviceSlotAggregateIdx(def, &dev); /* Reserve an address for the controller. pci-root and pcie-root * controllers don't plug into any other PCI controller, hence @@ -3239,6 +3294,7 @@ qemuDomainEnsurePCIAddress(virDomainObjPtr obj, return 0; qemuDomainFillDevicePCIConnectFlags(obj->def, dev, priv->qemuCaps, driver); + qemuDomainSetDeviceSlotAggregateIdx(obj->def, dev); qemuDomainFillDevicePCIExtensionFlags(dev, info, priv->qemuCaps); diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h index 7ef3308246..198f813595 100644 --- a/src/qemu/qemu_domain_address.h +++ b/src/qemu/qemu_domain_address.h @@ -53,6 +53,15 @@ void qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def, virDomainDeviceDefPtr dev) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +void +qemuDomainSetDeviceSlotAggregateIdx(virDomainDefPtr def, + virDomainDeviceDefPtr dev); + +int +qemuDomainDefDeviceFindSlotAggregateIdx(virDomainDefPtr def, + virDomainDeviceDefPtr dev); + + void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, virDomainDeviceInfoPtr info); -- 2.26.2

This patch introduces two helpers that will be used in the next patches, virPCIDeviceIsMultifunction() and virHostdevIsPCIMultifunctionDevice(). Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/conf/domain_conf.c | 31 +++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 2 ++ src/util/virpci.c | 17 +++++++++++++++++ src/util/virpci.h | 2 ++ 5 files changed, 55 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c201fc901d..0d289fbab5 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -32659,3 +32659,34 @@ virHostdevIsVFIODevice(const virDomainHostdevDef *hostdev) hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; } + + +/** + * virHostdevIsPCIMultifunctionDevice + * @hostdev: host device to check + * + * Returns true if @hostdev is a PCI Multifunction device, false otherwise. + */ +bool +virHostdevIsPCIMultifunctionDevice(virDomainHostdevDefPtr hostdev) +{ + g_autoptr(virPCIDevice) pciDev = NULL; + virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return false; + + /* Libvirt should be able to perform all the operations in + * virPCIDeviceNew() even if it's running unprivileged, so if this + * fails, the device apparently doesn't currently exist on the host. + * Since we can't speculate, assume this device is not multifunction. + */ + pciDev = virPCIDeviceNew(pcisrc->addr.domain, pcisrc->addr.bus, + pcisrc->addr.slot, pcisrc->addr.function); + + if (!pciDev) + return false; + + return virPCIDeviceIsMultifunction(pciDev); +} diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ddc75d8de2..e6d3e04109 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3840,3 +3840,6 @@ virHostdevIsMdevDevice(const virDomainHostdevDef *hostdev) bool virHostdevIsVFIODevice(const virDomainHostdevDef *hostdev) ATTRIBUTE_NONNULL(1); +bool +virHostdevIsPCIMultifunctionDevice(virDomainHostdevDefPtr hostdev) + ATTRIBUTE_NONNULL(1); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fd04fcece3..c143264382 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -723,6 +723,7 @@ virDomainEventWatchdogNewFromObj; virDomainQemuMonitorEventNew; virDomainQemuMonitorEventStateRegisterID; virHostdevIsMdevDevice; +virHostdevIsPCIMultifunctionDevice; virHostdevIsSCSIDevice; virHostdevIsVFIODevice; @@ -2794,6 +2795,7 @@ virPCIDeviceGetUnbindFromStub; virPCIDeviceGetUsedBy; virPCIDeviceHasPCIExpressLink; virPCIDeviceIsAssignable; +virPCIDeviceIsMultifunction; virPCIDeviceIsPCIExpress; virPCIDeviceListAdd; virPCIDeviceListAddCopy; diff --git a/src/util/virpci.c b/src/util/virpci.c index 6c7e6bbcab..82e86456c4 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -2837,6 +2837,23 @@ int virPCIGetHeaderType(virPCIDevicePtr dev, int *hdrType) } +bool +virPCIDeviceIsMultifunction(virPCIDevicePtr dev) +{ + int fd; + uint8_t type; + + if ((fd = virPCIDeviceConfigOpen(dev)) < 0) + return -1; + + type = virPCIDeviceRead8(dev, fd, PCI_HEADER_TYPE); + + virPCIDeviceConfigClose(dev, fd); + + return type & PCI_HEADER_TYPE_MULTI; +} + + void virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev) { diff --git a/src/util/virpci.h b/src/util/virpci.h index f16d23614a..64a9109d9b 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -270,6 +270,8 @@ int virPCIDeviceGetLinkCapSta(virPCIDevicePtr dev, int virPCIGetHeaderType(virPCIDevicePtr dev, int *hdrType); +bool virPCIDeviceIsMultifunction(virPCIDevicePtr dev); + void virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev); ssize_t virPCIGetMdevTypes(const char *sysfspath, -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/hypervisor/virhostdev.c | 29 +++++++++++++++++++++++++++++ src/hypervisor/virhostdev.h | 2 ++ src/libvirt_private.syms | 1 + 3 files changed, 32 insertions(+) diff --git a/src/hypervisor/virhostdev.c b/src/hypervisor/virhostdev.c index 9017cc3be8..03e0c5b470 100644 --- a/src/hypervisor/virhostdev.c +++ b/src/hypervisor/virhostdev.c @@ -312,6 +312,35 @@ virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) } +bool +virHostdevPCIDevicesBelongToSameSlot(virDomainHostdevDefPtr dev1, + virDomainHostdevDefPtr dev2) +{ + virPCIDeviceAddressPtr devAddr1 = NULL, devAddr2 = NULL; + + if (!dev1 || !dev2) + return false; + + devAddr1 = &dev1->source.subsys.u.pci.addr; + devAddr2 = &dev2->source.subsys.u.pci.addr; + if ((devAddr1->domain != devAddr2->domain) || + (devAddr1->bus != devAddr2->bus) || + (devAddr1->slot != devAddr2->slot) || + (virPCIDeviceAddressEqual(devAddr1, devAddr2))) { + return false; + } + + /* The Virtual Functions have multifunction false even though they have same + * domain:bus:slot as the Physical function. They are to be treated + * like non-multifunction devices + */ + if (virHostdevIsVirtualFunction(dev1) || virHostdevIsVirtualFunction(dev2)) + return false; + + return true; +} + + static int virHostdevNetDevice(virDomainHostdevDefPtr hostdev, int pfNetDevIdx, diff --git a/src/hypervisor/virhostdev.h b/src/hypervisor/virhostdev.h index 811bda40ed..84515d83fd 100644 --- a/src/hypervisor/virhostdev.h +++ b/src/hypervisor/virhostdev.h @@ -202,6 +202,8 @@ int virHostdevPCINodeDeviceReAttach(virHostdevManagerPtr mgr, int virHostdevPCINodeDeviceReset(virHostdevManagerPtr mgr, virPCIDevicePtr pci) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +bool virHostdevPCIDevicesBelongToSameSlot(virDomainHostdevDefPtr dev1, + virDomainHostdevDefPtr dev2); int virHostdevPrepareOneNVMeDevice(virHostdevManagerPtr hostdev_mgr, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c143264382..e74174ffbd 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -726,6 +726,7 @@ virHostdevIsMdevDevice; virHostdevIsPCIMultifunctionDevice; virHostdevIsSCSIDevice; virHostdevIsVFIODevice; +virHostdevPCIDevicesBelongToSameSlot; # conf/domain_nwfilter.h -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> For existing domains using the primary function alone of a multifunction card, the card is still treated as a multifunction card. This is done to prevent hotplug of other functions when the primary function is already hotplugged. If the secondary functions are part of the xml without the primary function being part of the xml, this has never been supported. So, Libvirt doesn't consider this either as a multifunction card. Since we're now checking PCI headers via virHostdevIsPCIMultifunctionDevice(), changes in virpcitestdata files were required to allow the test suit to recognize the 0005:90:01.N test device as multifunction. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_domain.h | 11 ++ src/qemu/qemu_domain_address.c | 150 +++++++++++++++++- tests/qemuhotplugtest.c | 1 + .../hostdev-pci-address-unassigned.args | 9 +- .../hostdev-pci-multifunction.args | 18 ++- .../hostdev-pci-multifunction.xml | 8 +- .../qemuxml2argvdata/pseries-hostdevs-1.args | 5 +- .../qemuxml2argvdata/pseries-hostdevs-3.args | 5 +- tests/qemuxml2argvtest.c | 6 +- .../hostdev-pci-address-unassigned.xml | 8 +- .../hostdev-pci-multifunction.xml | 24 +-- .../qemuxml2xmloutdata/pseries-hostdevs-1.xml | 4 +- .../qemuxml2xmloutdata/pseries-hostdevs-3.xml | 4 +- tests/virpcitestdata/0005-90-01.1.config | Bin 256 -> 256 bytes tests/virpcitestdata/0005-90-01.2.config | Bin 256 -> 256 bytes tests/virpcitestdata/0005-90-01.3.config | Bin 0 -> 256 bytes 16 files changed, 206 insertions(+), 47 deletions(-) create mode 100644 tests/virpcitestdata/0005-90-01.3.config diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 41d3f1561d..b601d52126 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -636,6 +636,17 @@ struct _qemuDomainSaveCookie { G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuDomainSaveCookie, virObjectUnref); +typedef struct _qemuDomainPCIHostdevdata qemuDomainPCIHostdevdata; +typedef qemuDomainPCIHostdevdata *qemuDomainPCIHostdevDataPtr; +struct _qemuDomainPCIHostdevdata { + const virDomainDef *def; + virDomainPCIAddressSetPtr addrs; + virDomainHostdevDefPtr device; +}; + +typedef int (*virDomainPCIHostdevCallback)(qemuDomainPCIHostdevDataPtr data, + virDomainHostdevDefPtr hostdev); + typedef struct _qemuDomainXmlNsDef qemuDomainXmlNsDef; typedef qemuDomainXmlNsDef *qemuDomainXmlNsDefPtr; struct _qemuDomainXmlNsDef { diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index f86c85eaa9..f431f4bb15 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1420,11 +1420,136 @@ qemuDomainSetupIsolationGroups(virDomainDefPtr def) } +#define PCI_MAX_BRIDGE_NUMBER 0xff +#define PCI_MAX_DEVICES 32 + + +/** + * qemuDomainPCIHostDevicesIter: + * @data - The data->device is the one which is called-back with for + * each hostdev + * cb() - callback to be called for each hostdev + * Return : + * If the callback for any of the hostdev fails, the Iter returns + * with the return value for that callback. + * Zero on success. + */ +static +int qemuDomainPCIHostDevicesIter(qemuDomainPCIHostdevDataPtr data, + virDomainPCIHostdevCallback cb) +{ + size_t i; + int ret = -1; + + /* Iterate through the PCI Hostdevices, the Mdev source is of type + * UUID, so skip that. */ + for (i = 0; i < data->def->nhostdevs; i++) { + virDomainHostdevSubsysPtr subsys = &data->def->hostdevs[i]->source.subsys; + virDomainHostdevDefPtr hostdev = data->def->hostdevs[i]; + if (data->device == hostdev) + continue; + if (data->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST && + (subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV && + subsys->u.mdev.model == VIR_MDEV_MODEL_TYPE_VFIO_PCI)) { + continue; + } + + if ((ret = cb(data, hostdev)) != 0) + return ret; + } + + return 0; +} + + + +static int +qemuDomainFindNextAggregationSlotIdxIter(virDomainDefPtr def G_GNUC_UNUSED, + virDomainDeviceDefPtr dev G_GNUC_UNUSED, + virDomainDeviceInfoPtr info, + void *opaque) +{ + int *aggregationSlotIdx = opaque; + + if (info && info->aggregateSlotIdx == *aggregationSlotIdx) + return -1; + + return 0; +} + + +static unsigned int +qemuDomainFindNextSlotAggregationIdx(virDomainDefPtr def) +{ + int aggregateSlotIdx = 2; + + while (aggregateSlotIdx < PCI_MAX_BRIDGE_NUMBER * PCI_MAX_DEVICES && + virDomainDeviceInfoIterate(def, + qemuDomainFindNextAggregationSlotIdxIter, + &aggregateSlotIdx) < 0) { + aggregateSlotIdx++; + } + + return aggregateSlotIdx; +} + + +static int +qemuDomainDefHostdevGetSlotAggregateIdx(qemuDomainPCIHostdevDataPtr data, + virDomainHostdevDefPtr hostdev) +{ + if (data->device && + virHostdevPCIDevicesBelongToSameSlot(data->device, hostdev)) { + if (hostdev->info->aggregateSlotIdx > 0) + return hostdev->info->aggregateSlotIdx; + } + + return 0; +} + +/** + * qemuDomainDefDeviceFindSlotAggregateIdx: + * @def : domain def + * @dev : Find the slot aggregate for the device if other + * functions are already part of the def and have + * a slot aggreate idx assigned. + * Return: + * -1: if not assigned. + * 0: If the device is not a hostdev or not a + * multifunction device. + * >0: If assigned a value; + **/ +int +qemuDomainDefDeviceFindSlotAggregateIdx(virDomainDefPtr def, + virDomainDeviceDefPtr dev) +{ + int aggregateSlotIdx = 0; + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + qemuDomainPCIHostdevdata temp = {def, NULL, hostdev}; + + /* Only PCI host devices are subject to isolation */ + if (!virHostdevIsPCIMultifunctionDevice(hostdev)) + return 0; + + aggregateSlotIdx = qemuDomainPCIHostDevicesIter(&temp, + qemuDomainDefHostdevGetSlotAggregateIdx); + + if (aggregateSlotIdx > 0) + return aggregateSlotIdx; + + return -1; +} + + void -qemuDomainSetDeviceSlotAggregateIdx(virDomainDefPtr def G_GNUC_UNUSED, +qemuDomainSetDeviceSlotAggregateIdx(virDomainDefPtr def, virDomainDeviceDefPtr dev) { virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(dev); + int aggregateSlotIdx = 0; if (!info) return; @@ -1437,6 +1562,12 @@ qemuDomainSetDeviceSlotAggregateIdx(virDomainDefPtr def G_GNUC_UNUSED, cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT) { info->aggregateSlotIdx = 1; } + } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + aggregateSlotIdx = qemuDomainDefDeviceFindSlotAggregateIdx(def, dev); + if (aggregateSlotIdx > 0) + info->aggregateSlotIdx = aggregateSlotIdx; + else if (aggregateSlotIdx < 0) + info->aggregateSlotIdx = qemuDomainFindNextSlotAggregationIdx(def); } return; @@ -2379,10 +2510,12 @@ qemuDomainAssignDevicePCISlots(virDomainDefPtr def, /* Host PCI devices */ for (i = 0; i < def->nhostdevs; i++) { - virDomainHostdevSubsysPtr subsys = &def->hostdevs[i]->source.subsys; - if (!virDeviceInfoPCIAddressIsWanted(def->hostdevs[i]->info)) + int function = 0; + virDomainHostdevDefPtr hostdev = def->hostdevs[i]; + virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; + if (!virDeviceInfoPCIAddressIsWanted(hostdev->info)) continue; - if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST && @@ -2396,9 +2529,14 @@ qemuDomainAssignDevicePCISlots(virDomainDefPtr def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) continue; - if (qemuDomainPCIAddressReserveNextAddr(addrs, - def->hostdevs[i]->info) < 0) + if (hostdev->info->aggregateSlotIdx > 1) + function = hostdev->source.subsys.u.pci.addr.function; + + if (virDomainPCIAddressReserveNextAddr(addrs, hostdev->info, + hostdev->info->pciConnectFlags, + function) < 0) { return -1; + } } /* memballoon. the qemu driver only accepts virtio memballoon devices */ diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index ba3fc4d814..57f64d191c 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -89,6 +89,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt, virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE); virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER); virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SCSI_BLOCK); + virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY); if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0) return -1; diff --git a/tests/qemuxml2argvdata/hostdev-pci-address-unassigned.args b/tests/qemuxml2argvdata/hostdev-pci-address-unassigned.args index 42fae17444..8e031c0f1f 100644 --- a/tests/qemuxml2argvdata/hostdev-pci-address-unassigned.args +++ b/tests/qemuxml2argvdata/hostdev-pci-address-unassigned.args @@ -25,7 +25,8 @@ server,nowait \ -no-shutdown \ -no-acpi \ -usb \ --device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.0,addr=0x3 \ --device vfio-pci,host=0005:90:01.2,id=hostdev2,bus=pci.0,addr=0x4 \ --device vfio-pci,host=0005:90:01.3,id=hostdev3,bus=pci.0,addr=0x5 \ --device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x6 +-device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.0,multifunction=on,\ +addr=0x3 \ +-device vfio-pci,host=0005:90:01.2,id=hostdev2,bus=pci.0,addr=0x3.0x2 \ +-device vfio-pci,host=0005:90:01.3,id=hostdev3,bus=pci.0,addr=0x3.0x3 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/hostdev-pci-multifunction.args b/tests/qemuxml2argvdata/hostdev-pci-multifunction.args index d8690c010b..3bf3629d48 100644 --- a/tests/qemuxml2argvdata/hostdev-pci-multifunction.args +++ b/tests/qemuxml2argvdata/hostdev-pci-multifunction.args @@ -25,11 +25,13 @@ server,nowait \ -no-shutdown \ -no-acpi \ -usb \ --device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.0,addr=0x3 \ --device vfio-pci,host=0001:01:00.1,id=hostdev1,bus=pci.0,addr=0x4 \ --device vfio-pci,host=0001:01:00.0,id=hostdev2,bus=pci.0,addr=0x5 \ --device vfio-pci,host=0005:90:01.2,id=hostdev3,bus=pci.0,addr=0x6 \ --device vfio-pci,host=0005:90:01.3,id=hostdev4,bus=pci.0,addr=0x7 \ --device vfio-pci,host=0000:06:12.1,id=hostdev5,bus=pci.0,addr=0x8 \ --device vfio-pci,host=0000:06:12.2,id=hostdev6,bus=pci.0,addr=0x9 \ --device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0xa +-device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.0,multifunction=on,\ +addr=0x3 \ +-device vfio-pci,host=0005:90:01.2,id=hostdev1,bus=pci.0,addr=0x3.0x2 \ +-device vfio-pci,host=0005:90:01.3,id=hostdev2,bus=pci.0,addr=0x3.0x3 \ +-device vfio-pci,host=0001:01:00.1,id=hostdev3,bus=pci.0,addr=0x4.0x1 \ +-device vfio-pci,host=0001:01:00.0,id=hostdev4,bus=pci.0,multifunction=on,\ +addr=0x4 \ +-device vfio-pci,host=0000:06:12.1,id=hostdev5,bus=pci.0,addr=0x5 \ +-device vfio-pci,host=0000:06:12.2,id=hostdev6,bus=pci.0,addr=0x6 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x7 diff --git a/tests/qemuxml2argvdata/hostdev-pci-multifunction.xml b/tests/qemuxml2argvdata/hostdev-pci-multifunction.xml index 06c889c64d..a0af6c5a90 100644 --- a/tests/qemuxml2argvdata/hostdev-pci-multifunction.xml +++ b/tests/qemuxml2argvdata/hostdev-pci-multifunction.xml @@ -22,25 +22,25 @@ <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> </source> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> </source> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> </source> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> </source> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> diff --git a/tests/qemuxml2argvdata/pseries-hostdevs-1.args b/tests/qemuxml2argvdata/pseries-hostdevs-1.args index 51ec025dce..d745f5bae6 100644 --- a/tests/qemuxml2argvdata/pseries-hostdevs-1.args +++ b/tests/qemuxml2argvdata/pseries-hostdevs-1.args @@ -26,5 +26,6 @@ server,nowait \ -device spapr-pci-host-bridge,index=1,id=pci.1 \ -device spapr-pci-host-bridge,index=2,id=pci.2 \ -device vfio-pci,host=0005:90:01.0,id=hostdev0,bus=pci.1.0,addr=0x1 \ --device vfio-pci,host=0001:01:00.0,id=hostdev1,bus=pci.2.0,addr=0x1 \ --device vfio-pci,host=0001:01:00.1,id=hostdev2,bus=pci.2.0,addr=0x2 +-device vfio-pci,host=0001:01:00.0,id=hostdev1,bus=pci.2.0,multifunction=on,\ +addr=0x1 \ +-device vfio-pci,host=0001:01:00.1,id=hostdev2,bus=pci.2.0,addr=0x1.0x1 diff --git a/tests/qemuxml2argvdata/pseries-hostdevs-3.args b/tests/qemuxml2argvdata/pseries-hostdevs-3.args index 5820140065..d29b01f4d8 100644 --- a/tests/qemuxml2argvdata/pseries-hostdevs-3.args +++ b/tests/qemuxml2argvdata/pseries-hostdevs-3.args @@ -25,5 +25,6 @@ server,nowait \ -no-shutdown \ -device spapr-pci-host-bridge,index=1,id=pci.1 \ -device spapr-pci-host-bridge,index=2,id=pci.2 \ --device vfio-pci,host=0001:01:00.0,id=hostdev0,bus=pci.2.0,addr=0x1 \ --device vfio-pci,host=0001:01:00.1,id=hostdev1,bus=pci.2.0,addr=0x2 +-device vfio-pci,host=0001:01:00.0,id=hostdev0,bus=pci.2.0,multifunction=on,\ +addr=0x1 \ +-device vfio-pci,host=0001:01:00.1,id=hostdev1,bus=pci.2.0,addr=0x1.0x1 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 4f613e8f1a..df11669cd5 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1442,7 +1442,8 @@ mymain(void) DO_TEST("hostdev-pci-multifunction", QEMU_CAPS_KVM, - QEMU_CAPS_DEVICE_VFIO_PCI); + QEMU_CAPS_DEVICE_VFIO_PCI, + QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY); DO_TEST("hostdev-pci-address-unassigned", QEMU_CAPS_KVM, @@ -2033,14 +2034,17 @@ mymain(void) QEMU_CAPS_VIRTIO_SCSI); DO_TEST("pseries-hostdevs-1", QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE, + X_QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_VIRTIO_SCSI, QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST("pseries-hostdevs-2", QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE, + X_QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_VIRTIO_SCSI, QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST("pseries-hostdevs-3", QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE, + X_QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_VIRTIO_SCSI, QEMU_CAPS_DEVICE_VFIO_PCI); diff --git a/tests/qemuxml2xmloutdata/hostdev-pci-address-unassigned.xml b/tests/qemuxml2xmloutdata/hostdev-pci-address-unassigned.xml index 2341e8432b..d6c26c3252 100644 --- a/tests/qemuxml2xmloutdata/hostdev-pci-address-unassigned.xml +++ b/tests/qemuxml2xmloutdata/hostdev-pci-address-unassigned.xml @@ -28,7 +28,7 @@ <source> <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> @@ -42,17 +42,17 @@ <source> <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/> </hostdev> <memballoon model='virtio'> - <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </memballoon> </devices> </domain> diff --git a/tests/qemuxml2xmloutdata/hostdev-pci-multifunction.xml b/tests/qemuxml2xmloutdata/hostdev-pci-multifunction.xml index 52ed86e305..c40b2130df 100644 --- a/tests/qemuxml2xmloutdata/hostdev-pci-multifunction.xml +++ b/tests/qemuxml2xmloutdata/hostdev-pci-multifunction.xml @@ -28,52 +28,52 @@ <source> <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> - <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0' multifunction='on'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x1'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x08' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x2'/> </source> - <address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> </hostdev> <memballoon model='virtio'> - <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> </memballoon> </devices> </domain> diff --git a/tests/qemuxml2xmloutdata/pseries-hostdevs-1.xml b/tests/qemuxml2xmloutdata/pseries-hostdevs-1.xml index e77a060a38..c09588de9d 100644 --- a/tests/qemuxml2xmloutdata/pseries-hostdevs-1.xml +++ b/tests/qemuxml2xmloutdata/pseries-hostdevs-1.xml @@ -40,14 +40,14 @@ <source> <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> </source> - <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0' multifunction='on'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> </source> - <address type='pci' domain='0x0000' bus='0x02' slot='0x02' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x1'/> </hostdev> <memballoon model='none'/> <panic model='pseries'/> diff --git a/tests/qemuxml2xmloutdata/pseries-hostdevs-3.xml b/tests/qemuxml2xmloutdata/pseries-hostdevs-3.xml index f91959b805..f01adf6d25 100644 --- a/tests/qemuxml2xmloutdata/pseries-hostdevs-3.xml +++ b/tests/qemuxml2xmloutdata/pseries-hostdevs-3.xml @@ -32,14 +32,14 @@ <source> <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> </source> - <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0' multifunction='on'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> <driver name='vfio'/> <source> <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> </source> - <address type='pci' domain='0x0000' bus='0x02' slot='0x02' function='0x0'/> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x1'/> </hostdev> <memballoon model='none'/> <panic model='pseries'/> diff --git a/tests/virpcitestdata/0005-90-01.1.config b/tests/virpcitestdata/0005-90-01.1.config index beee76534041a7020c08ae9ac03d9a349c6ea12e..a60599bd342d3ebcdc7b8367ca36ad337f602fde 100644 GIT binary patch delta 44 ycmZo*YG4vE7BFRCV-R3+7GUOK;Amg~f~JXq5)*X<85t+qEn;Cc-jFjfPzC^<7YL>R delta 39 ucmZo*YG4vE7BFRCV-R3+7GUOK;9y{25MXGU7$`AON05<eqTQm22?_viD+cla diff --git a/tests/virpcitestdata/0005-90-01.2.config b/tests/virpcitestdata/0005-90-01.2.config index cfd72e4d7165bff2751ecbdc570ce7f1e0646226..a60599bd342d3ebcdc7b8367ca36ad337f602fde 100644 GIT binary patch literal 256 zcmXpOFlAt45MXi^VCG@qXkY+>CJ=!Q7z5RUfCHEW5{!&mj0{Y5Fz#TaS&cX3;ByxM DCDjDB literal 256 zcmXpOc)-BMAi%_;z|6zo!oa|wz|aIFu>xbDS`csmlR$!5K#7rosSd`)Mk^@TV-u#E T7_0Gy9FS#<5E~CbC<F-rZiWW} diff --git a/tests/virpcitestdata/0005-90-01.3.config b/tests/virpcitestdata/0005-90-01.3.config new file mode 100644 index 0000000000000000000000000000000000000000..a60599bd342d3ebcdc7b8367ca36ad337f602fde GIT binary patch literal 256 zcmXpOFlAt45MXi^VCG@qXkY+>CJ=!Q7z5RUfCHEW5{!&mj0{Y5Fz#TaS&cX3;ByxM DCDjDB literal 0 HcmV?d00001 -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> It is invalid to have secondary functions without the primary functions part of the domain. Prevents new domain define, but existing ones would not vanish. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_domain_address.c | 1 - src/qemu/qemu_domain_address.h | 4 ++ src/qemu/qemu_validate.c | 56 +++++++++++++++++++ .../hostdev-pci-no-primary-function.xml | 23 ++++++++ .../hostdev-pci-validate.args | 30 ++++++++++ .../qemuxml2argvdata/hostdev-pci-validate.xml | 29 ++++++++++ tests/qemuxml2argvtest.c | 8 +++ 7 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 tests/qemuxml2argvdata/hostdev-pci-no-primary-function.xml create mode 100644 tests/qemuxml2argvdata/hostdev-pci-validate.args create mode 100644 tests/qemuxml2argvdata/hostdev-pci-validate.xml diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index f431f4bb15..05ef4de100 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1434,7 +1434,6 @@ qemuDomainSetupIsolationGroups(virDomainDefPtr def) * with the return value for that callback. * Zero on success. */ -static int qemuDomainPCIHostDevicesIter(qemuDomainPCIHostdevDataPtr data, virDomainPCIHostdevCallback cb) { diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h index 198f813595..8d51456f5b 100644 --- a/src/qemu/qemu_domain_address.h +++ b/src/qemu/qemu_domain_address.h @@ -25,6 +25,7 @@ #include "domain_conf.h" #include "qemu_conf.h" #include "qemu_capabilities.h" +#include "qemu_domain.h" int qemuDomainGetSCSIControllerModel(const virDomainDef *def, const virDomainControllerDef *cont, @@ -57,6 +58,9 @@ void qemuDomainSetDeviceSlotAggregateIdx(virDomainDefPtr def, virDomainDeviceDefPtr dev); +int qemuDomainPCIHostDevicesIter(qemuDomainPCIHostdevDataPtr data, + virDomainPCIHostdevCallback cb); + int qemuDomainDefDeviceFindSlotAggregateIdx(virDomainDefPtr def, virDomainDeviceDefPtr dev); diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 584d1375b8..956e3daf7d 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -762,6 +762,59 @@ qemuValidateDefGetVcpuHotplugGranularity(const virDomainDef *def) } +static int +qemuValidatePCIHostdevIsPrimaryFunction(qemuDomainPCIHostdevDataPtr data, + virDomainHostdevDefPtr hostdev) +{ + if (!data->device || !hostdev) + return 0; + + if ((hostdev->source.subsys.u.pci.addr.function == 0) && + (virHostdevPCIDevicesBelongToSameSlot(data->device, hostdev))) + return 1; + + return 0; +} + + +static int qemuValidateDomainDefPCIMultifunctionHostdev(qemuDomainPCIHostdevDataPtr data, + virDomainHostdevDefPtr hostdev) +{ + qemuDomainPCIHostdevdata hostdevIterData = {data->def, NULL, hostdev}; + + if (!virHostdevIsPCIMultifunctionDevice(hostdev) || + hostdev->source.subsys.u.pci.addr.function == 0) + return 0; + + /* If the device is non-zero function but its Primary function is not + * part of the domain, error out. + */ + if (!qemuDomainPCIHostDevicesIter(&hostdevIterData, + qemuValidatePCIHostdevIsPrimaryFunction)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Secondary functions of a PCI multifunction card " + "cannot be assigned to a domain without the " + "Primary function.")); + return -1; + } + + return 0; +} + + +static int qemuValidateDomainDefPCIHostdevs(const virDomainDef *def) +{ + qemuDomainPCIHostdevdata hostdevdata = {def, NULL, NULL}; + + if (qemuDomainPCIHostDevicesIter(&hostdevdata, + qemuValidateDomainDefPCIMultifunctionHostdev)) { + return -1; + } + + return 0; +} + + int qemuValidateDomainDef(const virDomainDef *def, void *opaque) @@ -966,6 +1019,9 @@ qemuValidateDomainDef(const virDomainDef *def, if (qemuValidateDomainDefConsole(def, qemuCaps) < 0) return -1; + if (qemuValidateDomainDefPCIHostdevs(def) < 0) + return -1; + if (cfg->vncTLS && cfg->vncTLSx509secretUUID && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_TLS_CREDS_X509)) { for (i = 0; i < def->ngraphics; i++) { diff --git a/tests/qemuxml2argvdata/hostdev-pci-no-primary-function.xml b/tests/qemuxml2argvdata/hostdev-pci-no-primary-function.xml new file mode 100644 index 0000000000..9cd844bbe7 --- /dev/null +++ b/tests/qemuxml2argvdata/hostdev-pci-no-primary-function.xml @@ -0,0 +1,23 @@ +<domain type='kvm'> + <name>delete</name> + <uuid>583a8e8e-f0ce-4f53-89ab-092862148b25</uuid> + <memory unit='KiB'>262144</memory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + </os> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x1'/> + </source> + </hostdev> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/hostdev-pci-validate.args b/tests/qemuxml2argvdata/hostdev-pci-validate.args new file mode 100644 index 0000000000..2a8508991e --- /dev/null +++ b/tests/qemuxml2argvdata/hostdev-pci-validate.args @@ -0,0 +1,30 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/tmp/lib/domain--1-delete \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/tmp/lib/domain--1-delete/.local/share \ +XDG_CACHE_HOME=/tmp/lib/domain--1-delete/.cache \ +XDG_CONFIG_HOME=/tmp/lib/domain--1-delete/.config \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-x86_64 \ +-name delete \ +-S \ +-machine pc,accel=kvm,usb=off,dump-guest-core=off \ +-m 256 \ +-realtime mlock=off \ +-smp 4,sockets=4,cores=1,threads=1 \ +-uuid 583a8e8e-f0ce-4f53-89ab-092862148b25 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-delete/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-no-acpi \ +-usb \ +-device vfio-pci,host=0000:06:12.1,id=hostdev0,bus=pci.0,addr=0x3 \ +-device vfio-pci,host=0000:06:12.2,id=hostdev1,bus=pci.0,addr=0x4 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x5 diff --git a/tests/qemuxml2argvdata/hostdev-pci-validate.xml b/tests/qemuxml2argvdata/hostdev-pci-validate.xml new file mode 100644 index 0000000000..54797c2dda --- /dev/null +++ b/tests/qemuxml2argvdata/hostdev-pci-validate.xml @@ -0,0 +1,29 @@ +<domain type='kvm'> + <name>delete</name> + <uuid>583a8e8e-f0ce-4f53-89ab-092862148b25</uuid> + <memory unit='KiB'>262144</memory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + </os> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0000' bus='0x06' slot='0x12' function='0x1'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0000' bus='0x06' slot='0x12' function='0x2'/> + </source> + </hostdev> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index df11669cd5..84e8157e17 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1714,6 +1714,14 @@ mymain(void) QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST("hostdev-vfio-multidomain", QEMU_CAPS_DEVICE_VFIO_PCI); + DO_TEST("hostdev-pci-validate", + QEMU_CAPS_KVM, + X_QEMU_CAPS_NODEFCONFIG, X_QEMU_CAPS_HOST_PCI_MULTIDOMAIN, + QEMU_CAPS_DEVICE_VFIO_PCI); + DO_TEST_PARSE_ERROR("hostdev-pci-no-primary-function", + QEMU_CAPS_KVM, + X_QEMU_CAPS_NODEFCONFIG, X_QEMU_CAPS_HOST_PCI_MULTIDOMAIN, + QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST("hostdev-mdev-precreated", QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST_PARSE_ERROR("hostdev-mdev-src-address-invalid", -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> In some cases it may be better to have a bitmap representing the state of individual functions rather than iterating the definition. The new helper creates a bitmap representing the state from the domain definition. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/conf/domain_conf.c | 27 +++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + 3 files changed, 31 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0d289fbab5..fcd390ace4 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17315,6 +17315,33 @@ virDomainHostdevFind(virDomainDefPtr def, return *found ? i : -1; } +#define PCI_MAX_SLOT_FUNCTIONS 8 +/** + * virDomainDefHostdevGetPCIOnlineFunctionMap: + * @def: domain definition + * @aggrSlotIdx: slot aggregation index + * Returns a bitmap representing state of individual functions of a slot. + */ +virBitmapPtr +virDomainDefHostdevGetPCIOnlineFunctionMap(virDomainDefPtr def, + int aggrSlotIdx) +{ + size_t i; + virBitmapPtr ret = NULL; + + if (!(ret = virBitmapNew(PCI_MAX_SLOT_FUNCTIONS))) + return NULL; + + for (i = 0; i < def->nhostdevs; i++) { + size_t function = def->hostdevs[i]->source.subsys.u.pci.addr.function; + + if (def->hostdevs[i]->info->aggregateSlotIdx == aggrSlotIdx) + ignore_value(virBitmapSetBit(ret, function)); + } + + return ret; +} + static bool virDomainDiskControllerMatch(int controller_type, int disk_bus) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e6d3e04109..634865842a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3315,6 +3315,9 @@ virDomainHostdevDefPtr virDomainHostdevRemove(virDomainDefPtr def, size_t i); int virDomainHostdevFind(virDomainDefPtr def, virDomainHostdevDefPtr match, virDomainHostdevDefPtr *found); +virBitmapPtr virDomainDefHostdevGetPCIOnlineFunctionMap(virDomainDefPtr def, + int aggrSlotIdx); + virDomainGraphicsListenDefPtr virDomainGraphicsGetListen(virDomainGraphicsDefPtr def, size_t i); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e74174ffbd..323291367f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -323,6 +323,7 @@ virDomainDefHasOldStyleUEFI; virDomainDefHasUSB; virDomainDefHasVcpusOffline; virDomainDefHasVFIOHostdev; +virDomainDefHostdevGetPCIOnlineFunctionMap; virDomainDefLifecycleActionAllowed; virDomainDefMaybeAddController; virDomainDefMaybeAddInput; -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> No functional change. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_hotplug.c | 80 ++++++++++++++++++++++++++--------------- src/qemu/qemu_hotplug.h | 5 +++ 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 3571dead89..172747b2e2 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1014,6 +1014,55 @@ qemuDomainAttachUSBMassStorageDevice(virQEMUDriverPtr driver, } +int qemuDomainAttachPCIHostDevicePrepare(virQEMUDriverPtr driver, + virDomainDefPtr def, + virDomainHostdevDefPtr hostdev, + virQEMUCapsPtr qemuCaps) +{ + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + unsigned int flags = 0; + int backend; + + if (!cfg->relaxedACS) + flags |= VIR_HOSTDEV_STRICT_ACS_CHECK; + if (qemuHostdevPreparePCIDevices(driver, def->name, def->uuid, + &hostdev, 1, qemuCaps, flags) < 0) + return -1; + + /* this could have been changed by qemuHostdevPreparePCIDevices */ + backend = hostdev->source.subsys.u.pci.backend; + + switch ((virDomainHostdevSubsysPCIBackendType) backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("VFIO PCI device assignment is not " + "supported by this version of qemu")); + goto error; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN: + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("QEMU does not support device assignment mode '%s'"), + virDomainHostdevSubsysPCIBackendTypeToString(backend)); + goto error; + break; + } + + return 0; + + error: + qemuHostdevReAttachPCIDevices(driver, def->name, &hostdev, 1); + return -1; +} + + static int qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -1557,44 +1606,17 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, bool teardowndevice = false; bool teardownmemlock = false; int backend; - g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); - unsigned int flags = 0; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1; - if (!cfg->relaxedACS) - flags |= VIR_HOSTDEV_STRICT_ACS_CHECK; - if (qemuHostdevPreparePCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps, flags) < 0) + if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def, + hostdev, priv->qemuCaps) < 0) return -1; /* this could have been changed by qemuHostdevPreparePCIDevices */ backend = hostdev->source.subsys.u.pci.backend; - switch ((virDomainHostdevSubsysPCIBackendType)backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: - if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("VFIO PCI device assignment is not " - "supported by this version of qemu")); - goto error; - } - break; - - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: - break; - - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN: - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("QEMU does not support device assignment mode '%s'"), - virDomainHostdevSubsysPCIBackendTypeToString(backend)); - goto error; - break; - } - if (qemuDomainAdjustMaxMemLockHostdev(vm, hostdev) < 0) goto error; teardownmemlock = true; diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 4a49e04a15..9df4a4e5f7 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -122,6 +122,11 @@ void qemuDomainRemoveVcpuAlias(virQEMUDriverPtr driver, virDomainObjPtr vm, const char *alias); +int qemuDomainAttachPCIHostDevicePrepare(virQEMUDriverPtr driver, + virDomainDefPtr def, + virDomainHostdevDefPtr dev, + virQEMUCapsPtr qemuCaps); + int qemuDomainChrInsert(virDomainDefPtr vmdef, virDomainChrDefPtr chr); -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> The hostdevices are the only devices which have dependencies outside of themselves such that, other functions of the PCI card should also have been detached from host driver before attempting the hotplug. This patch moves the detach to the beginning of the hotplug so that the following patch can detach all functions first before attempting to hotplug any. We don't need to move the detach for net devices using SRIOV as all SRIOV devices are single function devices and can be independently detached as usual. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_hotplug.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 172747b2e2..d88a0922d4 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1222,6 +1222,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, g_autofree char *netdev_name = NULL; g_autoptr(virConnect) conn = NULL; virErrorPtr save_err = NULL; + virDomainHostdevDefPtr hostdev = NULL; /* preallocate new slot for device */ if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets + 1) < 0) @@ -1252,9 +1253,18 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, * as a hostdev (the hostdev code will reach over into the * netdev-specific code as appropriate), then also added to * the nets list (see cleanup:) if successful. + * + * qemuDomainAttachHostDevice uses a connection to resolve + * a SCSI hostdev secret, which is not this case, so pass NULL. */ - ret = qemuDomainAttachHostDevice(driver, vm, - virDomainNetGetActualHostdev(net)); + hostdev = virDomainNetGetActualHostdev(net); + if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def, + hostdev, priv->qemuCaps) < 0) + goto cleanup; + + if ((ret = qemuDomainAttachHostDevice(driver, vm, hostdev)) < 0) + qemuHostdevReAttachPCIDevices(driver, vm->def->name, &hostdev, 1); + goto cleanup; } @@ -1610,10 +1620,6 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1; - if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def, - hostdev, priv->qemuCaps) < 0) - return -1; - /* this could have been changed by qemuHostdevPreparePCIDevices */ backend = hostdev->source.subsys.u.pci.backend; @@ -1689,8 +1695,6 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, if (releaseaddr) qemuDomainReleaseDeviceAddress(vm, info); - qemuHostdevReAttachPCIDevices(driver, vm->def->name, &hostdev, 1); - return -1; } @@ -2929,6 +2933,8 @@ qemuDomainAttachHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { + qemuDomainObjPrivatePtr priv = vm->privateData; + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hotplug is not supported for hostdev mode '%s'"), @@ -2938,9 +2944,15 @@ qemuDomainAttachHostDevice(virQEMUDriverPtr driver, switch (hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - if (qemuDomainAttachHostPCIDevice(driver, vm, - hostdev) < 0) + if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def, + hostdev, priv->qemuCaps) < 0) return -1; + + if (qemuDomainAttachHostPCIDevice(driver, vm, hostdev) < 0) { + qemuHostdevReAttachPCIDevices(driver, vm->def->name, &hostdev, 1); + return -1; + } + break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> No functional change. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_hotplug.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index d88a0922d4..6991d7988c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1605,12 +1605,9 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev) { qemuDomainObjPrivatePtr priv = vm->privateData; - virDomainDeviceDef dev = { VIR_DOMAIN_DEVICE_HOSTDEV, - { .hostdev = hostdev } }; virDomainDeviceInfoPtr info = hostdev->info; int ret; g_autofree char *devstr = NULL; - bool releaseaddr = false; bool teardowncgroup = false; bool teardownlabel = false; bool teardowndevice = false; @@ -1643,14 +1640,6 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, if (qemuAssignDeviceHostdevAlias(vm->def, &info->alias, -1) < 0) goto error; - if (qemuDomainIsPSeries(vm->def)) - /* Isolation groups are only relevant for pSeries guests */ - qemuDomainFillDeviceIsolationGroup(vm->def, &dev); - - if (qemuDomainEnsurePCIAddress(vm, &dev, driver) < 0) - goto error; - releaseaddr = true; - if (!virDomainObjIsActive(vm)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("guest unexpectedly quit during hotplug")); @@ -1692,9 +1681,6 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, if (teardownmemlock && qemuDomainAdjustMaxMemLock(vm, false) < 0) VIR_WARN("Unable to reset maximum locked memory on hotplug fail"); - if (releaseaddr) - qemuDomainReleaseDeviceAddress(vm, info); - return -1; } @@ -2934,6 +2920,8 @@ qemuDomainAttachHostDevice(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev) { qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainDeviceDef dev = { VIR_DOMAIN_DEVICE_HOSTDEV, + { .hostdev = hostdev } }; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -2948,7 +2936,15 @@ qemuDomainAttachHostDevice(virQEMUDriverPtr driver, hostdev, priv->qemuCaps) < 0) return -1; + if (qemuDomainIsPSeries(vm->def)) + /* Isolation groups are only relevant for pSeries guests */ + qemuDomainFillDeviceIsolationGroup(vm->def, &dev); + + if (qemuDomainEnsurePCIAddress(vm, &dev, driver) < 0) + return -1; + if (qemuDomainAttachHostPCIDevice(driver, vm, hostdev) < 0) { + qemuDomainReleaseDeviceAddress(vm, hostdev->info); qemuHostdevReAttachPCIDevices(driver, vm->def->name, &hostdev, 1); return -1; } -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> virDomainDeviceDefParseXMLMany will be used to parse '<devices>' XML elements that will be used to supply multiple hostdevs for hotplug/unplug. The idea is to parse each dev individually, by calling the same code that already handles it today. The result is returned in a new virDomainDeviceDefListPtr pointer type, which is an array of device definitions. This idea of calling existing functions to handles a single device definition in a loop is the central idea of how PCI multifunction hotplug and hotunplug is implemented in the next patches. Roughly speaking, we'll use virDomainDeviceDefListPtr lists to call (almost) the same hotplug/unplug mechanics we have today for each hostdev in the list. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/conf/domain_conf.c | 166 +++++++++++++++++++++++++++++++++++---- src/conf/domain_conf.h | 32 ++++++++ src/libvirt_private.syms | 5 ++ 3 files changed, 189 insertions(+), 14 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index fcd390ace4..4c74ee7229 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1351,6 +1351,90 @@ virDomainXMLOptionDispose(void *obj) (xmlopt->config.privFree)(xmlopt->config.priv); } +/* virDomainDeviceDefListAddCopy - add a *copy* of the device to this list */ +static int +virDomainDeviceDefListAddCopy(virDomainDeviceDefListPtr list, + virDomainDeviceDefPtr dev, + virDomainDeviceDefListDataPtr data, + void *parseOpaque) +{ + virDomainDeviceDefPtr copy = virDomainDeviceDefCopy(dev, + data->def, + data->xmlopt, + parseOpaque); + + if (!copy) + return -1; + + if (VIR_APPEND_ELEMENT(list->devs, list->count, copy) < 0) { + virDomainDeviceDefFree(copy); + return -1; + } + + return 0; +} + +void virDomainDeviceDefListFree(virDomainDeviceDefListPtr list) +{ + size_t i; + + if (!list) + return; + for (i = 0; i < list->count; i++) + virDomainDeviceDefFree(list->devs[i]); + VIR_FREE(list->devs); +} + +void virDomainDeviceDefListFreeShallow(virDomainDeviceDefListPtr list) +{ + size_t i; + + if (!list) + return; + for (i = 0; i < list->count; i++) + VIR_FREE(list->devs[i]); +} + + +/* virDomainDeviceDefListIter - Iterate through the list with the callback*/ +int +virDomainDeviceDefListIterate(virDomainDeviceDefListPtr list, + virDomainDeviceDefListIterCallback cb, + void *data) +{ + size_t i; + + for (i = 0; i < list->count; i++) + if (cb(list->devs[i], data)) + return -1; + + return 0; +} + +virDomainDeviceDefListPtr +virDomainDeviceDefListCopy(virDomainDeviceDefListPtr list, + virDomainDeviceDefListDataPtr data, + void *parseOpaque) +{ + size_t i; + virDomainDeviceDefListPtr devlist = NULL; + + if (list && (VIR_ALLOC(devlist) < 0)) + goto cleanup; + + for (i = 0; i < list->count; i++) { + if (virDomainDeviceDefListAddCopy(devlist, list->devs[i], + data, parseOpaque) < 0) + goto cleanup; + } + + return devlist; + cleanup: + virDomainDeviceDefListFree(devlist); + return NULL; +} + + /** * virDomainKeyWrapCipherDefParseXML: * @@ -16873,23 +16957,17 @@ virDomainVsockDefParseXML(virDomainXMLOptionPtr xmlopt, return g_steal_pointer(&vsock); } -virDomainDeviceDefPtr -virDomainDeviceDefParse(const char *xmlStr, - const virDomainDef *def, - virDomainXMLOptionPtr xmlopt, - void *parseOpaque, - unsigned int flags) + +static virDomainDeviceDefPtr +virDomainDeviceDefParseXML(xmlNodePtr node, + const virDomainDef *def, + virDomainXMLOptionPtr xmlopt, + xmlXPathContextPtr ctxt, + void *parseOpaque, + unsigned int flags) { - g_autoptr(xmlDoc) xml = NULL; - xmlNodePtr node; - g_autoptr(xmlXPathContext) ctxt = NULL; g_autoptr(virDomainDeviceDef) dev = NULL; - if (!(xml = virXMLParseStringCtxt(xmlStr, _("(device_definition)"), &ctxt))) - return NULL; - - node = ctxt->node; - if (VIR_ALLOC(dev) < 0) return NULL; @@ -17046,6 +17124,29 @@ virDomainDeviceDefParse(const char *xmlStr, } +virDomainDeviceDefPtr +virDomainDeviceDefParse(const char *xmlStr, + const virDomainDef *def, + virDomainXMLOptionPtr xmlopt, + void *parseOpaque, + unsigned int flags) +{ + g_autoptr(xmlDoc) xml = NULL; + xmlNodePtr node; + g_autoptr(xmlXPathContext) ctxt = NULL; + virDomainDeviceDefPtr dev = NULL; + + if (!(xml = virXMLParseStringCtxt(xmlStr, _("(device_definition)"), &ctxt))) + return NULL; + + node = ctxt->node; + + dev = virDomainDeviceDefParseXML(node, def, xmlopt, ctxt, parseOpaque, flags); + + return dev; + } + + virDomainDiskDefPtr virDomainDiskDefParse(const char *xmlStr, virDomainXMLOptionPtr xmlopt, @@ -32365,6 +32466,43 @@ virDomainDiskTranslateSourcePool(virDomainDiskDefPtr def) return 0; } +virDomainDeviceDefListPtr +virDomainDeviceDefParseXMLMany(const char *xml, + const virDomainDef *def, + virDomainXMLOptionPtr xmlopt, + void *parseOpaque, + unsigned int flags) +{ + g_autoptr(xmlXPathContext) ctxt = NULL; + g_autoptr(xmlDoc) xmlPtr = NULL; + xmlNodePtr node, root; + virDomainDeviceDefPtr dev = NULL; + virDomainDeviceDefListPtr devlist; + + if (!(xmlPtr = virXMLParseStringCtxt(xml, _("(device_definition)"), &ctxt))) + return NULL; + + if (VIR_ALLOC(devlist) < 0) + return NULL; + + root = xmlDocGetRootElement(xmlPtr); + node = root->children; + while (node) { + if (node->type == XML_ELEMENT_NODE) { + dev = virDomainDeviceDefParseXML(node, def, xmlopt, ctxt, + parseOpaque, flags); + if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0) { + virDomainDeviceDefFree(dev); + virDomainDeviceDefListFree(devlist); + return NULL; + } + dev = NULL; + } + node = node->next; + } + + return devlist; +} /** * virDomainDiskGetDetectZeroesMode: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 634865842a..e31fac8a4a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2748,6 +2748,33 @@ typedef enum { typedef int (*virDomainDefPostParseBasicCallback)(virDomainDefPtr def, void *opaque); +typedef struct _virDomainDeviceDefListData virDomainDeviceDefListData; +typedef virDomainDeviceDefListData *virDomainDeviceDefListDataPtr; +struct _virDomainDeviceDefListData { + const virDomainDef *def; + virDomainXMLOptionPtr xmlopt; +}; + +struct virDomainDeviceDefList { + virDomainDeviceDefPtr *devs; + size_t count; +}; +typedef struct virDomainDeviceDefList *virDomainDeviceDefListPtr; + +typedef int (*virDomainDeviceDefListIterCallback)(virDomainDeviceDefPtr dev, + void *opaque); +int virDomainDeviceDefListIterate(virDomainDeviceDefListPtr devlist, + virDomainDeviceDefListIterCallback cb, + void *data); +virDomainDeviceDefListPtr +virDomainDeviceDefListCopy(virDomainDeviceDefListPtr list, + virDomainDeviceDefListDataPtr data, + void *parseOpaque); + +void virDomainDeviceDefListFree(virDomainDeviceDefListPtr list); +void virDomainDeviceDefListFreeShallow(virDomainDeviceDefListPtr list); + + /* Called once after everything else has been parsed, for adjusting * overall domain defaults. * @parseOpaque is opaque data passed by virDomainDefParse* caller, @@ -3155,6 +3182,11 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(const char *xmlStr, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags); +virDomainDeviceDefListPtr virDomainDeviceDefParseXMLMany(const char *xmlStr, + const virDomainDef *def, + virDomainXMLOptionPtr xmlopt, + void *parseOpaque, + unsigned int flags); virDomainDiskDefPtr virDomainDiskDefParse(const char *xmlStr, virDomainXMLOptionPtr xmlopt, unsigned int flags); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 323291367f..29c6ac8e5e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -343,7 +343,12 @@ virDomainDeleteConfig; virDomainDeviceAliasIsUserAlias; virDomainDeviceDefCopy; virDomainDeviceDefFree; +virDomainDeviceDefListCopy; +virDomainDeviceDefListFree; +virDomainDeviceDefListFreeShallow; +virDomainDeviceDefListIterate; virDomainDeviceDefParse; +virDomainDeviceDefParseXMLMany; virDomainDeviceFindSCSIController; virDomainDeviceGetInfo; virDomainDeviceInfoIterate; -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_domain.c | 74 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 7 ++++ 2 files changed, 81 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index d5e3d1a3cc..0471a37803 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -13391,6 +13391,80 @@ qemuProcessEventFree(struct qemuProcessEvent *event) } +static bool isPCIMultifunctionDeviceXML(const char *xml) +{ + xmlDocPtr xmlptr; + + if (!(xmlptr = virXMLParse(NULL, xml, _("(device_definition)")))) { + /* We report error anyway later */ + return false; + } + + return STREQ((const char *)(xmlDocGetRootElement(xmlptr))->name, "devices"); +} + +static +int qemuDomainValidateMultifunctionDeviceList(virDomainDeviceDefListPtr devlist) +{ + size_t i; + + for (i = 0; i < devlist->count; i++) { + virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(devlist->devs[i]); + + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + return -1; + } + + if (devlist->devs[i]->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + } + } + + return 0; +} + + +virDomainDeviceDefListPtr +qemuDomainDeviceParseXMLMany(const char *xml, + virDomainDeviceDefListDataPtr data, + void *parseOpaque, + unsigned int parse_flags) +{ + virDomainDeviceDefListPtr devlist = NULL; + + if (isPCIMultifunctionDeviceXML(xml)) { + if (!(devlist = virDomainDeviceDefParseXMLMany(xml, data->def, + data->xmlopt, + parseOpaque, + parse_flags))) + return NULL; + + if (qemuDomainValidateMultifunctionDeviceList(devlist) < 0) + goto cleanup; + } else { + virDomainDeviceDefPtr dev = virDomainDeviceDefParse(xml, data->def, + data->xmlopt, + parseOpaque, + parse_flags); + if (!dev || VIR_ALLOC(devlist) < 0) + return NULL; + + if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0) + goto cleanup; + } + + return devlist; + + cleanup: + virDomainDeviceDefListFree(devlist); + return NULL; +} + + char * qemuDomainGetManagedPRSocketPath(qemuDomainObjPrivatePtr priv) { diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index b601d52126..31c843de96 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -1291,6 +1291,13 @@ qemuDomainNVRAMPathGenerate(virQEMUDriverConfigPtr cfg, virDomainEventSuspendedDetailType qemuDomainPausedReasonToSuspendedEvent(virDomainPausedReason reason); + +virDomainDeviceDefListPtr +qemuDomainDeviceParseXMLMany(const char *xml, + virDomainDeviceDefListDataPtr data, + void *parseOpaque, + unsigned int parse_flags); + int qemuDomainValidateActualNetDef(const virDomainNetDef *net, virQEMUCapsPtr qemuCaps); -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> This helps calling the routines with a list of devices. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_driver.c | 45 +++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dd9ae30bb5..43a3ffea99 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8117,11 +8117,8 @@ qemuCheckDiskConfigAgainstDomain(const virDomainDef *def, static int -qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, - virDomainDeviceDefPtr dev, - virQEMUCapsPtr qemuCaps, - unsigned int parse_flags, - virDomainXMLOptionPtr xmlopt) +qemuDomainAttachDeviceConfigInternal(virDomainDefPtr vmdef, + virDomainDeviceDefPtr dev) { virDomainDiskDefPtr disk; virDomainNetDefPtr net; @@ -8316,19 +8313,35 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, return -1; } - if (virDomainDefPostParse(vmdef, parse_flags, xmlopt, qemuCaps) < 0) - return -1; - return 0; } static int -qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, +qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev, virQEMUCapsPtr qemuCaps, unsigned int parse_flags, virDomainXMLOptionPtr xmlopt) +{ + if (virDomainDefCompatibleDevice(vmdef, dev, NULL, + VIR_DOMAIN_DEVICE_ACTION_ATTACH, + false) < 0) + return -1; + + if (qemuDomainAttachDeviceConfigInternal(vmdef, dev)) + return -1; + + if (virDomainDefPostParse(vmdef, parse_flags, xmlopt, qemuCaps) < 0) + return -1; + + return 0; +} + + +static int +qemuDomainDetachDeviceConfigInternal(virDomainDefPtr vmdef, + virDomainDeviceDefPtr dev) { virDomainDiskDefPtr disk, det_disk; virDomainNetDefPtr net; @@ -8518,6 +8531,20 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, return -1; } + return 0; +} + + +static int +qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, + virDomainDeviceDefPtr dev, + virQEMUCapsPtr qemuCaps, + unsigned int parse_flags, + virDomainXMLOptionPtr xmlopt) +{ + if (qemuDomainDetachDeviceConfigInternal(vmdef, dev)) + return -1; + if (virDomainDefPostParse(vmdef, parse_flags, xmlopt, qemuCaps) < 0) return -1; -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Helps calling multiple time per device. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_driver.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 43a3ffea99..f880a5d6cf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7824,9 +7824,9 @@ qemuDomainUndefine(virDomainPtr dom) } static int -qemuDomainAttachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - virQEMUDriverPtr driver) +qemuDomainAttachDeviceLiveInternal(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver) { int ret = -1; const char *alias = NULL; @@ -7972,13 +7972,28 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, virObjectEventStateQueue(driver->domainEventState, event); } + return ret; +} + +static int +qemuDomainAttachDeviceLive(virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + virQEMUDriverPtr driver) +{ + int ret = -1; + + if (virDomainDefCompatibleDevice(vm->def, dev, NULL, + VIR_DOMAIN_DEVICE_ACTION_ATTACH, + false) < 0) + return -1; + + ret = qemuDomainAttachDeviceLiveInternal(vm, dev, driver); if (ret == 0) ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); return ret; } - static int qemuDomainChangeDiskLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev, -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> With multifunction devices, multiple delete requests are sent to qemu and all the requests should be queued up. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_domain.h | 3 ++- src/qemu/qemu_hotplug.c | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 31c843de96..4fab1360f6 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -244,7 +244,8 @@ typedef enum { typedef struct _qemuDomainUnpluggingDevice qemuDomainUnpluggingDevice; typedef qemuDomainUnpluggingDevice *qemuDomainUnpluggingDevicePtr; struct _qemuDomainUnpluggingDevice { - const char *alias; + const char **aliases; + size_t naliases; qemuDomainUnpluggingDeviceStatus status; bool eventSeen; /* True if DEVICE_DELETED event arrived. */ }; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6991d7988c..b9946e8b8e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5134,13 +5134,19 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, static void qemuDomainMarkDeviceAliasForRemoval(virDomainObjPtr vm, - const char *alias) + const char *alias, + bool fresh) { qemuDomainObjPrivatePtr priv = vm->privateData; - memset(&priv->unplug, 0, sizeof(priv->unplug)); + if (fresh) + memset(&priv->unplug, 0, sizeof(priv->unplug)); + + if (VIR_REALLOC_N(priv->unplug.aliases, priv->unplug.naliases + 1) < 0) + return; + + priv->unplug.aliases[priv->unplug.naliases++] = alias; - priv->unplug.alias = alias; } @@ -5149,7 +5155,7 @@ qemuDomainMarkDeviceForRemoval(virDomainObjPtr vm, virDomainDeviceInfoPtr info) { - qemuDomainMarkDeviceAliasForRemoval(vm, info->alias); + qemuDomainMarkDeviceAliasForRemoval(vm, info->alias, true); } @@ -5157,7 +5163,9 @@ static void qemuDomainResetDeviceRemoval(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; - priv->unplug.alias = NULL; + priv->unplug.aliases = NULL; + VIR_FREE(priv->unplug.aliases); + priv->unplug.naliases = 0; priv->unplug.eventSeen = false; } @@ -5192,13 +5200,14 @@ qemuDomainWaitForDeviceRemoval(virDomainObjPtr vm) return 1; until += qemuDomainGetUnplugTimeout(vm); - while (priv->unplug.alias) { + /* All devices should get released around same time*/ + while (priv->unplug.naliases) { if ((rc = virDomainObjWaitUntil(vm, until)) == 1) return 0; if (rc < 0) { VIR_WARN("Failed to wait on unplug condition for domain '%s' " - "device '%s'", vm->def->name, priv->unplug.alias); + "device '%s'", vm->def->name, priv->unplug.aliases[0]); return 1; } } @@ -5224,14 +5233,16 @@ qemuDomainSignalDeviceRemoval(virDomainObjPtr vm, qemuDomainUnpluggingDeviceStatus status) { qemuDomainObjPrivatePtr priv = vm->privateData; + size_t i; - if (STREQ_NULLABLE(priv->unplug.alias, devAlias)) { - VIR_DEBUG("Removal of device '%s' continues in waiting thread", devAlias); - qemuDomainResetDeviceRemoval(vm); - priv->unplug.status = status; - priv->unplug.eventSeen = true; - virDomainObjBroadcast(vm); - return true; + for (i = 0; i < priv->unplug.naliases; i++) { + if (STREQ_NULLABLE(priv->unplug.aliases[i], devAlias)) { + VIR_DEBUG("Removal of device '%s' continues in waiting thread", devAlias); + VIR_DELETE_ELEMENT(priv->unplug.aliases, i, priv->unplug.naliases); + priv->unplug.status = status; + virDomainObjBroadcast(vm); + return true; + } } return false; } @@ -6075,7 +6086,7 @@ qemuDomainHotplugDelVcpu(virQEMUDriverPtr driver, return -1; } - qemuDomainMarkDeviceAliasForRemoval(vm, vcpupriv->alias); + qemuDomainMarkDeviceAliasForRemoval(vm, vcpupriv->alias, true); if (qemuDomainDeleteDevice(vm, vcpupriv->alias) < 0) { if (virDomainObjIsActive(vm)) -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/conf/device_conf.h | 6 +++ src/conf/domain_addr.c | 84 +++++++++++++++++++++++++++++++++++++++- src/conf/domain_addr.h | 5 +++ src/libvirt_private.syms | 1 + src/util/virpci.h | 2 + 5 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h index cc1707c1d1..e7d8e2539f 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -182,6 +182,12 @@ struct _virDomainDeviceInfo { bool isolationGroupLocked; }; +typedef struct _virDomainPCIMultifunctionAddressInfo virDomainPCIMultifunctionAddressInfo; +typedef virDomainPCIMultifunctionAddressInfo *virDomainPCIMultifunctionAddressInfoPtr; +struct _virDomainPCIMultifunctionAddressInfo { + virDomainDeviceInfoPtr infos[VIR_PCI_MAX_FUNCTIONS]; +}; + void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info); void virDomainDeviceInfoFree(virDomainDeviceInfoPtr info); diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 5c8fc62725..0a1c9ce142 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -21,11 +21,12 @@ #include <config.h> +#include "device_conf.h" +#include "domain_addr.h" #include "viralloc.h" +#include "virhashcode.h" #include "virlog.h" #include "virstring.h" -#include "domain_addr.h" -#include "virhashcode.h" #define VIR_FROM_THIS VIR_FROM_DOMAIN @@ -990,6 +991,85 @@ virDomainPCIAddressExtensionReleaseAddr(virDomainPCIAddressSetPtr addrs, } +int +virDomainPCIAddressEnsureMultifunctionAddress(virDomainPCIAddressSetPtr addrs, + virDomainPCIMultifunctionAddressInfoPtr pcicard) +{ + size_t i; + int ret = 0; + virPCIDeviceAddressPtr addr1 = NULL, addr2 = NULL; + virDomainDeviceInfoPtr dev = NULL; + + if (!pcicard->infos[0]) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Function-Zero missing on the slot")); + return -1; + } + + /* If the address is given by the user, make sure they belong + * to same slot */ + for (i = 0; i < VIR_PCI_MAX_FUNCTIONS; i++) { + g_autofree char *addrStr = NULL; + dev = pcicard->infos[i]; + + if (dev && !dev->pciConnectFlags) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Not a PCI Multifunction device.")); + return -1; + } + if (dev && virDeviceInfoPCIAddressIsPresent(dev)) { + /* Pick one and compare against rest of the user given */ + addr1 = addr1 ? addr1 : &dev->addr.pci; + addr2 = &dev->addr.pci; + + if (!(addrStr = virPCIDeviceAddressAsString(addr2))) + return -1; + + if (!virDomainPCIAddressValidate(addrs, addr2, addrStr, + dev->pciConnectFlags, true)) + return -1; + + if (!(addr1->domain == addr2->domain && addr1->bus == addr2->bus && + addr1->slot == addr2->slot)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Addresses belong to different PCI slots")); + return -1; + } + } + } + + /* Reserve all the user given addresses. */ + for (i = 0; i < VIR_PCI_MAX_FUNCTIONS; i++) { + dev = pcicard->infos[i]; + if (dev && virDeviceInfoPCIAddressIsPresent(dev)) { + ret = virDomainPCIAddressReserveAddrInternal(addrs, &dev->addr.pci, + dev->pciConnectFlags, + dev->isolationGroup, + dev->aggregateSlotIdx, + true); + if (ret < 0) + return ret; + } + } + + /* If the user has not given addresses, start with function zero */ + for (i = 0; i < VIR_PCI_MAX_FUNCTIONS; i++) { + dev = pcicard->infos[i]; + if (dev && !virDeviceInfoPCIAddressIsPresent(dev)) { + ret = virDomainPCIAddressReserveNextAddr(addrs, dev, + dev->pciConnectFlags, i); + if (ret < 0) + return ret; + } + } + + /* Set multi on overriding what user has set. */ + pcicard->infos[0]->addr.pci.multi = VIR_TRISTATE_SWITCH_ON; + + return 0; +} + + void virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs, virPCIDeviceAddressPtr addr) diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index ddcff36e51..e3c1293e1d 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -180,6 +180,11 @@ int virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs, virDomainPCIConnectFlags flags) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int +virDomainPCIAddressEnsureMultifunctionAddress(virDomainPCIAddressSetPtr addrs, + virDomainPCIMultifunctionAddressInfoPtr pcicard) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + void virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs, virPCIDeviceAddressPtr addr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 29c6ac8e5e..99afe20294 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -148,6 +148,7 @@ virDomainCCWAddressSetFree; virDomainPCIAddressBusIsFullyReserved; virDomainPCIAddressBusSetModel; virDomainPCIAddressEnsureAddr; +virDomainPCIAddressEnsureMultifunctionAddress; virDomainPCIAddressExtensionReleaseAddr; virDomainPCIAddressExtensionReserveAddr; virDomainPCIAddressExtensionReserveNextAddr; diff --git a/src/util/virpci.h b/src/util/virpci.h index 64a9109d9b..8f20e31b43 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -25,6 +25,8 @@ #include "virobject.h" #include "virenum.h" +#define VIR_PCI_MAX_FUNCTIONS 8 + typedef struct _virPCIDevice virPCIDevice; typedef virPCIDevice *virPCIDevicePtr; typedef struct _virPCIDeviceAddress virPCIDeviceAddress; -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> The support to allow multifunction device hotplug consists of using virDomainDeviceDefListPtr lists instead of single virDomainDeviceDefPtr device definitions in both qemuDomainAttachDeviceConfig() and qemuDomainAttachDeviceLive(). In AttachDeviceConfig() the same existing single device code is ran for all devs in the list. AttachDeviceLive() will verify if the list has size = 1 (meaning it is a single device attach) and redirect the code to the regular single dev attach procedure. If list size > 1, we'll execute a new specialized function called qemuDomainAttachMultifunctionDevice(), which is prepared to handle the nuances of this type of hotplug. The changes in both AttachDeviceLive() and AttachDeviceConfig() implied in changes to be made in qemuDomainAttachDeviceLiveAndConfig(), which is now handling a device list instead of a single device. No changes in regular device hotplug mechanics were made. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_domain_address.c | 74 +++++++++++ src/qemu/qemu_domain_address.h | 4 + src/qemu/qemu_driver.c | 119 +++++++++++------- src/qemu/qemu_hotplug.c | 86 +++++++++++++ src/qemu/qemu_hotplug.h | 4 + tests/qemuhotplugtest.c | 39 ++++-- ...emuhotplug-multifunction-hostdev-pci-2.xml | 14 +++ .../qemuhotplug-multifunction-hostdev-pci.xml | 26 ++++ ...ug-base-live+multifunction-hostdev-pci.xml | 82 ++++++++++++ ...-base-live+multifunction-hostdev-pci-2.xml | 59 +++++++++ ...es-base-live+multifunction-hostdev-pci.xml | 69 ++++++++++ 11 files changed, 524 insertions(+), 52 deletions(-) create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 05ef4de100..94d91a5daf 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -23,6 +23,7 @@ #include "qemu_domain_address.h" #include "qemu_domain.h" +#include "domain_conf.h" #include "viralloc.h" #include "virhostdev.h" #include "virerror.h" @@ -3439,6 +3440,79 @@ qemuDomainEnsurePCIAddress(virDomainObjPtr obj, info->pciConnectFlags); } + +int +qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(virDomainObjPtr vm, + virDomainDeviceDefListPtr devlist, + virQEMUDriverPtr driver) +{ + int ret = -1, aggrslotidx = 0; + virBitmapPtr slotmap = NULL; + size_t i; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainPCIMultifunctionAddressInfoPtr devinfos = NULL; + + if (devlist->count > VIR_PCI_MAX_FUNCTIONS) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("More devices per slot found")); + return -1; + } + + for (i = 0; i < devlist->count; i++) + qemuDomainFillDevicePCIConnectFlags(vm->def, devlist->devs[i], + priv->qemuCaps, driver); + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + devlist->count) < 0) + return -1; + + /* Temporarily add the devices to the domain def to get the + * next aggregateIdx */ + for (i = 0; i < devlist->count; i++) + vm->def->hostdevs[vm->def->nhostdevs++] = devlist->devs[i]->data.hostdev; + + for (i = 0; i < devlist->count; i++) { + virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev; + + if (qemuDomainIsPSeries(vm->def)) + /* Isolation groups are only relevant for pSeries guests */ + qemuDomainFillDeviceIsolationGroup(vm->def, devlist->devs[i]); + + qemuDomainSetDeviceSlotAggregateIdx(vm->def, devlist->devs[i]); + aggrslotidx = aggrslotidx ? aggrslotidx : hostdev->info->aggregateSlotIdx; + + if (aggrslotidx != hostdev->info->aggregateSlotIdx) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Devices belong to different PCI slots")); + return -1; + } + } + + for (i = 0; i < devlist->count; i++) + vm->def->hostdevs[--(vm->def->nhostdevs)] = NULL; + + slotmap = virDomainDefHostdevGetPCIOnlineFunctionMap(vm->def, aggrslotidx); + if (!virBitmapIsAllClear(slotmap)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Device already assigned to guest")); + return -1; + } + + if (VIR_ALLOC(devinfos) < 0) + return -1; + + for (i = 0; i < devlist->count; i++) { + virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev; + virPCIDeviceAddress addr = hostdev->source.subsys.u.pci.addr; + + devinfos->infos[addr.function] = hostdev->info; + } + + ret = virDomainPCIAddressEnsureMultifunctionAddress(priv->pciaddrs, devinfos); + VIR_FREE(devinfos); + return ret; +} + + void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, virDomainDeviceInfoPtr info) diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h index 8d51456f5b..b9ba577001 100644 --- a/src/qemu/qemu_domain_address.h +++ b/src/qemu/qemu_domain_address.h @@ -50,6 +50,10 @@ int qemuDomainEnsurePCIAddress(virDomainObjPtr obj, virQEMUDriverPtr driver) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +int qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(virDomainObjPtr obj, + virDomainDeviceDefListPtr devlist, + virQEMUDriverPtr driver); + void qemuDomainFillDeviceIsolationGroup(virDomainDefPtr def, virDomainDeviceDefPtr dev) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f880a5d6cf..3217d93a73 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7977,17 +7977,28 @@ qemuDomainAttachDeviceLiveInternal(virDomainObjPtr vm, static int qemuDomainAttachDeviceLive(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, + virDomainDeviceDefListPtr devlist, virQEMUDriverPtr driver) { int ret = -1; + size_t i; - if (virDomainDefCompatibleDevice(vm->def, dev, NULL, - VIR_DOMAIN_DEVICE_ACTION_ATTACH, - false) < 0) - return -1; + for (i = 0; i < devlist->count; i++) + if (virDomainDefCompatibleDevice(vm->def, devlist->devs[i], NULL, + VIR_DOMAIN_DEVICE_ACTION_ATTACH, + false) < 0) + return ret; + + if (devlist->count > 1) { + ret = qemuDomainAttachMultifunctionDevice(vm, devlist, driver); + if (ret == 0) { + for (i = 0; i < devlist->count; i++) + devlist->devs[i]->data.hostdev = NULL; + } + } else if (devlist->count == 1) { + ret = qemuDomainAttachDeviceLiveInternal(vm, devlist->devs[0], driver); + } - ret = qemuDomainAttachDeviceLiveInternal(vm, dev, driver); if (ret == 0) ret = qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE); @@ -8334,18 +8345,24 @@ qemuDomainAttachDeviceConfigInternal(virDomainDefPtr vmdef, static int qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, - virDomainDeviceDefPtr dev, + virDomainDeviceDefListPtr devlist, virQEMUCapsPtr qemuCaps, unsigned int parse_flags, virDomainXMLOptionPtr xmlopt) { - if (virDomainDefCompatibleDevice(vmdef, dev, NULL, - VIR_DOMAIN_DEVICE_ACTION_ATTACH, - false) < 0) - return -1; + size_t i; - if (qemuDomainAttachDeviceConfigInternal(vmdef, dev)) - return -1; + for (i = 0; i < devlist->count; i++) { + if (virDomainDefCompatibleDevice(vmdef, devlist->devs[i], NULL, + VIR_DOMAIN_DEVICE_ACTION_ATTACH, + false) < 0) + return -1; + } + + for (i = 0; i < devlist->count; i++) { + if (qemuDomainAttachDeviceConfigInternal(vmdef, devlist->devs[i])) + return -1; + } if (virDomainDefPostParse(vmdef, parse_flags, xmlopt, qemuCaps) < 0) return -1; @@ -8711,9 +8728,14 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm, qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDefPtr vmdef = NULL; g_autoptr(virQEMUDriverConfig) cfg = NULL; - virDomainDeviceDefPtr devConf = NULL; - virDomainDeviceDef devConfSave = { 0 }; + virDomainDeviceDefPtr devConfSave = NULL; virDomainDeviceDefPtr devLive = NULL; + virDomainDeviceDefListPtr devListConf = NULL; + virDomainDeviceDefListPtr devListConfSave = NULL; + virDomainDeviceDefListPtr devListLive = NULL; + virDomainDeviceDefListData data = {.def = vm->def, + .xmlopt = driver->xmlopt}; + size_t i; int ret = -1; unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_ABI_UPDATE; @@ -8732,52 +8754,58 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm, if (!vmdef) goto cleanup; - if (!(devConf = virDomainDeviceDefParse(xml, vmdef, - driver->xmlopt, priv->qemuCaps, - parse_flags))) + devListConf = qemuDomainDeviceParseXMLMany(xml, &data, priv->qemuCaps, + parse_flags); + if (!devListConf) goto cleanup; /* - * devConf will be NULLed out by + * devListConf will be NULLed out by * qemuDomainAttachDeviceConfig(), so save it for later use by * qemuDomainAttachDeviceLiveAndConfigHomogenize() */ - devConfSave = *devConf; - - if (virDomainDeviceValidateAliasForHotplug(vm, devConf, - VIR_DOMAIN_AFFECT_CONFIG) < 0) - goto cleanup; + if (flags & VIR_DOMAIN_AFFECT_LIVE) { + devListConfSave = virDomainDeviceDefListCopy(devListConf, + &data, + priv->qemuCaps); + if (!devListConfSave) + goto cleanup; + } - if (virDomainDefCompatibleDevice(vmdef, devConf, NULL, - VIR_DOMAIN_DEVICE_ACTION_ATTACH, - false) < 0) - goto cleanup; + for (i = 0; i < devListConf->count; i++) { + if (virDomainDeviceValidateAliasForHotplug(vm, + devListConf->devs[i], + flags) < 0) + goto cleanup; + } - if (qemuDomainAttachDeviceConfig(vmdef, devConf, priv->qemuCaps, + if (qemuDomainAttachDeviceConfig(vmdef, devListConf, priv->qemuCaps, parse_flags, driver->xmlopt) < 0) goto cleanup; } if (flags & VIR_DOMAIN_AFFECT_LIVE) { - if (!(devLive = virDomainDeviceDefParse(xml, vm->def, - driver->xmlopt, priv->qemuCaps, - parse_flags))) + devListLive = qemuDomainDeviceParseXMLMany(xml, &data, priv->qemuCaps, + parse_flags); + if (!devListLive) goto cleanup; - if (flags & VIR_DOMAIN_AFFECT_CONFIG) - qemuDomainAttachDeviceLiveAndConfigHomogenize(&devConfSave, devLive); + for (i = 0; i < devListLive->count; i++) { + devLive = devListLive->devs[i]; - if (virDomainDeviceValidateAliasForHotplug(vm, devLive, - VIR_DOMAIN_AFFECT_LIVE) < 0) - goto cleanup; + if (flags & VIR_DOMAIN_AFFECT_CONFIG) { + devConfSave = devListConfSave->devs[i]; + qemuDomainAttachDeviceLiveAndConfigHomogenize(devConfSave, + devLive); + } - if (virDomainDefCompatibleDevice(vm->def, devLive, NULL, - VIR_DOMAIN_DEVICE_ACTION_ATTACH, - true) < 0) - goto cleanup; + if (virDomainDeviceValidateAliasForHotplug(vm, devLive, + VIR_DOMAIN_AFFECT_LIVE) < 0) + goto cleanup; + } - if (qemuDomainAttachDeviceLive(vm, devLive, driver) < 0) + if (qemuDomainAttachDeviceLive(vm, devListLive, driver) < 0) goto cleanup; /* * update domain status forcibly because the domain status may be @@ -8800,8 +8828,11 @@ qemuDomainAttachDeviceLiveAndConfig(virDomainObjPtr vm, ret = 0; cleanup: virDomainDefFree(vmdef); - virDomainDeviceDefFree(devConf); - virDomainDeviceDefFree(devLive); + devConfSave = NULL; + devLive = NULL; + virDomainDeviceDefListFree(devListConf); + virDomainDeviceDefListFree(devListConfSave); + virDomainDeviceDefListFree(devListLive); return ret; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index b9946e8b8e..a6c520ec1b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1685,6 +1685,92 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, } +static int +qemuiHostdevPCIMultifunctionDevicesListSort(const void *p1, + const void *p2) +{ + virDomainDeviceDefPtr a = *(virDomainDeviceDefPtr *) p1; + virDomainDeviceDefPtr b = *(virDomainDeviceDefPtr *) p2; + virPCIDeviceAddressPtr addr1 = &a->data.hostdev->source.subsys.u.pci.addr; + virPCIDeviceAddressPtr addr2 = &b->data.hostdev->source.subsys.u.pci.addr; + + return addr1->function - addr2->function; +} + + +int +qemuDomainAttachMultifunctionDevice(virDomainObjPtr vm, + virDomainDeviceDefListPtr devlist, + virQEMUDriverPtr driver) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + size_t i, d, h = devlist->count; + int ret = -1; + char *alias; + virObjectEventPtr event; + virDomainHostdevDefPtr hostdev; + + qsort(devlist->devs, devlist->count, sizeof(*devlist->devs), + qemuiHostdevPCIMultifunctionDevicesListSort); + + if (qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(vm, devlist, + driver) < 0) + return -1; + + for (d = 0; d < devlist->count; d++) { + hostdev = devlist->devs[d]->data.hostdev; + + if (qemuDomainAttachPCIHostDevicePrepare(driver, vm->def, hostdev, + priv->qemuCaps) < 0) + goto cleanup; + } + + /* Hotplug all functions, and Primary at last */ + for (h = devlist->count; h > 0; h--) { + /* The functions need not be contiguous, as a card may be sold with + * minimal functionality and then install the additional functions on + * purchase into any of the daughter-card connectors. + */ + hostdev = devlist->devs[h-1]->data.hostdev; + + ret = qemuDomainAttachHostPCIDevice(driver, vm, hostdev); + if (ret) + goto release; + + alias = hostdev->info->alias; + hostdev = NULL; + + event = virDomainEventDeviceAddedNewFromObj(vm, alias); + virObjectEventStateQueue(driver->domainEventState, event); + } + + release: + /* Release addresses for the device which are not hotplugged. + */ + for (i = 0; i < h; i++) + qemuDomainReleaseDeviceAddress(vm, devlist->devs[i]->data.hostdev->info); + + cleanup: + /* If none are actually hotplugged and just detached from the + * host driver reattach the devices to host driver. + * + * If one of the hotplug failed, those which are already hotplugged cannot + * be unplugged as they are released by qemu only on guest reboot even + * if we issue device_del on them. + * So, dont attempt to reattach any of them. + * NB: Let them be in the guest as they are not used anyway without + * function-zero? + */ + if (d > 0 && h == devlist->count) { + for (i = 0; i < d; i++) + qemuHostdevReAttachPCIDevices(driver, vm->def->name, + &devlist->devs[i]->data.hostdev, 1); + } + + return ret; +} + + void qemuDomainDelTLSObjects(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 9df4a4e5f7..b62528a6d9 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -126,6 +126,10 @@ int qemuDomainAttachPCIHostDevicePrepare(virQEMUDriverPtr driver, virDomainDefPtr def, virDomainHostdevDefPtr dev, virQEMUCapsPtr qemuCaps); +int +qemuDomainAttachMultifunctionDevice(virDomainObjPtr vm, + virDomainDeviceDefListPtr devlist, + virQEMUDriverPtr driver); int qemuDomainChrInsert(virDomainDefPtr vmdef, diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 57f64d191c..32d186ef73 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -90,6 +90,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt, virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER); virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SCSI_BLOCK); virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY); + virQEMUCapsSet(priv->qemuCaps, X_QEMU_CAPS_PCI_MULTIFUNCTION); if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0) return -1; @@ -118,9 +119,14 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt, static int testQemuHotplugAttach(virDomainObjPtr vm, - virDomainDeviceDefPtr dev) + virDomainDeviceDefListPtr devlist) { int ret = -1; + virDomainDeviceDefPtr dev; + + if (devlist->count > 1) + return qemuDomainAttachMultifunctionDevice(vm, devlist, &driver); + dev = devlist->devs[0]; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -245,7 +251,9 @@ testQemuHotplug(const void *data) bool keep = test->keep; unsigned int device_parse_flags = 0; virDomainObjPtr vm = NULL; - virDomainDeviceDefPtr dev = NULL; + virDomainDeviceDefPtr dev = NULL; /* temporary */ + virDomainDeviceDefListPtr devlist = NULL; + virDomainDeviceDefListData listdata; virCapsPtr caps = NULL; qemuMonitorTestPtr test_mon = NULL; qemuDomainObjPrivatePtr priv = NULL; @@ -283,11 +291,15 @@ testQemuHotplug(const void *data) if (test->action == ATTACH) device_parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE; - if (!(dev = virDomainDeviceDefParse(device_xml, vm->def, - driver.xmlopt, NULL, - device_parse_flags))) + listdata.def = vm->def; + listdata.xmlopt = driver.xmlopt; + devlist = qemuDomainDeviceParseXMLMany(device_xml, &listdata, NULL, + device_parse_flags); + if (!devlist) goto cleanup; + dev = devlist->devs[0]; /* temporary */ + /* Now is the best time to feed the spoofed monitor with predefined * replies. */ if (!(test_mon = qemuMonitorTestNew(driver.xmlopt, vm, &driver, @@ -316,11 +328,11 @@ testQemuHotplug(const void *data) switch (test->action) { case ATTACH: - ret = testQemuHotplugAttach(vm, dev); + ret = testQemuHotplugAttach(vm, devlist); if (ret == 0) { /* vm->def stolen dev->data.* so we just need to free the dev * envelope */ - VIR_FREE(dev); + virDomainDeviceDefListFreeShallow(devlist); } if (ret == 0 || fail) ret = testQemuHotplugCheckResult(vm, result_xml, @@ -356,7 +368,7 @@ testQemuHotplug(const void *data) virObjectUnref(vm); test->vm = NULL; } - virDomainDeviceDefFree(dev); + virDomainDeviceDefListFree(devlist); virObjectUnref(caps); qemuMonitorTestFree(test_mon); return ((ret < 0 && fail) || (!ret && !fail)) ? 0 : -1; @@ -840,6 +852,17 @@ mymain(void) "device_add", QMP_OK); DO_TEST_DETACH("pseries-base-live", "hostdev-pci", false, false, "device_del", QMP_DEVICE_DELETED("hostdev0") QMP_OK); + DO_TEST_ATTACH("base-live", "multifunction-hostdev-pci", false, false, + "device_add", QMP_OK, + "device_add", QMP_OK, + "device_add", QMP_OK, + "device_add", QMP_OK); + + qemuTestSetHostArch(&driver, VIR_ARCH_PPC64); + DO_TEST_ATTACH("pseries-base-live", "multifunction-hostdev-pci-2", false, false, + "device_add", QMP_OK, + "device_add", QMP_OK); + qemuTestSetHostArch(&driver, VIR_ARCH_X86_64); DO_TEST_ATTACH("base-live", "watchdog", false, true, "watchdog-set-action", QMP_OK, diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml new file mode 100644 index 0000000000..02e2236e5d --- /dev/null +++ b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-2.xml @@ -0,0 +1,14 @@ +<devices> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> + </source> + </hostdev> +</devices> diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml new file mode 100644 index 0000000000..bef4be219f --- /dev/null +++ b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci.xml @@ -0,0 +1,26 @@ +<devices> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x1'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> + </source> + </hostdev> +</devices> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml new file mode 100644 index 0000000000..9dd04d3350 --- /dev/null +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci.xml @@ -0,0 +1,82 @@ +<domain type='kvm' id='7'> + <name>hotplug</name> + <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid> + <memory unit='KiB'>4194304</memory> + <currentMemory unit='KiB'>4194304</currentMemory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <alias name='ide'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='scsi' index='0' model='virtio-scsi'> + <alias name='scsi0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci'/> + </controller> + <controller type='virtio-serial' index='0'> + <alias name='virtio-serial0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </controller> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> + </source> + <alias name='hostdev0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x3'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + </source> + <alias name='hostdev1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x2'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x1'/> + </source> + <alias name='hostdev2'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x1'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> + </source> + <alias name='hostdev3'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0' multifunction='on'/> + </hostdev> + <memballoon model='none'/> + </devices> + <seclabel type='none' model='none'/> +</domain> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml new file mode 100644 index 0000000000..f99c1b3741 --- /dev/null +++ b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci-2.xml @@ -0,0 +1,59 @@ +<domain type='kvm' id='7'> + <name>hotplug</name> + <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid> + <memory unit='KiB'>4194304</memory> + <currentMemory unit='KiB'>4194304</currentMemory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='ppc64' machine='pseries'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-ppc64</emulator> + <controller type='pci' index='0' model='pci-root'> + <model name='spapr-pci-host-bridge'/> + <target index='0'/> + <alias name='pci.0'/> + </controller> + <controller type='pci' index='1' model='pci-root'> + <model name='spapr-pci-host-bridge'/> + <target index='1'/> + <alias name='pci.1'/> + </controller> + <controller type='usb' index='0'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/> + </controller> + <input type='keyboard' bus='usb'> + <alias name='input0'/> + <address type='usb' bus='0' port='1'/> + </input> + <input type='mouse' bus='usb'> + <alias name='input1'/> + <address type='usb' bus='0' port='2'/> + </input> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x1'/> + </source> + <alias name='hostdev0'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x1'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0001' bus='0x01' slot='0x00' function='0x0'/> + </source> + <alias name='hostdev1'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x0' multifunction='on'/> + </hostdev> + <memballoon model='none'/> + <panic model='pseries'/> + </devices> + <seclabel type='none' model='none'/> +</domain> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml new file mode 100644 index 0000000000..9338d42e2e --- /dev/null +++ b/tests/qemuhotplugtestdomains/qemuhotplug-pseries-base-live+multifunction-hostdev-pci.xml @@ -0,0 +1,69 @@ +<domain type='kvm' id='7'> + <name>hotplug</name> + <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid> + <memory unit='KiB'>4194304</memory> + <currentMemory unit='KiB'>4194304</currentMemory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='ppc64' machine='pseries'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-ppc64</emulator> + <controller type='pci' index='0' model='pci-root'> + <model name='spapr-pci-host-bridge'/> + <target index='0'/> + <alias name='pci.0'/> + </controller> + <controller type='pci' index='1' model='pci-root'> + <model name='spapr-pci-host-bridge'/> + <target index='1'/> + <alias name='pci.1'/> + </controller> + <controller type='usb' index='0'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/> + </controller> + <input type='keyboard' bus='usb'> + <alias name='input0'/> + <address type='usb' bus='0' port='1'/> + </input> + <input type='mouse' bus='usb'> + <alias name='input1'/> + <address type='usb' bus='0' port='2'/> + </input> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + </source> + <alias name='hostdev0'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x2'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x1'/> + </source> + <alias name='hostdev1'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x1'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> + </source> + <alias name='hostdev2'/> + <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x0' multifunction='on'/> + </hostdev> + <memballoon model='none'> + <alias name='balloon0'/> + </memballoon> + <panic model='pseries'/> + </devices> + <seclabel type='none' model='none'/> +</domain> -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> PCI hostdevs once part of the domain can't be changed. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_driver.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3217d93a73..ce44a1455a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8889,6 +8889,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, virDomainObjPtr vm = NULL; qemuDomainObjPrivatePtr priv; virDomainDefPtr vmdef = NULL; + virDomainDeviceDefListPtr devlist; + virDomainDeviceDefListData data = {.xmlopt = driver->xmlopt}; virDomainDeviceDefPtr dev = NULL, dev_copy = NULL; bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0; int ret = -1; @@ -8906,6 +8908,8 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, if (!(vm = qemuDomainObjFromDomain(dom))) goto cleanup; + data.def = vm->def; + priv = vm->privateData; if (virDomainUpdateDeviceFlagsEnsureACL(dom->conn, vm->def, flags) < 0) @@ -8921,12 +8925,20 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, !(flags & VIR_DOMAIN_AFFECT_LIVE)) parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE; - dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, - driver->xmlopt, priv->qemuCaps, - parse_flags); - if (dev == NULL) + + devlist = qemuDomainDeviceParseXMLMany(xml, &data, priv->qemuCaps, + parse_flags); + if (!devlist) goto endjob; + if (devlist->count > 1) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("Update of multifunction devices is not supported")); + goto endjob; + } + + dev = dev_copy = devlist->devs[0]; + if (flags & VIR_DOMAIN_AFFECT_CONFIG && flags & VIR_DOMAIN_AFFECT_LIVE) { /* If we are affecting both CONFIG and LIVE -- 2.26.2

From: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> The same design ideas used in multifunction hotplug were used here as well. qemuDomainDetachDeviceConfig() was changed to accept a list of devices instead of a single device definition. qemuDomainDetachDeviceLiveAndConfig() was changed to handle device lists as well. For VIR_DOMAIN_AFFECT_LIVE, check if we're handling a single device unplug (devlist size = 1) and forward it to the regular qemuDomainDetachDeviceLive() function. Otherwise, a new qemuDomainDetachMultifunctionDevice() was added to handle the hot-unplug of multifunction devices. DetachMultifunctionDevice() is not considering the new multifunction unplug mechanics for the Pseries guest, present in QEMU 4.2 (the newest release ATM), to not break compatibility with older QEMU versions. This will be done properly in a later patch. Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_driver.c | 66 +++++++++++++++------- src/qemu/qemu_hotplug.c | 116 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 5 ++ src/qemu/qemu_validate.c | 2 +- src/qemu/qemu_validate.h | 1 + tests/qemuhotplugtest.c | 27 ++++++--- 6 files changed, 189 insertions(+), 28 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ce44a1455a..e7119fda8c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -50,6 +50,7 @@ #include "qemu_security.h" #include "qemu_checkpoint.h" #include "qemu_backup.h" +#include "qemu_validate.h" #include "virerror.h" #include "virlog.h" @@ -8569,17 +8570,27 @@ qemuDomainDetachDeviceConfigInternal(virDomainDefPtr vmdef, static int qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, - virDomainDeviceDefPtr dev, + virDomainDeviceDefListPtr devlist, virQEMUCapsPtr qemuCaps, unsigned int parse_flags, virDomainXMLOptionPtr xmlopt) { - if (qemuDomainDetachDeviceConfigInternal(vmdef, dev)) - return -1; + size_t i; + + for (i = 0; i < devlist->count; i++) { + if (qemuDomainDetachDeviceConfigInternal(vmdef, devlist->devs[i])) + return -1; + } if (virDomainDefPostParse(vmdef, parse_flags, xmlopt, qemuCaps) < 0) return -1; + /* Don't allow removing the primary function alone for a + * multifunction device (devlist->count greater than 1) + * leading to guest start failure later. */ + if (devlist->count > 1 && qemuValidateDomainDefPCIHostdevs(vmdef) < 0) + return -1; + return 0; } @@ -9012,7 +9023,9 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, { qemuDomainObjPrivatePtr priv = vm->privateData; g_autoptr(virQEMUDriverConfig) cfg = NULL; - virDomainDeviceDefPtr dev = NULL, dev_copy = NULL; + virDomainDeviceDefListPtr devlist = NULL, devcopylist = NULL; + virDomainDeviceDefListData data = {.def = vm->def, + .xmlopt = driver->xmlopt}; unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE; virDomainDefPtr vmdef = NULL; int ret = -1; @@ -9026,11 +9039,11 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, !(flags & VIR_DOMAIN_AFFECT_LIVE)) parse_flags |= VIR_DOMAIN_DEF_PARSE_INACTIVE; - dev = dev_copy = virDomainDeviceDefParse(xml, vm->def, - driver->xmlopt, priv->qemuCaps, - parse_flags); - if (dev == NULL) + devlist = qemuDomainDeviceParseXMLMany(xml, &data, + priv->qemuCaps, parse_flags); + if (!devlist) goto cleanup; + devcopylist = devlist; if (flags & VIR_DOMAIN_AFFECT_CONFIG && flags & VIR_DOMAIN_AFFECT_LIVE) { @@ -9038,9 +9051,9 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, * create a deep copy of device as adding * to CONFIG takes one instance. */ - dev_copy = virDomainDeviceDefCopy(dev, vm->def, - driver->xmlopt, priv->qemuCaps); - if (!dev_copy) + devcopylist = virDomainDeviceDefListCopy(devlist, &data, + priv->qemuCaps); + if (!devcopylist) goto cleanup; } @@ -9050,7 +9063,7 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, if (!vmdef) goto cleanup; - if (qemuDomainDetachDeviceConfig(vmdef, dev, priv->qemuCaps, + if (qemuDomainDetachDeviceConfig(vmdef, devlist, priv->qemuCaps, parse_flags, driver->xmlopt) < 0) goto cleanup; @@ -9059,7 +9072,13 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, if (flags & VIR_DOMAIN_AFFECT_LIVE) { int rc; - if ((rc = qemuDomainDetachDeviceLive(vm, dev_copy, driver, false)) < 0) + if (devlist->count > 1) { + if ((rc = qemuDomainDetachMultifunctionDevice(vm, devlist, driver)) < 0) + goto cleanup; + } else if ((rc = qemuDomainDetachDeviceLive(vm, + devcopylist->devs[0], + driver, + false)) < 0) goto cleanup; if (rc == 0 && qemuDomainUpdateDeviceList(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) @@ -9086,9 +9105,9 @@ qemuDomainDetachDeviceLiveAndConfig(virQEMUDriverPtr driver, ret = 0; cleanup: - if (dev != dev_copy) - virDomainDeviceDefFree(dev_copy); - virDomainDeviceDefFree(dev); + if (devlist != devcopylist) + virDomainDeviceDefListFree(devcopylist); + virDomainDeviceDefListFree(devlist); virDomainDefFree(vmdef); return ret; } @@ -9106,6 +9125,7 @@ qemuDomainDetachDeviceAliasLiveAndConfig(virQEMUDriverPtr driver, virDomainDefPtr persistentDef = NULL; virDomainDefPtr vmdef = NULL; unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE; + virDomainDeviceDefListPtr devlist = NULL; int ret = -1; virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | @@ -9121,16 +9141,22 @@ qemuDomainDetachDeviceAliasLiveAndConfig(virQEMUDriverPtr driver, goto cleanup; if (persistentDef) { - virDomainDeviceDef dev; + virDomainDeviceDefPtr dev; if (!(vmdef = virDomainObjCopyPersistentDef(vm, driver->xmlopt, priv->qemuCaps))) goto cleanup; - if (virDomainDefFindDevice(vmdef, alias, &dev, true) < 0) + if (virDomainDefFindDevice(vmdef, alias, dev, true) < 0) + goto cleanup; + + if (VIR_ALLOC(devlist) < 0) goto cleanup; - if (qemuDomainDetachDeviceConfig(vmdef, &dev, priv->qemuCaps, + if (VIR_APPEND_ELEMENT(devlist->devs, devlist->count, dev) < 0) + goto cleanup; + + if (qemuDomainDetachDeviceConfig(vmdef, devlist, priv->qemuCaps, parse_flags, driver->xmlopt) < 0) goto cleanup; } @@ -9159,6 +9185,8 @@ qemuDomainDetachDeviceAliasLiveAndConfig(virQEMUDriverPtr driver, ret = 0; cleanup: virDomainDefFree(vmdef); + virDomainDeviceDefListFree(devlist); + return ret; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index a6c520ec1b..9d6671f4f6 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6086,6 +6086,122 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, } +int +qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, + virDomainDeviceDefListPtr devlist, + virQEMUDriverPtr driver) +{ + size_t i; + int ret = -1; + g_autoptr(virBitmap) onlinemap = NULL; + virDomainHostdevDefPtr hostdev, detach = NULL; + virDomainHostdevSubsysPtr subsys = NULL; + int slotaggridx = 0; + virDomainHostdevSubsysPCIPtr pcisrc = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + + qsort(devlist->devs, devlist->count, sizeof(*devlist->devs), + qemuiHostdevPCIMultifunctionDevicesListSort); + +#define FOR_EACH_DEV_IN_DEVLIST() \ + for (i = 0; i < devlist->count; i++) { \ + hostdev = devlist->devs[i]->data.hostdev; \ + subsys = &hostdev->source.subsys; \ + pcisrc = &subsys->u.pci; \ + virDomainHostdevFind(vm->def, hostdev, &detach); + + FOR_EACH_DEV_IN_DEVLIST() + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hot unplug is not supported for hostdev mode '%s'"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + } + + FOR_EACH_DEV_IN_DEVLIST() + if (!detach) { + virReportError(VIR_ERR_DEVICE_MISSING, + _("host pci device %.4x:%.2x:%.2x.%.1x not found"), + pcisrc->addr.domain, pcisrc->addr.bus, + pcisrc->addr.slot, pcisrc->addr.function); + return -1; + } + } + + /* Check if the devices belong to same guest slot.*/ + FOR_EACH_DEV_IN_DEVLIST() + /* Pick one aggregateSlotIdx and compare against rest of them */ + slotaggridx = slotaggridx ? slotaggridx : detach->info->aggregateSlotIdx; + if (slotaggridx != detach->info->aggregateSlotIdx) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("host pci device %.4x:%.2x:%.2x.%.1x does " + "not belong to same slot"), + pcisrc->addr.domain, + pcisrc->addr.bus, + pcisrc->addr.slot, + pcisrc->addr.function); + return -1; + } + } + + /* Check if the whole slot is being removed or not */ + onlinemap = virDomainDefHostdevGetPCIOnlineFunctionMap(vm->def, slotaggridx); + FOR_EACH_DEV_IN_DEVLIST() + ignore_value(virBitmapClearBit(onlinemap, pcisrc->addr.function)); + } + + if (!virBitmapIsAllClear(onlinemap)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("hot unplug of partial PCI slot not allowed")); + return -1; + } + + /* Mark all aliases for removal */ + memset(&priv->unplug, 0, sizeof(priv->unplug)); + FOR_EACH_DEV_IN_DEVLIST() + if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) + goto reset; + + qemuDomainMarkDeviceAliasForRemoval(vm, detach->info->alias, false); + } + + qemuDomainObjEnterMonitor(driver, vm); + + /* must plug non-zero first, zero at last */ + for (i = devlist->count; i > 0; i--) { + hostdev = devlist->devs[i -1]->data.hostdev; + subsys = &hostdev->source.subsys; + pcisrc = &subsys->u.pci; + virDomainHostdevFind(vm->def, hostdev, &detach); + + if (qemuMonitorDelDevice(priv->mon, detach->info->alias) < 0) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + if (virDomainObjIsActive(vm)) + virDomainAuditHostdev(vm, detach, "detach", false); + goto reset; + } + if (ARCH_IS_X86(vm->def->os.arch)) + break; /* deleting any one is enough! */ + } + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) { + FOR_EACH_DEV_IN_DEVLIST() + ret = qemuDomainRemoveHostDevice(driver, vm, detach); + } + } + reset: + qemuDomainResetDeviceRemoval(vm); + +#undef FOR_EACH_DEV_IN_DEVLIST + + return ret; +} + + static int qemuDomainRemoveVcpu(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index b62528a6d9..6e74473936 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -131,6 +131,11 @@ qemuDomainAttachMultifunctionDevice(virDomainObjPtr vm, virDomainDeviceDefListPtr devlist, virQEMUDriverPtr driver); +int +qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, + virDomainDeviceDefListPtr devlist, + virQEMUDriverPtr driver); + int qemuDomainChrInsert(virDomainDefPtr vmdef, virDomainChrDefPtr chr); diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 956e3daf7d..b913ce2c5b 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -802,7 +802,7 @@ static int qemuValidateDomainDefPCIMultifunctionHostdev(qemuDomainPCIHostdevData } -static int qemuValidateDomainDefPCIHostdevs(const virDomainDef *def) +int qemuValidateDomainDefPCIHostdevs(const virDomainDef *def) { qemuDomainPCIHostdevdata hostdevdata = {def, NULL, NULL}; diff --git a/src/qemu/qemu_validate.h b/src/qemu/qemu_validate.h index acf7d26ce0..c6782066eb 100644 --- a/src/qemu/qemu_validate.h +++ b/src/qemu/qemu_validate.h @@ -33,3 +33,4 @@ int qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk, int qemuValidateDomainDeviceDef(const virDomainDeviceDef *dev, const virDomainDef *def, void *opaque); +int qemuValidateDomainDefPCIHostdevs(const virDomainDef *def); diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 32d186ef73..175f3ffed8 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -158,10 +158,15 @@ testQemuHotplugAttach(virDomainObjPtr vm, static int testQemuHotplugDetach(virDomainObjPtr vm, - virDomainDeviceDefPtr dev, + virDomainDeviceDefListPtr devlist, bool async) { int ret = -1; + virDomainDeviceDefPtr dev; + + if (devlist->count > 1) + return qemuDomainDetachMultifunctionDevice(vm, devlist, &driver); + dev = devlist->devs[0]; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -251,7 +256,6 @@ testQemuHotplug(const void *data) bool keep = test->keep; unsigned int device_parse_flags = 0; virDomainObjPtr vm = NULL; - virDomainDeviceDefPtr dev = NULL; /* temporary */ virDomainDeviceDefListPtr devlist = NULL; virDomainDeviceDefListData listdata; virCapsPtr caps = NULL; @@ -298,8 +302,6 @@ testQemuHotplug(const void *data) if (!devlist) goto cleanup; - dev = devlist->devs[0]; /* temporary */ - /* Now is the best time to feed the spoofed monitor with predefined * replies. */ if (!(test_mon = qemuMonitorTestNew(driver.xmlopt, vm, &driver, @@ -340,14 +342,14 @@ testQemuHotplug(const void *data) break; case DETACH: - ret = testQemuHotplugDetach(vm, dev, false); + ret = testQemuHotplugDetach(vm, devlist, false); if (ret == 0 || fail) ret = testQemuHotplugCheckResult(vm, domain_xml, domain_filename, fail); break; case UPDATE: - ret = testQemuHotplugUpdate(vm, dev); + ret = testQemuHotplugUpdate(vm, devlist->devs[0]); } virObjectLock(priv->mon); @@ -852,16 +854,25 @@ mymain(void) "device_add", QMP_OK); DO_TEST_DETACH("pseries-base-live", "hostdev-pci", false, false, "device_del", QMP_DEVICE_DELETED("hostdev0") QMP_OK); - DO_TEST_ATTACH("base-live", "multifunction-hostdev-pci", false, false, + DO_TEST_ATTACH("base-live", "multifunction-hostdev-pci", false, true, "device_add", QMP_OK, "device_add", QMP_OK, "device_add", QMP_OK, "device_add", QMP_OK); + DO_TEST_DETACH("base-live", "multifunction-hostdev-pci", false, false, + "device_del", QMP_DEVICE_DELETED("hostdev3") + QMP_DEVICE_DELETED("hostdev2") + QMP_DEVICE_DELETED("hostdev1") + QMP_DEVICE_DELETED("hostdev0") QMP_OK); qemuTestSetHostArch(&driver, VIR_ARCH_PPC64); - DO_TEST_ATTACH("pseries-base-live", "multifunction-hostdev-pci-2", false, false, + DO_TEST_ATTACH("pseries-base-live", "multifunction-hostdev-pci-2", false, true, "device_add", QMP_OK, "device_add", QMP_OK); + DO_TEST_DETACH("pseries-base-live", "multifunction-hostdev-pci-2", false, false, + "device_del", QMP_OK, + "device_del", QMP_DEVICE_DELETED("hostdev1") + QMP_DEVICE_DELETED("hostdev0") QMP_OK); qemuTestSetHostArch(&driver, VIR_ARCH_X86_64); DO_TEST_ATTACH("base-live", "watchdog", false, true, -- 2.26.2

In a multifunction hot-unplug, QEMU will unplug all the functions in a single detach operation, sequentially, triggered by the detach of function zero for Pseries guests or any function for x86 guests. This impacts the amount of timeout we're supposed to wait - an unplug operation with 4 hostdevs will naturally take longer than a single hostdev unplug. The previous existing timeout wasn't enough to handle this multifunction detach case, timing out the detach operation needlessly. This patch handles it by adding a new qemuDomainWaitForMultipleDeviceRemoval function that considers the number of naliases to be unplugged when calculating unplugTimeout. Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_hotplug.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 9d6671f4f6..23c7d57682 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5276,15 +5276,21 @@ qemuDomainGetUnplugTimeout(virDomainObjPtr vm) * - we failed to reliably wait for the event and thus use fallback behavior */ static int -qemuDomainWaitForDeviceRemoval(virDomainObjPtr vm) +qemuDomainWaitForMultipleDeviceRemoval(virDomainObjPtr vm, + bool isMultiFunctionDevice) { qemuDomainObjPrivatePtr priv = vm->privateData; - unsigned long long until; + unsigned long long until, waitTime; int rc; if (virTimeMillisNow(&until) < 0) return 1; - until += qemuDomainGetUnplugTimeout(vm); + + waitTime = qemuDomainGetUnplugTimeout(vm); + if (isMultiFunctionDevice) + waitTime *= priv->unplug.naliases; + + until += waitTime; /* All devices should get released around same time*/ while (priv->unplug.naliases) { @@ -5307,6 +5313,17 @@ qemuDomainWaitForDeviceRemoval(virDomainObjPtr vm) return 1; } +/* For a single device, call qemuDomainWaitForMultipleDeviceRemoval with + * isMultiFunctionDevice = false. + * + * Returns: same values as qemuDomainWaitForMultipleDeviceRemoval. + */ +static int +qemuDomainWaitForDeviceRemoval(virDomainObjPtr vm) +{ + return qemuDomainWaitForMultipleDeviceRemoval(vm, false); +} + /* Returns: * true there was a thread waiting for devAlias to be removed and this * thread will take care of finishing the removal @@ -6188,7 +6205,7 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, if (qemuDomainObjExitMonitor(driver, vm) < 0) ret = -1; - if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) { + if ((ret = qemuDomainWaitForMultipleDeviceRemoval(vm, true)) == 1) { FOR_EACH_DEV_IN_DEVLIST() ret = qemuDomainRemoveHostDevice(driver, vm, detach); } -- 2.26.2

The new address type 'unassigned' allows for a hostdev to be managed by Libvirt, but not be assigned for guest usage. This can allow us to execute PCI multifunction hotplug, with managed mode, leaving non-zero functions unassigned. For hotplug and unplug, we'll skip unassigned devices when setting or evaluating aggregateSlotIdx and when we're about to execute device_add/device_del in QEMU. For hotplug we'll also skip adress assignment. For unplug we won't mark the device for removal since we won't be executing device_del on it. Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_domain.c | 1 + src/qemu/qemu_domain_address.c | 6 ++ src/qemu/qemu_hotplug.c | 16 ++++ tests/qemuhotplugtest.c | 9 ++ ...plug-multifunction-hostdev-pci-partial.xml | 27 ++++++ ...live+multifunction-hostdev-pci-partial.xml | 82 +++++++++++++++++++ 6 files changed, 141 insertions(+) create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-partial.xml create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci-partial.xml diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0471a37803..7bff50ca09 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -13412,6 +13412,7 @@ int qemuDomainValidateMultifunctionDeviceList(virDomainDeviceDefListPtr devlist) virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(devlist->devs[i]); if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED && info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { return -1; } diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 94d91a5daf..ebae69cc6d 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -3473,6 +3473,9 @@ qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(virDomainObjPtr vm, for (i = 0; i < devlist->count; i++) { virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev; + if (hostdev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) + continue; + if (qemuDomainIsPSeries(vm->def)) /* Isolation groups are only relevant for pSeries guests */ qemuDomainFillDeviceIsolationGroup(vm->def, devlist->devs[i]); @@ -3504,6 +3507,9 @@ qemuDomainPCIMultifunctionHostdevEnsurePCIAddresses(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev = devlist->devs[i]->data.hostdev; virPCIDeviceAddress addr = hostdev->source.subsys.u.pci.addr; + if (hostdev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) + continue; + devinfos->infos[addr.function] = hostdev->info; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 23c7d57682..64dd69b923 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1646,6 +1646,13 @@ qemuDomainAttachHostPCIDevice(virQEMUDriverPtr driver, goto error; } + /* For an unassigned hostdev, add it to the domain definition + * and return without hotplugging it to QEMU. */ + if (hostdev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) { + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + return 0; + } + if (!(devstr = qemuBuildPCIHostdevDevStr(vm->def, hostdev, 0, priv->qemuCaps))) goto error; @@ -6148,6 +6155,9 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, /* Check if the devices belong to same guest slot.*/ FOR_EACH_DEV_IN_DEVLIST() + if (detach->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) + continue; + /* Pick one aggregateSlotIdx and compare against rest of them */ slotaggridx = slotaggridx ? slotaggridx : detach->info->aggregateSlotIdx; if (slotaggridx != detach->info->aggregateSlotIdx) { @@ -6180,6 +6190,9 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, if (qemuAssignDeviceHostdevAlias(vm->def, &detach->info->alias, -1) < 0) goto reset; + if (detach->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) + continue; + qemuDomainMarkDeviceAliasForRemoval(vm, detach->info->alias, false); } @@ -6192,6 +6205,9 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, pcisrc = &subsys->u.pci; virDomainHostdevFind(vm->def, hostdev, &detach); + if (detach->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) + continue; + if (qemuMonitorDelDevice(priv->mon, detach->info->alias) < 0) { ignore_value(qemuDomainObjExitMonitor(driver, vm)); if (virDomainObjIsActive(vm)) diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 175f3ffed8..5e9d770cb1 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -865,6 +865,15 @@ mymain(void) QMP_DEVICE_DELETED("hostdev1") QMP_DEVICE_DELETED("hostdev0") QMP_OK); + DO_TEST_ATTACH("base-live", "multifunction-hostdev-pci-partial", false, true, + "device_add", QMP_OK, + "device_add", QMP_OK, + "device_add", QMP_OK); + DO_TEST_DETACH("base-live", "multifunction-hostdev-pci-partial", false, false, + "device_del", QMP_DEVICE_DELETED("hostdev3") + QMP_DEVICE_DELETED("hostdev2") + QMP_DEVICE_DELETED("hostdev0") QMP_OK); + qemuTestSetHostArch(&driver, VIR_ARCH_PPC64); DO_TEST_ATTACH("pseries-base-live", "multifunction-hostdev-pci-2", false, true, "device_add", QMP_OK, diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-partial.xml b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-partial.xml new file mode 100644 index 0000000000..5e01564d2b --- /dev/null +++ b/tests/qemuhotplugtestdevices/qemuhotplug-multifunction-hostdev-pci-partial.xml @@ -0,0 +1,27 @@ +<devices> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x1'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + </source> + <address type='unassigned'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> + </source> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> + </source> + </hostdev> +</devices> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci-partial.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci-partial.xml new file mode 100644 index 0000000000..1808a0071e --- /dev/null +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+multifunction-hostdev-pci-partial.xml @@ -0,0 +1,82 @@ +<domain type='kvm' id='7'> + <name>hotplug</name> + <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid> + <memory unit='KiB'>4194304</memory> + <currentMemory unit='KiB'>4194304</currentMemory> + <vcpu placement='static'>4</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <controller type='usb' index='0'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <alias name='ide'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='scsi' index='0' model='virtio-scsi'> + <alias name='scsi0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci'/> + </controller> + <controller type='virtio-serial' index='0'> + <alias name='virtio-serial0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </controller> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x3'/> + </source> + <alias name='hostdev0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x3'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x2'/> + </source> + <alias name='hostdev1'/> + <address type='unassigned'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x1'/> + </source> + <alias name='hostdev2'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x1'/> + </hostdev> + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='vfio'/> + <source> + <address domain='0x0005' bus='0x90' slot='0x01' function='0x0'/> + </source> + <alias name='hostdev3'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0' multifunction='on'/> + </hostdev> + <memballoon model='none'/> + </devices> + <seclabel type='none' model='none'/> +</domain> -- 2.26.2

QEMU 4.2.0 introduced an enhanced version of the PCI multifunction hotunplug for the PSeries guest [1] where a single device_del of the function 0 will detach all the functions of the slot. The idea is to make this option similar to the behavior we already have on x86. This means that the unplug code in qemuDomainDetachMultifunctionDevice() can be simplified if the domain is running with QEMU 4.2.0 or newer. [1] https://github.com/qemu/qemu/commit/02a1536eee333123c7735cd36484da53b860fbb7 Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> --- src/qemu/qemu_hotplug.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 64dd69b923..806dc98556 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -6123,6 +6123,7 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, int slotaggridx = 0; virDomainHostdevSubsysPCIPtr pcisrc = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; + int pSeriesEnhancedUnplugVersion; qsort(devlist->devs, devlist->count, sizeof(*devlist->devs), qemuiHostdevPCIMultifunctionDevicesListSort); @@ -6198,15 +6199,16 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, qemuDomainObjEnterMonitor(driver, vm); - /* must plug non-zero first, zero at last */ - for (i = devlist->count; i > 0; i--) { - hostdev = devlist->devs[i -1]->data.hostdev; - subsys = &hostdev->source.subsys; - pcisrc = &subsys->u.pci; - virDomainHostdevFind(vm->def, hostdev, &detach); + /* QEMU 4.2.0 introduced a new Pseries hotunplug mechanic, where + * the whole slot can be unplugged by hotunplugging function zero + * (see QEMU commit 02a1536eee for details). This makes it similar + * to what x86 does, as long as we hotunplug function zero first + * in all cases. */ + pSeriesEnhancedUnplugVersion = 4 * 1000000 + 2 * 1000; - if (detach->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) - continue; + if (virQEMUCapsGetVersion(priv->qemuCaps) >= pSeriesEnhancedUnplugVersion) { + hostdev = devlist->devs[0]->data.hostdev; + virDomainHostdevFind(vm->def, hostdev, &detach); if (qemuMonitorDelDevice(priv->mon, detach->info->alias) < 0) { ignore_value(qemuDomainObjExitMonitor(driver, vm)); @@ -6214,8 +6216,25 @@ qemuDomainDetachMultifunctionDevice(virDomainObjPtr vm, virDomainAuditHostdev(vm, detach, "detach", false); goto reset; } - if (ARCH_IS_X86(vm->def->os.arch)) - break; /* deleting any one is enough! */ + } else { + /* We're running in an older QEMU, so we must plug non-zero first, + * zero at last. */ + for (i = devlist->count; i > 0; i--) { + hostdev = devlist->devs[i -1]->data.hostdev; + virDomainHostdevFind(vm->def, hostdev, &detach); + + if (detach->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED) + continue; + + if (qemuMonitorDelDevice(priv->mon, detach->info->alias) < 0) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + if (virDomainObjIsActive(vm)) + virDomainAuditHostdev(vm, detach, "detach", false); + goto reset; + } + if (ARCH_IS_X86(vm->def->os.arch)) + break; /* deleting any one is enough! */ + } } if (qemuDomainObjExitMonitor(driver, vm) < 0) -- 2.26.2
participants (1)
-
Daniel Henrique Barboza