These rules will make it possible for libvirt to
automatically assign PCI addresses in a way that
respects any isolation constraints devices might
have.
Signed-off-by: Andrea Bolognani <abologna(a)redhat.com>
---
src/conf/domain_addr.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 99 insertions(+), 5 deletions(-)
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
index 48af1f5..22ff014 100644
--- a/src/conf/domain_addr.c
+++ b/src/conf/domain_addr.c
@@ -534,7 +534,7 @@ static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr addr,
virDomainPCIConnectFlags flags,
- int isolationGroup ATTRIBUTE_UNUSED,
+ int isolationGroup,
bool fromConfig)
{
int ret = -1;
@@ -542,6 +542,8 @@ virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr
addrs,
virDomainPCIAddressBusPtr bus;
virErrorNumber errType = (fromConfig
? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR);
+ bool firstDevice;
+ size_t slot;
if (!(addrStr = virDomainPCIAddressAsString(addr)))
goto cleanup;
@@ -572,6 +574,36 @@ virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr
addrs,
bus->slot[addr->slot].aggregate = true;
}
+ /* Figure out whether this is the first device we're plugging
+ * into the bus by looking at all the slots */
+ firstDevice = true;
+ for (slot = bus->minSlot; slot <= bus->maxSlot; slot++) {
+ if (bus->slot[slot].functions) {
+ firstDevice = false;
+ break;
+ }
+ }
+
+ if (firstDevice && !bus->isolationGroupLocked) {
+ /* The first device decides the isolation group for the
+ * entire bus */
+ bus->isolationGroup = isolationGroup;
+ VIR_DEBUG("PCI bus %.4x:%.2x assigned isolation group %d because of "
+ "first device %s",
+ addr->domain, addr->bus, isolationGroup, addrStr);
+ } else if (bus->isolationGroup != isolationGroup && fromConfig) {
+ /* If this is not the first function and its isolation group
+ * doesn't match the bus', then it should not be using this
+ * address. However, if the address comes from the user then
+ * we comply with the request and change the isolation group
+ * back to the default (because at that point isolation can't
+ * be guaranteed anymore) */
+ bus->isolationGroup = 0;
+ VIR_DEBUG("PCI bus %.4x:%.2x assigned isolation group %d because of "
+ "user assigned address %s",
+ addr->domain, addr->bus, isolationGroup, addrStr);
+ }
+
/* mark the requested function as reserved */
bus->slot[addr->slot].functions |= (1 << addr->function);
VIR_DEBUG("Reserving PCI address %s (aggregate='%s')", addrStr,
@@ -643,7 +675,28 @@ int
virDomainPCIAddressReleaseAddr(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr addr)
{
- addrs->buses[addr->bus].slot[addr->slot].functions &= ~(1 <<
addr->function);
+ virDomainPCIAddressBusPtr bus = &addrs->buses[addr->bus];
+ bool lastDevice;
+ size_t slot;
+
+ bus->slot[addr->slot].functions &= ~(1 << addr->function);
+
+ /* Figure out whether this is the first device we're plugging
+ * into the bus by looking at all the slots */
+ lastDevice = true;
+ for (slot = bus->minSlot; slot <= bus->maxSlot; slot++) {
+ if (bus->slot[slot].functions) {
+ lastDevice = false;
+ break;
+ }
+ }
+
+ /* If the one we just unplugged was the last device on the bus,
+ * we can reset the isolation group for the bus so that any
+ * device we'll try to plug in from now on will be accepted */
+ if (lastDevice && !bus->isolationGroupLocked)
+ bus->isolationGroup = 0;
+
return 0;
}
@@ -749,7 +802,7 @@ static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs,
virPCIDeviceAddressPtr next_addr,
virDomainPCIConnectFlags flags,
- int isolationGroup ATTRIBUTE_UNUSED,
+ int isolationGroup,
int function)
{
virPCIDeviceAddress a = { 0 };
@@ -765,11 +818,50 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs,
else
a.function = function;
- /* "Begin at the beginning," the King said, very gravely, "and go on
- * till you come to the end: then stop." */
+ /* When looking for a suitable bus for the device, start by being
+ * very strict and ignoring all those where the isolation groups
+ * don't match. This ensures all devices sharing the same isolation
+ * group will end up on the same group */
+ for (a.bus = 0; a.bus < addrs->nbuses; a.bus++) {
+ virDomainPCIAddressBusPtr bus = &addrs->buses[a.bus];
+ bool found = false;
+
+ if (bus->isolationGroup != isolationGroup)
+ continue;
+
+ a.slot = bus->minSlot;
+
+ if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, function,
+ flags, &found) < 0) {
+ goto error;
+ }
+
+ if (found)
+ goto success;
+ }
+
+ /* We haven't been able to find a perfectly matching bus, but we
+ * might still be able to make this work by altering the isolation
+ * group for a bus that's currently empty. So let's try that */
for (a.bus = 0; a.bus < addrs->nbuses; a.bus++) {
virDomainPCIAddressBusPtr bus = &addrs->buses[a.bus];
bool found = false;
+ bool firstDevice = true;
+ size_t slot;
+
+ /* Go through all the slots and see whether they are empty */
+ for (slot = bus->minSlot; slot <= bus->maxSlot; slot++) {
+ if (bus->slot[slot].functions) {
+ firstDevice = false;
+ break;
+ }
+ }
+
+ /* We can only change the isolation group for a bus when
+ * plugging in the first device; moreover, some buses are
+ * prevented from ever changing it */
+ if (!firstDevice || bus->isolationGroupLocked)
+ continue;
a.slot = bus->minSlot;
@@ -778,6 +870,8 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs,
goto error;
}
+ /* The isolation group for the bus will actually be changed
+ * later, in virDomainPCIAddressReserveAddrInternal() */
if (found)
goto success;
}
--
2.7.5