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>
Reviewed-by: Ján Tomko <jtomko(a)redhat.com>
---
src/conf/device_conf.h | 7 +
src/conf/domain_addr.c | 291 +++++++++++++++++++++++++++++++++++++++++
src/conf/domain_addr.h | 15 +++
src/libvirt_private.syms | 3 +
src/qemu/qemu_domain_address.c | 58 +++++++-
5 files changed, 373 insertions(+), 1 deletion(-)
diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h
index 6f926dff1d..40838a9401 100644
--- a/src/conf/device_conf.h
+++ b/src/conf/device_conf.h
@@ -211,6 +211,13 @@ virDeviceInfoPCIAddressPresent(const virDomainDeviceInfo *info)
!virPCIDeviceAddressIsEmpty(&info->addr.pci);
}
+static inline bool
+virDeviceInfoPCIAddressExtensionPresent(const virDomainDeviceInfo *info)
+{
+ return info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
+ info->addr.pci.zpci;
+}
+
int virPCIDeviceAddressParseXML(xmlNodePtr node,
virPCIDeviceAddressPtr addr);
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index 90d8abc1f4..cf1fc467f2 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -33,6 +33,170 @@
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 +208,120 @@ virDomainPCIAddressSetExtensionFree(virDomainPCIAddressSetPtr addrs)
VIR_FREE(addrs->zpciIds);
}
+
+static int
+virDomainZPCIAddressReserveAddr(virDomainZPCIAddressIdsPtr zpciIds,
+ virZPCIDeviceAddressPtr addr)
+{
+ if (!addr)
+ return 0;
+
+ if (addr->uid_assigned &&
+ virDomainZPCIAddressReserveUid(zpciIds->uids, addr)) {
+ return -1;
+ }
+
+ if (addr->fid_assigned &&
+ virDomainZPCIAddressReserveFid(zpciIds->fids, addr)) {
+ virDomainZPCIAddressReleaseUid(zpciIds->uids, addr);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainZPCIAddressReserveNextAddr(virDomainZPCIAddressIdsPtr zpciIds,
+ virZPCIDeviceAddressPtr addr)
+{
+ if (!addr->uid_assigned &&
+ virDomainZPCIAddressReserveNextUid(zpciIds->uids, addr)) {
+ return -1;
+ }
+
+ if (!addr->fid_assigned &&
+ virDomainZPCIAddressReserveNextFid(zpciIds->fids, addr)) {
+ virDomainZPCIAddressReleaseUid(zpciIds->uids, addr);
+ 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->zpciIds, addr->zpci) < 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->zpciIds, &zpci) < 0)
+ return -1;
+
+ if (!addrs->dryRun) {
+ if (!dev->zpci && VIR_ALLOC(dev->zpci) < 0)
+ return -1;
+ *dev->zpci = zpci;
+ }
+ }
+
+ return 0;
+}
+
+static int
+virDomainPCIAddressExtensionEnsureAddr(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->zpciIds, zpci))
+ return -1;
+ }
+
+ return 0;
+}
+
virDomainPCIConnectFlags
virDomainPCIControllerModelToConnectType(virDomainControllerModelPCI model)
{
@@ -740,12 +1018,25 @@ virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs,
ret = virDomainPCIAddressReserveNextAddr(addrs, dev, flags, -1);
}
+ if (virDomainPCIAddressExtensionEnsureAddr(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 d57a836c2e..490fbf5739 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 200d96a790..68897cde2c 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -1373,6 +1373,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,
@@ -1466,6 +1484,29 @@ 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 (!virDeviceInfoPCIAddressExtensionPresent(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,
+ info->pciAddressExtFlags);
+}
+
static virDomainPCIAddressSetPtr
qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
@@ -1560,6 +1601,12 @@ qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
if (virDomainDeviceInfoIterate(def, qemuDomainCollectPCIAddress, addrs) < 0)
goto error;
+ if (virDomainDeviceInfoIterate(def,
+ qemuDomainCollectPCIAddressExtension,
+ addrs) < 0) {
+ goto error;
+ }
+
return addrs;
error:
@@ -2561,6 +2608,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,
@@ -2655,6 +2705,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
*/
@@ -3114,8 +3167,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