6d3351
From 672fd7d403ef52f10084c5c0b6aaeeb94f6fba2a Mon Sep 17 00:00:00 2001
6d3351
Message-Id: <672fd7d403ef52f10084c5c0b6aaeeb94f6fba2a@dist-git>
6d3351
From: Andrea Bolognani <abologna@redhat.com>
6d3351
Date: Tue, 18 Jul 2017 12:10:06 +0200
6d3351
Subject: [PATCH] conf: Implement isolation rules
6d3351
6d3351
These rules will make it possible for libvirt to
6d3351
automatically assign PCI addresses in a way that
6d3351
respects any isolation constraints devices might
6d3351
have.
6d3351
6d3351
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
6d3351
Reviewed-by: Laine Stump <laine@laine.org>
6d3351
(cherry picked from commit 209dc615e898f027b6dc8fa6acd3aeba5fd465bd)
6d3351
6d3351
Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1280542
6d3351
6d3351
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
6d3351
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
6d3351
---
6d3351
 src/conf/domain_addr.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++---
6d3351
 src/conf/domain_addr.h |  3 +++
6d3351
 2 files changed, 72 insertions(+), 4 deletions(-)
6d3351
6d3351
diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c
6d3351
index a067493136..d586bb7e47 100644
6d3351
--- a/src/conf/domain_addr.c
6d3351
+++ b/src/conf/domain_addr.c
6d3351
@@ -369,6 +369,20 @@ virDomainPCIAddressBusIsFullyReserved(virDomainPCIAddressBusPtr bus)
6d3351
 }
6d3351
 
6d3351
 
6d3351
+bool
6d3351
+virDomainPCIAddressBusIsEmpty(virDomainPCIAddressBusPtr bus)
6d3351
+{
6d3351
+    size_t i;
6d3351
+
6d3351
+    for (i = bus->minSlot; i <= bus->maxSlot; i++) {
6d3351
+        if (bus->slot[i].functions)
6d3351
+            return false;
6d3351
+    }
6d3351
+
6d3351
+    return true;
6d3351
+}
6d3351
+
6d3351
+
6d3351
 /* Ensure addr fits in the address set, by expanding it if needed
6d3351
  *
6d3351
  * Return value:
6d3351
@@ -548,7 +562,7 @@ static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
6d3351
 virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr addrs,
6d3351
                                        virPCIDeviceAddressPtr addr,
6d3351
                                        virDomainPCIConnectFlags flags,
6d3351
-                                       unsigned int isolationGroup ATTRIBUTE_UNUSED,
6d3351
+                                       unsigned int isolationGroup,
6d3351
                                        bool fromConfig)
6d3351
 {
6d3351
     int ret = -1;
6d3351
@@ -586,6 +600,26 @@ virDomainPCIAddressReserveAddrInternal(virDomainPCIAddressSetPtr addrs,
6d3351
         bus->slot[addr->slot].aggregate = true;
6d3351
     }
6d3351
 
6d3351
+    if (virDomainPCIAddressBusIsEmpty(bus) && !bus->isolationGroupLocked) {
6d3351
+        /* The first device decides the isolation group for the
6d3351
+         * entire bus */
6d3351
+        bus->isolationGroup = isolationGroup;
6d3351
+        VIR_DEBUG("PCI bus %.4x:%.2x assigned isolation group %u because of "
6d3351
+                  "first device %s",
6d3351
+                  addr->domain, addr->bus, isolationGroup, addrStr);
6d3351
+    } else if (bus->isolationGroup != isolationGroup && fromConfig) {
6d3351
+        /* If this is not the first function and its isolation group
6d3351
+         * doesn't match the bus', then it should not be using this
6d3351
+         * address. However, if the address comes from the user then
6d3351
+         * we comply with the request and change the isolation group
6d3351
+         * back to the default (because at that point isolation can't
6d3351
+         * be guaranteed anymore) */
6d3351
+        bus->isolationGroup = 0;
6d3351
+        VIR_DEBUG("PCI bus %.4x:%.2x assigned isolation group %u because of "
6d3351
+                  "user assigned address %s",
6d3351
+                  addr->domain, addr->bus, isolationGroup, addrStr);
6d3351
+    }
6d3351
+
6d3351
     /* mark the requested function as reserved */
