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 collecting phase. If any of them is
not defined, we will find out an available value for it from zPCI
address hashtable, and reserve it. For hotplug case, there might be or
not zPCI definition. So allocate and reserve uid/fid for undefined
case. Assign if needed and reserve uid/fid for defined case. If the user
define zPCI extension address but zPCI capability doesn't exist, an
error will be reported.
Signed-off-by: Yi Min Zhao <zyimin(a)linux.ibm.com>
Reviewed-by: Boris Fiuczynski <fiuczy(a)linux.vnet.ibm.com>
---
src/conf/domain_addr.c | 264 +++++++++++++++++++++++++++++++++++++++++
src/conf/domain_addr.h | 15 +++
src/libvirt_private.syms | 3 +
src/qemu/qemu_domain_address.c | 41 ++++++-
4 files changed, 321 insertions(+), 2 deletions(-)
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index 66e61f3550..f5742f4430 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -33,6 +33,147 @@
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 %u is already reserved"), name, id);
+ return -1;
+ }
+
+ if (virHashAddEntry(set, &id, (void*)1) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to reserve %s %u"), name, id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+virDomainZPCIAddressReserveUid(virHashTablePtr set, virZPCIDeviceAddressPtr addr)
+{
+ return virDomainZPCIAddressReserveId(set, addr->zpci_uid, "uid");
+}
+
+static int
+virDomainZPCIAddressReserveFid(virHashTablePtr set, virZPCIDeviceAddressPtr addr)
+{
+ return virDomainZPCIAddressReserveId(set, addr->zpci_fid, "fid");
+}
+
+
+static bool
+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 false;
+ }
+ ++min;
+ }
+ *id = min;
+
+ return true;
+}
+
+static int
+virDomainZPCIAddressAssignUid(virHashTablePtr set, virZPCIDeviceAddressPtr addr)
+{
+ if (addr->uid_assigned)
+ return 0;
+
+ addr->uid_assigned = virDomainZPCIAddressAssignId(set, &addr->zpci_uid, 1,
+ VIR_DOMAIN_DEVICE_ZPCI_MAX_UID, "uid");
+ return addr->uid_assigned ? 0 : -1;
+}
+
+static int
+virDomainZPCIAddressAssignFid(virHashTablePtr set, virZPCIDeviceAddressPtr addr)
+{
+ if (addr->fid_assigned)
+ return 0;
+
+ addr->fid_assigned = virDomainZPCIAddressAssignId(set, &addr->zpci_fid, 0,
+ VIR_DOMAIN_DEVICE_ZPCI_MAX_FID, "fid");
+ return addr->fid_assigned ? 0 : -1;
+}
+
+
+static void
+virDomainZPCIAddressReleaseUid(virHashTablePtr set, virZPCIDeviceAddressPtr addr)
+{
+ if (!addr->uid_assigned)
+ return;
+
+ if (virHashRemoveEntry(set, &addr->zpci_uid))
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Release uid %u failed"), addr->zpci_uid);
+
+ addr->uid_assigned = false;
+}
+
+static void
+virDomainZPCIAddressReleaseFid(virHashTablePtr set, virZPCIDeviceAddressPtr addr)
+{
+ if (!addr->fid_assigned)
+ return;
+
+ if (virHashRemoveEntry(set, &addr->zpci_fid))
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Release fid %u failed"), addr->zpci_fid);
+
+ addr->fid_assigned = false;
+}
+
+
+static void
+virDomainZPCIAddressReleaseIds(virDomainZPCIAddressIdsPtr zpciIds,
+ virPCIDeviceAddressPtr addr)
+{
+ if (!zpciIds || !addr->zpci)
+ return;
+
+ virDomainZPCIAddressReleaseUid(zpciIds->uids, addr->zpci);
+
+ virDomainZPCIAddressReleaseFid(zpciIds->fids, addr->zpci);
+
+ VIR_FREE(addr->zpci);
+}
+
+
+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 void
virDomainPCIAddressSetExtensionFree(virDomainPCIAddressSetPtr addrs)
{
@@ -44,6 +185,116 @@ virDomainPCIAddressSetExtensionFree(virDomainPCIAddressSetPtr addrs)
VIR_FREE(addrs->zpciIds);
}
+static int
+virDomainZPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr addr)
+{
+ if (!addr->zpci)
+ return 0;
+
+ if (addr->zpci->uid_assigned &&
+ virDomainZPCIAddressReserveUid(addrs->zpciIds->uids, addr->zpci))
+ return -1;
+
+ if (addr->zpci->fid_assigned &&
+ virDomainZPCIAddressReserveFid(addrs->zpciIds->fids, addr->zpci)) {
+ virDomainZPCIAddressReleaseUid(addrs->zpciIds->uids, addr->zpci);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+virDomainZPCIAddressReserveNextAddr(virDomainPCIAddressSetPtr addrs,
+ virZPCIDeviceAddressPtr zpci)
+{
+ if (!zpci->uid_assigned &&
+ virDomainZPCIAddressReserveNextUid(addrs->zpciIds->uids, zpci))
+ return -1;
+
+ if (!zpci->fid_assigned &&
+ virDomainZPCIAddressReserveNextFid(addrs->zpciIds->fids, zpci)) {
+ virDomainZPCIAddressReleaseUid(addrs->zpciIds->uids, zpci);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+virDomainPCIAddressExtensionReserveAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr addr,
+ virDomainPCIAddressExtensionFlags extFlags)
+{
+ if (extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) {
+ /* Reserve uid/fid to ZPCI device which has defined uid/fid
+ * in the domain.
+ */
+ if (virDomainZPCIAddressReserveAddr(addrs, addr) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+virDomainPCIAddressExtensionReserveNextAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr dev,
+ virDomainPCIAddressExtensionFlags extFlags)
+{
+ if (extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) {
+ /* Assign and reserve uid/fid to ZPCI device which has not defined uid/fid
+ * in the domain.
+ */
+ virZPCIDeviceAddress zpci = { 0 };
+
+ if (dev->zpci)
+ zpci = *dev->zpci;
+
+ if (virDomainZPCIAddressReserveNextAddr(addrs, &zpci) < 0)
+ return -1;
+
+ if (!addrs->dryRun) {
+ if (!dev->zpci && VIR_ALLOC(dev->zpci) < 0)
+ return -1;
+ *dev->zpci = zpci;
+ }
+ }
+
+ return 0;
+}
+
+static int
+virDomainPCIAddressEnsureExtensionAddr(virDomainPCIAddressSetPtr addrs,
+ virDomainDeviceInfoPtr dev)
+{
+ virZPCIDeviceAddressPtr zpci = dev->addr.pci.zpci;
+
+ if (zpci && !dev->pciAddressExtFlags) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("zPCI is not
supported."));
+ return -1;
+ }
+
+ if (!zpci) {
+ return virDomainPCIAddressExtensionReserveNextAddr(addrs, &dev->addr.pci,
+ dev->pciAddressExtFlags);
+ } else {
+ if (virDomainZPCIAddressAssignUid(addrs->zpciIds->uids, zpci))
+ return -1;
+
+ if (virDomainZPCIAddressAssignFid(addrs->zpciIds->fids, zpci))
+ return -1;
+
+ if (virDomainZPCIAddressReserveAddr(addrs, &dev->addr.pci))
+ return -1;
+ }
+
+ return 0;
+}
+
virDomainPCIConnectFlags
virDomainPCIControllerModelToConnectType(virDomainControllerModelPCI model)
{
@@ -740,12 +991,25 @@ virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs,
ret = virDomainPCIAddressReserveNextAddr(addrs, dev, flags, -1);
}
+ if (virDomainPCIAddressEnsureExtensionAddr(addrs, dev) < 0)
+ goto cleanup;
+
cleanup:
VIR_FREE(addrStr);
return ret;
}
+void
+virDomainPCIAddressExtensionReleaseAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr addr,
+ int extFlags)
+{
+ if ((extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI))
+ virDomainZPCIAddressReleaseIds(addrs->zpciIds, addr);
+}
+
+
void
virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr addr)
diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h
index d1ec869da4..a1f71f15f4 100644
--- a/src/conf/domain_addr.h
+++ b/src/conf/domain_addr.h
@@ -164,6 +164,16 @@ bool virDomainPCIAddressSlotInUse(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr addr)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int virDomainPCIAddressExtensionReserveAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr addr,
+ virDomainPCIAddressExtensionFlags extFlags)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int virDomainPCIAddressExtensionReserveNextAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr addr,
+ virDomainPCIAddressExtensionFlags extFlags)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
int virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr addr,
virDomainPCIConnectFlags flags,
@@ -185,6 +195,11 @@ void virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr addr)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+void virDomainPCIAddressExtensionReleaseAddr(virDomainPCIAddressSetPtr addrs,
+ virPCIDeviceAddressPtr addr,
+ int extFlags)
+ 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 3d82d489de..163f249e07 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -111,6 +111,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 210f94e15e..1a823c7616 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -1368,6 +1368,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;
+ virDomainPCIAddressExtensionFlags extFlags = info->pciAddressExtFlags;
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ if (virDomainPCIAddressExtensionReserveNextAddr(addrs, addr, extFlags) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
static int
qemuDomainCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device,
@@ -1385,7 +1403,12 @@ qemuDomainCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
* parent, and will have its address collected during the scan
* of the parent's device type.
*/
- return 0;
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI ||
+ info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+ return virDomainPCIAddressExtensionReserveAddr(addrs, addr,
+ info->pciAddressExtFlags);
+ else
+ return 0;
}
/* If we get to here, the device has a PCI address assigned in the
@@ -1456,6 +1479,11 @@ qemuDomainCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
goto cleanup;
}
+ if (virDomainPCIAddressExtensionReserveAddr(addrs, addr,
+ info->pciAddressExtFlags) < 0) {
+ goto cleanup;
+ }
+
ret = 0;
cleanup:
return ret;
@@ -2549,6 +2577,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,
@@ -2643,6 +2674,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
*/
@@ -3102,8 +3136,11 @@ 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,
+ info->pciAddressExtFlags);
+ }
if (virDomainUSBAddressRelease(priv->usbaddrs, info) < 0)
VIR_WARN("Unable to release USB address on %s", NULLSTR(devstr));
--
Yi Min