This patch assigns multifunction pci addresses to devices in the devlist.
The pciaddrs passed in the argument is not altered so that the actual call to
reserve the address using virDomainPCIAddressEnsureAddr() passes. The function
focuses on user given address validation and also the auto-assign of addresses.
The address auto-assignment works well for PPC64 and x86-i440fx machines.
The q35 machines needs some level of logic here to get the correct next valid
slot so that the hotplug go through fine.
Signed-off-by: Shivaprasad G Bhat <sbhat(a)linux.vnet.ibm.com>
---
src/conf/domain_addr.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++
src/conf/domain_addr.h | 4 +
src/libvirt_private.syms | 1
3 files changed, 209 insertions(+)
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index 7ea9e4d..a78a8e4 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -454,6 +454,210 @@ virDomainPCIAddressReserveSlot(virDomainPCIAddressSetPtr addrs,
return virDomainPCIAddressReserveAddr(addrs, addr, flags, true, false);
}
+static int
+virDomainPCIAddressGetNextFunctionAddr(uint8_t *slot,
+ virPCIDeviceAddressPtr addr)
+{
+ size_t i = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (!(*slot & 1 << i)) {
+ addr->function = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int
+virDomainPCIAddressAssignFunctionAddr(virDomainPCIAddressSetPtr addrs,
+ uint8_t *slot,
+ virPCIDeviceAddressPtr addr,
+ virDomainPCIConnectFlags flags,
+ bool fromConfig)
+{
+ int ret = -1;
+ char *addrStr = NULL;
+ virErrorNumber errType = (fromConfig
+ ? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR);
+
+ if (!(addrStr = virDomainPCIAddressAsString(addr)))
+ goto cleanup;
+
+ /* Check that the requested bus exists, is the correct type, and we
+ * are asking for a valid slot and function
+ */
+ if (!virDomainPCIAddressValidate(addrs, addr, addrStr, flags, fromConfig))
+ goto cleanup;
+
+ if (*slot & (1 << addr->function)) {
+ if (addr->function == 0) {
+ virReportError(errType,
+ _("Attempted double use of PCI Address %s"),
+ addrStr);
+ } else {
+ virReportError(errType,
+ _("Attempted double use of PCI Address %s "
+ "(may need \"multifunction='on'\"
"
+ "for device on function 0)"), addrStr);
+ }
+ goto cleanup;
+ }
+ *slot |= (1 << addr->function);
+ if (addr->function == 0)
+ addr->multi = VIR_TRISTATE_SWITCH_ON;
+ VIR_DEBUG("Reserving PCI address %s", addrStr);
+
+ ret = 0;
+ cleanup:
+ VIR_FREE(addrStr);
+ return ret;
+}
+
+
+static int
+virDomainPCIAddressAssignSlotNextFunction(virDomainPCIAddressSetPtr addrs,
+ uint8_t *slot,
+ virDomainDeviceInfoPtr dev,
+ virDomainPCIConnectFlags flags)
+{
+ if (virDomainPCIAddressGetNextFunctionAddr(slot, &dev->addr.pci) < 0)
+ return -1;
+
+ if (virDomainPCIAddressAssignFunctionAddr(addrs, slot, &dev->addr.pci, flags,
false) < 0)
+ return -1;
+
+ dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+
+ return 0;
+}
+
+
+static int
+virDomainPCIAddressAssignSlotAddr(virDomainPCIAddressSetPtr addrs,
+ uint8_t *slot,
+ virDomainDeviceInfoPtr dev)
+{
+ int ret = -1;
+ char *addrStr = NULL;
+ virDomainPCIConnectFlags flags = (VIR_PCI_CONNECT_HOTPLUGGABLE |
+ VIR_PCI_CONNECT_TYPE_PCI_DEVICE);
+
+ if (!(addrStr = virDomainPCIAddressAsString(&dev->addr.pci)))
+ goto cleanup;
+
+ if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ if (((dev->addr.pci.function == 0) && (dev->addr.pci.multi ==
VIR_TRISTATE_SWITCH_ON)) ||
+ dev->addr.pci.function != 0) {
+
+ if (!virDomainPCIAddressValidate(addrs, &dev->addr.pci,
+ addrStr, flags, true))
+ goto cleanup;
+
+ ret = virDomainPCIAddressAssignFunctionAddr(addrs, slot,
&dev->addr.pci, flags, true);
+ } else {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Not a multifunction device address addressi %s"),
addrStr);
+ }
+ } else {
+ ret = virDomainPCIAddressAssignSlotNextFunction(addrs, slot, dev, flags);
+ }
+
+ cleanup:
+ VIR_FREE(addrStr);
+ return ret;
+}
+
+int
+virDomainPCIMultifunctionDeviceAddressAssign(virDomainPCIAddressSetPtr addrs,
virDomainDeviceDefListPtr devlist)
+{
+ size_t i;
+ virPCIDeviceAddress addr;
+ virDomainPCIAddressBusPtr bus;
+ uint8_t slot;
+ virDomainDeviceInfoPtr info = NULL, privinfo = NULL;
+ virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPES_MASK;
+
+ if (virDomainPCIAddressGetNextSlot(addrs, &addr, flags) < 0)
+ return -1;
+
+ bus = &addrs->buses[addr.bus];
+ slot = bus->slots[addr.slot];
+
+ for (i = 0; i < devlist->count; i++) {
+ virDomainDeviceDefPtr dev = devlist->devs[devlist->count - i - 1];
+ switch ((virDomainDeviceType) dev->type) {
+ case VIR_DOMAIN_DEVICE_DISK:
+ info = &dev->data.disk->info;
+ break;
+ case VIR_DOMAIN_DEVICE_NET:
+ info = &dev->data.net->info;
+ break;
+ case VIR_DOMAIN_DEVICE_RNG:
+ info = &dev->data.rng->info;
+ break;
+ case VIR_DOMAIN_DEVICE_HOSTDEV:
+ info = dev->data.hostdev->info;
+ break;
+ case VIR_DOMAIN_DEVICE_CONTROLLER:
+ info = &dev->data.controller->info;
+ break;
+ case VIR_DOMAIN_DEVICE_CHR:
+ info = &dev->data.chr->info;
+ break;
+ case VIR_DOMAIN_DEVICE_FS:
+ case VIR_DOMAIN_DEVICE_INPUT:
+ case VIR_DOMAIN_DEVICE_SOUND:
+ case VIR_DOMAIN_DEVICE_VIDEO:
+ case VIR_DOMAIN_DEVICE_WATCHDOG:
+ case VIR_DOMAIN_DEVICE_HUB:
+ case VIR_DOMAIN_DEVICE_SMARTCARD:
+ case VIR_DOMAIN_DEVICE_MEMBALLOON:
+ case VIR_DOMAIN_DEVICE_NVRAM:
+ case VIR_DOMAIN_DEVICE_SHMEM:
+ case VIR_DOMAIN_DEVICE_LEASE:
+ case VIR_DOMAIN_DEVICE_REDIRDEV:
+ case VIR_DOMAIN_DEVICE_MEMORY:
+ case VIR_DOMAIN_DEVICE_NONE:
+ case VIR_DOMAIN_DEVICE_TPM:
+ case VIR_DOMAIN_DEVICE_PANIC:
+ case VIR_DOMAIN_DEVICE_GRAPHICS:
+ case VIR_DOMAIN_DEVICE_LAST:
+ break;
+ }
+
+ if (privinfo) {
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ if (privinfo->addr.pci.bus != info->addr.pci.bus ||
+ privinfo->addr.pci.slot != info->addr.pci.slot)
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Multifunction PCI device "
+ "cant be split across different guest PCI
slots"));
+ return -1;
+
+ }
+ }
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ bus = &addrs->buses[info->addr.pci.bus];
+ slot = bus->slots[info->addr.pci.slot];
+ }
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
+ info->addr.pci.bus = addr.bus;
+ info->addr.pci.slot = addr.slot;
+ info->addr.pci.domain = addr.domain;
+ }
+
+ if (virDomainPCIAddressAssignSlotAddr(addrs, &slot, info) < 0)
+ return -1;
+ privinfo = info;
+ }
+ return 0;
+}
+
+
int
virDomainPCIAddressEnsureAddr(virDomainPCIAddressSetPtr addrs,
virDomainDeviceInfoPtr dev)
diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h
index f3eda89..9eb6b9d 100644
--- a/src/conf/domain_addr.h
+++ b/src/conf/domain_addr.h
@@ -157,6 +157,10 @@ int virDomainPCIAddressReserveNextSlot(virDomainPCIAddressSetPtr
addrs,
virDomainPCIConnectFlags flags)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int
+virDomainPCIMultifunctionDeviceAddressAssign(virDomainPCIAddressSetPtr addrs,
+ virDomainDeviceDefListPtr devlist);
+
struct _virDomainCCWAddressSet {
virHashTablePtr defined;
virDomainDeviceCCWAddress next;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d6a60b5..d72dc63 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -109,6 +109,7 @@ virDomainPCIAddressSetGrow;
virDomainPCIAddressSlotInUse;
virDomainPCIAddressValidate;
virDomainPCIControllerModelToConnectType;
+virDomainPCIMultifunctionDeviceAddressAssign;
virDomainPCIMultifunctionDeviceDefParseNode;
virDomainVirtioSerialAddrAssign;
virDomainVirtioSerialAddrAutoAssign;