diff --git a/src/conf/device_conf.c b/src/conf/device_conf.c index f9f6b6e38f..44b210d5ec 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 @@ -215,6 +216,21 @@ virDeviceInfoPCIAddressIsPresent(const virDomainDeviceInfo *info) } +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 2366e03607..867b633903 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -198,6 +198,9 @@ bool virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info, bool virDeviceInfoPCIAddressIsWanted(const virDomainDeviceInfo *info); bool virDeviceInfoPCIAddressIsPresent(const virDomainDeviceInfo *info); +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 380091f049..3e1d767e4f 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) { @@ -715,12 +947,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 8d64d6b795..183235309b 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -160,6 +160,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, @@ -181,6 +189,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 5588fa2e42..8ea463aa0a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -93,6 +93,8 @@ virCPUModeTypeToString; # conf/device_conf.h +virDeviceInfoPCIAddressExtensionIsPresent; +virDeviceInfoPCIAddressExtensionIsWanted; virDeviceInfoPCIAddressIsPresent; virDeviceInfoPCIAddressIsWanted; virDomainDeviceAddressIsValid; @@ -119,6 +121,9 @@ virDomainCCWAddressSetFree; 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 ae3e8f0845..73ed9cc68c 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1407,6 +1407,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, @@ -1500,6 +1518,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, @@ -1591,6 +1634,12 @@ qemuDomainPCIAddressSetCreate(virDomainDefPtr def, if (virDomainDeviceInfoIterate(def, qemuDomainCollectPCIAddress, addrs) < 0) goto error; + if (virDomainDeviceInfoIterate(def, + qemuDomainCollectPCIAddressExtension, + addrs) < 0) { + goto error; + } + return addrs; error: @@ -2595,6 +2644,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, @@ -2689,6 +2741,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 */ @@ -3148,8 +3203,10 @@ qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, if (!devstr) devstr = info->alias; - if (virDeviceInfoPCIAddressIsPresent(info)) + if (virDeviceInfoPCIAddressIsPresent(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));