6d3351
     bus->slot[addr->slot].functions |= (1 << addr->function);
6d3351
     VIR_DEBUG("Reserving PCI address %s (aggregate='%s')", addrStr,
6d3351
@@ -763,7 +797,7 @@ static int ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
6d3351
 virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs,
6d3351
                                virPCIDeviceAddressPtr next_addr,
6d3351
                                virDomainPCIConnectFlags flags,
6d3351
-                               unsigned int isolationGroup ATTRIBUTE_UNUSED,
6d3351
+                               unsigned int isolationGroup,
6d3351
                                int function)
6d3351
 {
6d3351
     virPCIDeviceAddress a = { 0 };
6d3351
@@ -779,12 +813,17 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs,
6d3351
     else
6d3351
         a.function = function;
6d3351
 
6d3351
-    /* "Begin at the beginning," the King said, very gravely, "and go on
6d3351
-     * till you come to the end: then stop." */
6d3351
+    /* When looking for a suitable bus for the device, start by being
6d3351
+     * very strict and ignoring all those where the isolation groups
6d3351
+     * don't match. This ensures all devices sharing the same isolation
6d3351
+     * group will end up on the same bus */
6d3351
     for (a.bus = 0; a.bus < addrs->nbuses; a.bus++) {
6d3351
         virDomainPCIAddressBusPtr bus = &addrs->buses[a.bus];
6d3351
         bool found = false;
6d3351
 
6d3351
+        if (bus->isolationGroup != isolationGroup)
6d3351
+            continue;
6d3351
+
6d3351
         a.slot = bus->minSlot;
6d3351
 
6d3351
         if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, function,
6d3351
@@ -796,6 +835,32 @@ virDomainPCIAddressGetNextAddr(virDomainPCIAddressSetPtr addrs,
6d3351
             goto success;
6d3351
     }
6d3351
 
6d3351
+    /* We haven't been able to find a perfectly matching bus, but we
6d3351
+     * might still be able to make this work by altering the isolation
6d3351
+     * group for a bus that's currently empty. So let's try that */
6d3351
+    for (a.bus = 0; a.bus < addrs->nbuses; a.bus++) {
6d3351
+        virDomainPCIAddressBusPtr bus = &addrs->buses[a.bus];
6d3351
+        bool found = false;
6d3351
+
6d3351
+        /* We can only change the isolation group for a bus when
6d3351
+         * plugging in the first device; moreover, some buses are
6d3351
+         * prevented from ever changing it */
6d3351
+        if (!virDomainPCIAddressBusIsEmpty(bus) || bus->isolationGroupLocked)
6d3351
+            continue;
6d3351
+
6d3351
+        a.slot = bus->minSlot;
6d3351
+
6d3351
+        if (virDomainPCIAddressFindUnusedFunctionOnBus(bus, &a, function,
6d3351
+                                                       flags, &found) < 0) {
6d3351
+            goto error;
6d3351
+        }
6d3351
+
6d3351
+        /* The isolation group for the bus will actually be changed
6d3351
+         * later, in virDomainPCIAddressReserveAddrInternal() */
6d3351
+        if (found)
6d3351
+            goto success;
6d3351
+    }
6d3351
+
6d3351
     /* There were no free slots after the last used one */
6d3351
     if (addrs->dryRun) {
6d3351
         /* a is already set to the first new bus */
6d3351
diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h
6d3351
index 01dbc5114f..ae6a342dbc 100644
6d3351
--- a/src/conf/domain_addr.h
6d3351
+++ b/src/conf/domain_addr.h
6d3351
@@ -150,6 +150,9 @@ int virDomainPCIAddressBusSetModel(virDomainPCIAddressBusPtr bus,
6d3351
 bool virDomainPCIAddressBusIsFullyReserved(virDomainPCIAddressBusPtr bus)
6d3351
     ATTRIBUTE_NONNULL(1);
6d3351
 
6d3351
+bool virDomainPCIAddressBusIsEmpty(virDomainPCIAddressBusPtr bus)
6d3351
+    ATTRIBUTE_NONNULL(1);
6d3351
+
6d3351
 bool virDomainPCIAddressSlotInUse(virDomainPCIAddressSetPtr addrs,
6d3351
                                   virPCIDeviceAddressPtr addr)
6d3351
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
6d3351
-- 
6d3351
2.13.3
6d3351