If a PCI device has VIR_PCI_CONNECT_AGGREGATE_SLOT set in its
pciConnectFlags, then during address assignment we allow multiple
instances of this type of device to be auto-assigned to multiple
functions on the same device. A slot is used for aggregating multiple
devices only if the first device assigned to that slot had
VIR_PCI_CONNECT_AGGREGATE_SLOT set. but any device types that have
AGGREGATE_SLOT set might be mix/matched on the same slot.
(NB: libvirt should never set the AGGREGATE_SLOT flag for a device
type that might need to be hotplugged. Currently it is only planned
for pcie-root-port and possibly other PCI controller types, and none
of those are hotpluggable anyway)
There aren't yet any devices that use this flag. That will be in a
later patch.
---
src/conf/domain_addr.c | 45 +++++++++++++++++++++++++++++++++++++++---
src/conf/domain_addr.h | 30 +++++++++++++++++++---------
src/qemu/qemu_domain_address.c | 2 +-
3 files changed, 64 insertions(+), 13 deletions(-)
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index b8a91d2..18421e0 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -564,8 +564,20 @@ virDomainPCIAddressReserveAddr(virDomainPCIAddressSetPtr addrs,
addrStr);
goto cleanup;
}
+
+ /* if this is the first function to be reserved on this slot, and
+ * the device it's being reserved for can aggregate multiples on a
+ * slot, set the slot's aggregate flag.
+ */
+ if (!bus->slot[addr->slot].functions &&
+ flags & VIR_PCI_CONNECT_AGGREGATE_SLOT) {
+ bus->slot[addr->slot].aggregate = true;
+ }
+
+ /* mark the requested function as reserved */
bus->slot[addr->slot].functions |= (1 << addr->function);
- VIR_DEBUG("Reserving PCI address %s", addrStr);
+ VIR_DEBUG("Reserving PCI address %s (aggregate='%s')", addrStr,
+ bus->slot[addr->slot].aggregate ? "true" :
"false");
ret = 0;
cleanup:
@@ -694,7 +706,7 @@ virDomainPCIAddressSetFree(virDomainPCIAddressSetPtr addrs)
static int
virDomainPCIAddressFindUnusedFunctionOnBus(virDomainPCIAddressBusPtr bus,
virPCIDeviceAddressPtr searchAddr,
- int function ATTRIBUTE_UNUSED,
+ int function,
virDomainPCIConnectFlags flags,
bool *found)
{
@@ -717,6 +729,33 @@ virDomainPCIAddressFindUnusedFunctionOnBus(virDomainPCIAddressBusPtr
bus,
break;
}
+ if (flags & VIR_PCI_CONNECT_AGGREGATE_SLOT &&
+ bus->slot[searchAddr->slot].aggregate) {
+ /* slot and device are okay with aggregating devices */
+ if ((bus->slot[searchAddr->slot].functions &
+ (1 << searchAddr->function)) == 0) {
+ *found = true;
+ break;
+ }
+
+ /* also check for *any* unused function if caller
+ * sent function = -1
+ */
+ if (function == -1) {
+ while (searchAddr->function < 8) {
+ if ((bus->slot[searchAddr->slot].functions &
+ (1 << searchAddr->function)) == 0) {
+ *found = true;
+ break; /* out of inner while */
+ }
+ searchAddr->function++;
+ }
+ if (*found)
+ break; /* out of outer while */
+ searchAddr->function = 0; /* reset for next try */
+ }
+ }
+
VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
searchAddr->domain, searchAddr->bus, searchAddr->slot);
searchAddr->slot++;
@@ -863,7 +902,7 @@ virDomainPCIAddressReserveNextSlot(virDomainPCIAddressSetPtr addrs,
virDomainDeviceInfoPtr dev,
virDomainPCIConnectFlags flags)
{
- return virDomainPCIAddressReserveNextAddr(addrs, dev, flags, 0);
+ return virDomainPCIAddressReserveNextAddr(addrs, dev, flags, -1);
}
diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h
index 50c4675..dd4cd5b 100644
--- a/src/conf/domain_addr.h
+++ b/src/conf/domain_addr.h
@@ -32,18 +32,23 @@
typedef enum {
VIR_PCI_CONNECT_HOTPLUGGABLE = 1 << 0, /* is hotplug needed/supported */
+ /* set for devices that can share a single slot in auto-assignment
+ * (by assigning one device to each of the 8 functions on the slot)
+ */
+ VIR_PCI_CONNECT_AGGREGATE_SLOT = 1 << 1,
+
/* kinds of devices as a bitmap so they can be combined (some PCI
* controllers permit connecting multiple types of devices)
*/
- VIR_PCI_CONNECT_TYPE_PCI_DEVICE = 1 << 1,
- VIR_PCI_CONNECT_TYPE_PCIE_DEVICE = 1 << 2,
- VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT = 1 << 3,
- VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT = 1 << 4,
- VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_DOWNSTREAM_PORT = 1 << 5,
- VIR_PCI_CONNECT_TYPE_DMI_TO_PCI_BRIDGE = 1 << 6,
- VIR_PCI_CONNECT_TYPE_PCI_EXPANDER_BUS = 1 << 7,
- VIR_PCI_CONNECT_TYPE_PCIE_EXPANDER_BUS = 1 << 8,
- VIR_PCI_CONNECT_TYPE_PCI_BRIDGE = 1 << 9,
+ VIR_PCI_CONNECT_TYPE_PCI_DEVICE = 1 << 2,
+ VIR_PCI_CONNECT_TYPE_PCIE_DEVICE = 1 << 3,
+ VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT = 1 << 4,
+ VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT = 1 << 5,
+ VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_DOWNSTREAM_PORT = 1 << 6,
+ VIR_PCI_CONNECT_TYPE_DMI_TO_PCI_BRIDGE = 1 << 7,
+ VIR_PCI_CONNECT_TYPE_PCI_EXPANDER_BUS = 1 << 8,
+ VIR_PCI_CONNECT_TYPE_PCIE_EXPANDER_BUS = 1 << 9,
+ VIR_PCI_CONNECT_TYPE_PCI_BRIDGE = 1 << 10,
} virDomainPCIConnectFlags;
/* a combination of all bits that describe the type of connections
@@ -75,6 +80,13 @@ typedef struct {
* in use by a device, or clear if it isn't.
*/
uint8_t functions;
+
+ /* aggregate is true if this slot has only devices with
+ * VIR_PCI_CONNECT_AGGREGATE assigned to its functions (meaning
+ * that other devices with the same flags could also be
+ * auto-assigned to the other functions)
+ */
+ bool aggregate;
} virDomainPCIAddressSlot;
typedef struct {
diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
index 4a2a489..8e2ffe0 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -904,7 +904,7 @@ static int
qemuDomainPCIAddressReserveNextSlot(virDomainPCIAddressSetPtr addrs,
virDomainDeviceInfoPtr dev)
{
- return qemuDomainPCIAddressReserveNextAddr(addrs, dev, 0);
+ return qemuDomainPCIAddressReserveNextAddr(addrs, dev, -1);
}
--
2.7.4