[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH v3 24/26] conf: Implement isolation rules



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 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]