528 lines
17 KiB
Diff
528 lines
17 KiB
Diff
From 87e3a5f2f797c79516a560ddc224074c834ef528 Mon Sep 17 00:00:00 2001
|
|
Message-Id: <87e3a5f2f797c79516a560ddc224074c834ef528@dist-git>
|
|
From: Yi Min Zhao <zyimin@linux.ibm.com>
|
|
Date: Mon, 8 Apr 2019 10:57:27 +0200
|
|
Subject: [PATCH] conf: Allocate/release 'uid' and 'fid' in PCI address
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
This patch adds new functions for reservation, assignment and release
|
|
to handle the uid/fid. If the uid/fid is defined in the domain XML,
|
|
they will be reserved directly in the collecting phase. If any of them
|
|
is not defined, we will find out an available value for them from the
|
|
zPCI address hashtable, and reserve them. For the hotplug case there
|
|
might not be a zPCI definition. So allocate and reserve uid/fid the
|
|
case. Assign if needed and reserve uid/fid for the defined case.
|
|
|
|
Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com>
|
|
Reviewed-by: Bjoern Walk <bwalk@linux.ibm.com>
|
|
Reviewed-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
|
|
Reviewed-by: Andrea Bolognani <abologna@redhat.com>
|
|
|
|
(cherry picked from commit f183b87fc1dbcc6446ac3c1cef9cdd345b9725fb)
|
|
|
|
https://bugzilla.redhat.com/show_bug.cgi?id=1508149
|
|
|
|
Conflicts:
|
|
|
|
* src/libvirt_private.syms
|
|
+ several symbols are not present in the list
|
|
- missing 9ad119f4db5, ab3f781a10c, edeef779585, b899726faa5
|
|
|
|
* src/qemu/qemu_domain_address.c
|
|
+ the old name for virDeviceInfoPCIAddressIsPresent() is used
|
|
- missing 76151a53a100
|
|
|
|
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
|
|
Message-Id: <20190408085732.28684-11-abologna@redhat.com>
|
|
Reviewed-by: Laine Stump <laine@redhat.com>
|
|
Reviewed-by: Ján Tomko <jtomko@redhat.com>
|
|
---
|
|
src/conf/device_conf.c | 16 +++
|
|
src/conf/device_conf.h | 3 +
|
|
src/conf/domain_addr.c | 244 +++++++++++++++++++++++++++++++++
|
|
src/conf/domain_addr.h | 12 ++
|
|
src/libvirt_private.syms | 5 +
|
|
src/qemu/qemu_domain_address.c | 59 +++++++-
|
|
6 files changed, 338 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/src/conf/device_conf.c b/src/conf/device_conf.c
|
|
index cadac32603..76370d30a2 100644
|
|
--- a/src/conf/device_conf.c
|
|
+++ b/src/conf/device_conf.c
|
|
@@ -28,6 +28,7 @@
|
|
#include "viruuid.h"
|
|
#include "virbuffer.h"
|
|
#include "device_conf.h"
|
|
+#include "domain_addr.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_DEVICE
|
|
@@ -230,6 +231,21 @@ int virPCIDeviceAddressIsValid(virPCIDeviceAddressPtr addr,
|
|
}
|
|
|
|
|
|
+bool
|
|
+virDeviceInfoPCIAddressExtensionIsWanted(const virDomainDeviceInfo *info)
|
|
+{
|
|
+ return (info->addr.pci.extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) &&
|
|
+ virZPCIDeviceAddressIsEmpty(&info->addr.pci.zpci);
|
|
+}
|
|
+
|
|
+bool
|
|
+virDeviceInfoPCIAddressExtensionIsPresent(const virDomainDeviceInfo *info)
|
|
+{
|
|
+ return (info->addr.pci.extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) &&
|
|
+ !virZPCIDeviceAddressIsEmpty(&info->addr.pci.zpci);
|
|
+}
|
|
+
|
|
+
|
|
int
|
|
virPCIDeviceAddressParseXML(xmlNodePtr node,
|
|
virPCIDeviceAddressPtr addr)
|
|
diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h
|
|
index c79066ec02..6bef2f093a 100644
|
|
--- a/src/conf/device_conf.h
|
|
+++ b/src/conf/device_conf.h
|
|
@@ -214,6 +214,9 @@ virDeviceInfoPCIAddressPresent(const virDomainDeviceInfo *info)
|
|
!virPCIDeviceAddressIsEmpty(&info->addr.pci);
|
|
}
|
|
|
|
+bool virDeviceInfoPCIAddressExtensionIsWanted(const virDomainDeviceInfo *info);
|
|
+bool virDeviceInfoPCIAddressExtensionIsPresent(const virDomainDeviceInfo *info);
|
|
+
|
|
int virPCIDeviceAddressParseXML(xmlNodePtr node,
|
|
virPCIDeviceAddressPtr addr);
|
|
|
|
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
|
|
index 9e0a0fdf95..a58910c394 100644
|
|
--- a/src/conf/domain_addr.c
|
|
+++ b/src/conf/domain_addr.c
|
|
@@ -33,6 +33,238 @@
|
|
|
|
VIR_LOG_INIT("conf.domain_addr");
|
|
|
|
+static int
|
|
+virDomainZPCIAddressReserveId(virHashTablePtr set,
|
|
+ unsigned int id,
|
|
+ const char *name)
|
|
+{
|
|
+ if (virHashLookup(set, &id)) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("zPCI %s %o is already reserved"),
|
|
+ name, id);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (virHashAddEntry(set, &id, (void*)1) < 0) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Failed to reserve %s %o"),
|
|
+ name, id);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressReserveUid(virHashTablePtr set,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ return virDomainZPCIAddressReserveId(set, addr->uid, "uid");
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressReserveFid(virHashTablePtr set,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ return virDomainZPCIAddressReserveId(set, addr->fid, "fid");
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressAssignId(virHashTablePtr set,
|
|
+ unsigned int *id,
|
|
+ unsigned int min,
|
|
+ unsigned int max,
|
|
+ const char *name)
|
|
+{
|
|
+ while (virHashLookup(set, &min)) {
|
|
+ if (min == max) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("There is no more free %s."),
|
|
+ name);
|
|
+ return -1;
|
|
+ }
|
|
+ ++min;
|
|
+ }
|
|
+ *id = min;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressAssignUid(virHashTablePtr set,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ return virDomainZPCIAddressAssignId(set, &addr->uid, 1,
|
|
+ VIR_DOMAIN_DEVICE_ZPCI_MAX_UID, "uid");
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressAssignFid(virHashTablePtr set,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ return virDomainZPCIAddressAssignId(set, &addr->fid, 0,
|
|
+ VIR_DOMAIN_DEVICE_ZPCI_MAX_FID, "fid");
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+virDomainZPCIAddressReleaseId(virHashTablePtr set,
|
|
+ unsigned int *id,
|
|
+ const char *name)
|
|
+{
|
|
+ if (virHashRemoveEntry(set, id) < 0) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Release %s %o failed"),
|
|
+ name, *id);
|
|
+ }
|
|
+
|
|
+ *id = 0;
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+virDomainZPCIAddressReleaseUid(virHashTablePtr set,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ virDomainZPCIAddressReleaseId(set, &addr->uid, "uid");
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+virDomainZPCIAddressReleaseFid(virHashTablePtr set,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ virDomainZPCIAddressReleaseId(set, &addr->fid, "fid");
|
|
+}
|
|
+
|
|
+
|
|
+static void
|
|
+virDomainZPCIAddressReleaseIds(virDomainZPCIAddressIdsPtr zpciIds,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (!zpciIds || virZPCIDeviceAddressIsEmpty(addr))
|
|
+ return;
|
|
+
|
|
+ virDomainZPCIAddressReleaseUid(zpciIds->uids, addr);
|
|
+
|
|
+ virDomainZPCIAddressReleaseFid(zpciIds->fids, addr);
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressReserveNextUid(virHashTablePtr uids,
|
|
+ virZPCIDeviceAddressPtr zpci)
|
|
+{
|
|
+ if (virDomainZPCIAddressAssignUid(uids, zpci) < 0)
|
|
+ return -1;
|
|
+
|
|
+ if (virDomainZPCIAddressReserveUid(uids, zpci) < 0)
|
|
+ return -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressReserveNextFid(virHashTablePtr fids,
|
|
+ virZPCIDeviceAddressPtr zpci)
|
|
+{
|
|
+ if (virDomainZPCIAddressAssignFid(fids, zpci) < 0)
|
|
+ return -1;
|
|
+
|
|
+ if (virDomainZPCIAddressReserveFid(fids, zpci) < 0)
|
|
+ return -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressReserveAddr(virDomainZPCIAddressIdsPtr zpciIds,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (virDomainZPCIAddressReserveUid(zpciIds->uids, addr) < 0)
|
|
+ return -1;
|
|
+
|
|
+ if (virDomainZPCIAddressReserveFid(zpciIds->fids, addr) < 0) {
|
|
+ virDomainZPCIAddressReleaseUid(zpciIds->uids, addr);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virDomainZPCIAddressReserveNextAddr(virDomainZPCIAddressIdsPtr zpciIds,
|
|
+ virZPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (virDomainZPCIAddressReserveNextUid(zpciIds->uids, addr) < 0)
|
|
+ return -1;
|
|
+
|
|
+ if (virDomainZPCIAddressReserveNextFid(zpciIds->fids, addr) < 0) {
|
|
+ virDomainZPCIAddressReleaseUid(zpciIds->uids, addr);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+virDomainPCIAddressExtensionReserveAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (addr->extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) {
|
|
+ /* Reserve uid/fid to ZPCI device which has defined uid/fid
|
|
+ * in the domain.
|
|
+ */
|
|
+ return virDomainZPCIAddressReserveAddr(addrs->zpciIds, &addr->zpci);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+int
|
|
+virDomainPCIAddressExtensionReserveNextAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (addr->extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) {
|
|
+ virZPCIDeviceAddress zpci = { 0 };
|
|
+
|
|
+ if (virDomainZPCIAddressReserveNextAddr(addrs->zpciIds, &zpci) < 0)
|
|
+ return -1;
|
|
+
|
|
+ if (!addrs->dryRun)
|
|
+ addr->zpci = zpci;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+virDomainPCIAddressExtensionEnsureAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (addr->extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) {
|
|
+ virZPCIDeviceAddressPtr zpci = &addr->zpci;
|
|
+
|
|
+ if (virZPCIDeviceAddressIsEmpty(zpci))
|
|
+ return virDomainZPCIAddressReserveNextAddr(addrs->zpciIds, zpci);
|
|
+ else
|
|
+ return virDomainZPCIAddressReserveAddr(addrs->zpciIds, zpci);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
virDomainPCIConnectFlags
|
|
virDomainPCIControllerModelToConnectType(virDomainControllerModelPCI model)
|
|
{
|
|
@@ -729,12 +961,24 @@ virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs,
|
|
ret = virDomainPCIAddressReserveNextAddr(addrs, dev, flags, -1);
|
|
}
|
|
|
|
+ dev->addr.pci.extFlags = dev->pciAddrExtFlags;
|
|
+ ret = virDomainPCIAddressExtensionEnsureAddr(addrs, &dev->addr.pci);
|
|
+
|
|
cleanup:
|
|
VIR_FREE(addrStr);
|
|
return ret;
|
|
}
|
|
|
|
|
|
+void
|
|
+virDomainPCIAddressExtensionReleaseAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+{
|
|
+ if (addr->extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI)
|
|
+ virDomainZPCIAddressReleaseIds(addrs->zpciIds, &addr->zpci);
|
|
+}
|
|
+
|
|
+
|
|
void
|
|
virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
|
|
virPCIDeviceAddressPtr addr)
|
|
diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h
|
|
index b01e6b9d20..e5ce4868d5 100644
|
|
--- a/src/conf/domain_addr.h
|
|
+++ b/src/conf/domain_addr.h
|
|
@@ -166,6 +166,14 @@ bool virDomainPCIAddressSlotInUse(virDomainPCIAddressSetPtr addrs,
|
|
virPCIDeviceAddressPtr addr)
|
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
|
|
|
+int virDomainPCIAddressExtensionReserveAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
|
+
|
|
+int virDomainPCIAddressExtensionReserveNextAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
|
+
|
|
int virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs,
|
|
virPCIDeviceAddressPtr addr,
|
|
virDomainPCIConnectFlags flags,
|
|
@@ -187,6 +195,10 @@ void virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
|
|
virPCIDeviceAddressPtr addr)
|
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
|
|
|
+void virDomainPCIAddressExtensionReleaseAddr(virDomainPCIAddressSetPtr addrs,
|
|
+ virPCIDeviceAddressPtr addr)
|
|
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
|
+
|
|
void virDomainPCIAddressSetAllMulti(virDomainDefPtr def)
|
|
ATTRIBUTE_NONNULL(1);
|
|
|
|
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
|
|
index b2a2a1f265..ee7625b0f3 100644
|
|
--- a/src/libvirt_private.syms
|
|
+++ b/src/libvirt_private.syms
|
|
@@ -93,6 +93,8 @@ virCPUModeTypeToString;
|
|
|
|
|
|
# conf/device_conf.h
|
|
+virDeviceInfoPCIAddressExtensionIsPresent;
|
|
+virDeviceInfoPCIAddressExtensionIsWanted;
|
|
virDomainDeviceInfoAddressIsEqual;
|
|
virDomainDeviceInfoCopy;
|
|
virInterfaceLinkFormat;
|
|
@@ -114,6 +116,9 @@ virDomainPCIAddressAsString;
|
|
virDomainPCIAddressBusIsFullyReserved;
|
|
virDomainPCIAddressBusSetModel;
|
|
virDomainPCIAddressEnsureAddr;
|
|
+virDomainPCIAddressExtensionReleaseAddr;
|
|
+virDomainPCIAddressExtensionReserveAddr;
|
|
+virDomainPCIAddressExtensionReserveNextAddr;
|
|
virDomainPCIAddressReleaseAddr;
|
|
virDomainPCIAddressReserveAddr;
|
|
virDomainPCIAddressReserveNextAddr;
|
|
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
|
|
index ba870d56b1..8338241cba 100644
|
|
--- a/src/qemu/qemu_domain_address.c
|
|
+++ b/src/qemu/qemu_domain_address.c
|
|
@@ -1405,6 +1405,24 @@ qemuDomainPCIAddressReserveNextAddr(virDomainPCIAddressSetPtr addrs,
|
|
}
|
|
|
|
|
|
+static int
|
|
+qemuDomainAssignPCIAddressExtension(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
|
+ virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
|
|
+ virDomainDeviceInfoPtr info,
|
|
+ void *opaque)
|
|
+{
|
|
+ virDomainPCIAddressSetPtr addrs = opaque;
|
|
+ virPCIDeviceAddressPtr addr = &info->addr.pci;
|
|
+
|
|
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
|
|
+ addr->extFlags = info->pciAddrExtFlags;
|
|
+
|
|
+ if (virDeviceInfoPCIAddressExtensionIsWanted(info))
|
|
+ return virDomainPCIAddressExtensionReserveNextAddr(addrs, addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
qemuDomainCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
|
virDomainDeviceDefPtr device,
|
|
@@ -1498,6 +1516,31 @@ qemuDomainCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
|
return ret;
|
|
}
|
|
|
|
+static int
|
|
+qemuDomainCollectPCIAddressExtension(virDomainDefPtr def ATTRIBUTE_UNUSED,
|
|
+ virDomainDeviceDefPtr device,
|
|
+ virDomainDeviceInfoPtr info,
|
|
+ void *opaque)
|
|
+{
|
|
+ virDomainPCIAddressSetPtr addrs = opaque;
|
|
+ virPCIDeviceAddressPtr addr = &info->addr.pci;
|
|
+
|
|
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
|
|
+ addr->extFlags = info->pciAddrExtFlags;
|
|
+
|
|
+ if (!virDeviceInfoPCIAddressExtensionIsPresent(info) ||
|
|
+ ((device->type == VIR_DOMAIN_DEVICE_HOSTDEV) &&
|
|
+ (device->data.hostdev->parent.type != VIR_DOMAIN_DEVICE_NONE))) {
|
|
+ /* If a hostdev has a parent, its info will be a part of the
|
|
+ * parent, and will have its address collected during the scan
|
|
+ * of the parent's device type.
|
|
+ */
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return virDomainPCIAddressExtensionReserveAddr(addrs, addr);
|
|
+}
|
|
+
|
|
static virDomainPCIAddressSetPtr
|
|
qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
|
|
virQEMUCapsPtr qemuCaps,
|
|
@@ -1589,6 +1632,12 @@ qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
|
|
if (virDomainDeviceInfoIterate(def, qemuDomainCollectPCIAddress, addrs) < 0)
|
|
goto error;
|
|
|
|
+ if (virDomainDeviceInfoIterate(def,
|
|
+ qemuDomainCollectPCIAddressExtension,
|
|
+ addrs) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
return addrs;
|
|
|
|
error:
|
|
@@ -2590,6 +2639,9 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
|
|
if (qemuDomainAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
|
|
goto cleanup;
|
|
|
|
+ if (virDomainDeviceInfoIterate(def, qemuDomainAssignPCIAddressExtension, addrs) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
/* Only for *new* domains with pcie-root (and no other
|
|
* manually specified PCI controllers in the definition): If,
|
|
* after assigning addresses/reserving slots for all devices,
|
|
@@ -2684,6 +2736,9 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
|
|
if (qemuDomainAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
|
|
goto cleanup;
|
|
|
|
+ if (virDomainDeviceInfoIterate(def, qemuDomainAssignPCIAddressExtension, addrs) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
/* set multi attribute for devices at function 0 of
|
|
* any slot that has multiple functions in use
|
|
*/
|
|
@@ -3143,8 +3198,10 @@ qemuDomainReleaseDeviceAddress(virDomainObjPtr vm,
|
|
if (!devstr)
|
|
devstr = info->alias;
|
|
|
|
- if (virDeviceInfoPCIAddressPresent(info))
|
|
+ if (virDeviceInfoPCIAddressPresent(info)) {
|
|
virDomainPCIAddressReleaseAddr(priv->pciaddrs, &info->addr.pci);
|
|
+ virDomainPCIAddressExtensionReleaseAddr(priv->pciaddrs, &info->addr.pci);
|
|
+ }
|
|
|
|
if (virDomainUSBAddressRelease(priv->usbaddrs, info) < 0)
|
|
VIR_WARN("Unable to release USB address on %s", NULLSTR(devstr));
|
|
--
|
|
2.22.0
|
|
|