Allow specifying addresses with non-zero buses in the XML.
Check that the bridge topology results in their indexes matching
the PCI buses they provide.
---
 src/qemu/qemu_command.c | 207 +++++++++++++++++++++++++++++++++++++++++++++---
 src/qemu/qemu_command.h |   3 +-
 2 files changed, 196 insertions(+), 14 deletions(-)

diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 7817b13..7073844 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1195,6 +1195,8 @@ cleanup:
 typedef uint8_t _qemuDomainPCIAddressBus[QEMU_PCI_ADDRESS_LAST_SLOT];
 struct _qemuDomainPCIAddressSet {
     _qemuDomainPCIAddressBus *used;
+    size_t nbuses;        /* allocation of used */
+    unsigned int maxbus;        /* maximum used bus number */
     virDevicePCIAddress lastaddr;
 };
 
@@ -1203,7 +1205,7 @@ struct _qemuDomainPCIAddressSet {
  * Returns -1 if the address is unusable
  * 0 if it's OK.
  */
-static int qemuPCIAddressCheck(qemuDomainPCIAddressSetPtr addrs 
ATTRIBUTE_UNUSED,
+static int qemuPCIAddressCheck(qemuDomainPCIAddressSetPtr addrs,
                                virDevicePCIAddressPtr addr)
 {
     if (addr->domain != 0) {
@@ -1211,9 +1213,10 @@ static int 
qemuPCIAddressCheck(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UNUSED
                        _("Only PCI domain 0 is available"));
         return -1;
     }
-    if (addr->bus != 0) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("Only PCI bus 0 is available"));
+    if (addr->bus >= addrs->nbuses) {
+        virReportError(VIR_ERR_XML_ERROR, _("Only PCI buses up to %u are"
+                                            " available"),
+                       (unsigned int) addrs->nbuses - 1);
         return -1;
     }
     if (addr->function >= QEMU_PCI_ADDRESS_LAST_FUNCTION) {
@@ -1228,9 +1231,46 @@ static int 
qemuPCIAddressCheck(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UNUSED
                        QEMU_PCI_ADDRESS_LAST_SLOT);
         return -1;
     }
+    if (addr->slot == 0) {
+        if (addr->bus) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Slot 0 is unusable on PCI bridges"));
+            return -1;
+        } else {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                          _("Slot 0 on bus 0 is reserved for the host 
bridge"));
+            return -1;
+        }
+    }
+    if (addr->bus > addrs->maxbus)
+        addrs->maxbus = addr->bus;
     return 0;
 }
 
+/* grows the address set to fit addr in
+ * -1 = OOM
+ *  0 = no action required
+ * >0 = number of buses added
+ */
+static int qemuPCIAddressSetGrow(qemuDomainPCIAddressSetPtr addrs,
+                                 virDevicePCIAddressPtr addr)
+{
+    int add, i;
+
+    add = addr->bus - addrs->nbuses + 1;
+    i = addrs->nbuses;
+    if (add <= 0)
+        return 0;
+    if (VIR_EXPAND_N(addrs->used, addrs->nbuses, add) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+    /* reserve slot 0 on the new buses */
+    for (; i < addrs->nbuses; i++)
+        addrs->used[i][0] = 0xFF;
+    return add;
+}
+
 
 static char *qemuPCIAddressAsString(virDevicePCIAddressPtr addr)
 {
@@ -1268,6 +1308,9 @@ static int qemuCollectPCIAddress(virDomainDefPtr def 
ATTRIBUTE_UNUSED,
         return 0;
     }
 
+    if (qemuPCIAddressSetGrow(addrs, addr) < 0)
+        return -1;
+
     if (qemuPCIAddressCheck(addrs, addr) < 0)
         return -1;
 
@@ -1311,6 +1354,124 @@ cleanup:
 }
 
 
+typedef struct _qemuDomainPCIBridge qemuDomainPCIBridge;
+struct _qemuDomainPCIBridge {
+    int idx;
+    int slot;
+};
+
+
+/* Recursively check if PCI bridge indexes match numbers of the buses
+ * they provide.
+ *
+ * ptr: [nbuses][32] array of qemuDomainPCIBridges sorted by slot number
+ * bus: bus where to start checking
+ * start: the index the first bridge on that bus should have
+ * nbuses: number of buses in the ptr array
+ *
+ * Returns -1 if there is a mismatch
+ * The number of buses provided so far otherwise.
+ */
+static int
+qemuDomainVerifyPCIBridgesRecursive(qemuDomainPCIBridge **ptr,
+                                    unsigned int bus,
+                                    unsigned int start,
+                                    unsigned int nbuses)
+{
+    int i, idx;
+    int cur = start;
+
+    if (bus >= nbuses) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("More bridges than buses"));
+        return -1;
+    }
+    for (i = 0; i < QEMU_PCI_ADDRESS_LAST_SLOT; i++) {
+        idx = ptr[bus][i].idx;
+        /* no more bridges on this bus? */
+        if (!idx)
+            return cur;
+        if (idx != cur) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("PCI bridge index %d doesn't match"
+                             " expected index %d"), idx, cur);
+            return -1;
+        }
+        cur++;
+        if ((cur = qemuDomainVerifyPCIBridgesRecursive(ptr, idx, cur,
+                                                       nbuses)) < 0)
+            return -1;
+    }
+    return cur;
+}
+
+
+/*
+ * Verify PCI bridge topology
+ */
+static int
+qemuDomainVerifyPCIBridges(virDomainDefPtr def,
+                           unsigned int nbuses)
+{
+    qemuDomainPCIBridge **ptr;
+    int i, j, ret = -1;
+    size_t tmp = 0;
+    int rv;
+
+    if (VIR_ALLOC_N(ptr, nbuses) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    for (i = 0; i < nbuses; i++) {
+        if (VIR_ALLOC_N(ptr[i], QEMU_PCI_ADDRESS_LAST_SLOT) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
+    }
+
+    for (i = 0; i < def->ncontrollers; i++) {
+        /* go through all PCI bridges defined in the domain */
+        virDomainControllerDefPtr cdef = def->controllers[i];
+        if (cdef->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI_BRIDGE) {
+            unsigned int bus = cdef->info.addr.pci.bus;
+            unsigned int slot = cdef->info.addr.pci.slot;
+            qemuDomainPCIBridge br = {
+                .slot = slot,
+                .idx = cdef->idx
+            };
+
+            if (bus >= nbuses) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               "%s",
+                               _("bridge is on a non-existent bus"));
+                goto cleanup;
+            }
+
+            /* sort PCI bridges by slot number */
+            for (j = 0; j < QEMU_PCI_ADDRESS_LAST_SLOT; j++) {
+                if (!ptr[bus][j].idx || ptr[bus][j].slot > slot)
+                    break;
+            }
+            ignore_value(VIR_INSERT_ELEMENT_INPLACE(ptr[bus], j, tmp, br));
+        }
+    }
+
+    rv = qemuDomainVerifyPCIBridgesRecursive(ptr, 0, 1, nbuses);
+    if (rv == nbuses) {
+        ret = 0;
+    } else if (rv > 0) {
+        virReportError(VIR_ERR_XML_ERROR, _("not enough PCI bridges for %u"
+                                            " buses"), nbuses);
+    }
+cleanup:
+    for (i = 0; i < nbuses; i++)
+        VIR_FREE(ptr[i]);
+    VIR_FREE(ptr);
+    return ret;
+}
+
+
 int
 qemuDomainAssignPCIAddresses(virDomainDefPtr def,
                              virQEMUCapsPtr qemuCaps,
@@ -1321,11 +1482,20 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
     qemuDomainObjPrivatePtr priv = NULL;
 
     if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
-        if (!(addrs = qemuDomainPCIAddressSetCreate(def)))
+        if (!(addrs = qemuDomainPCIAddressSetCreate(def, 1)))
             goto cleanup;
 
         if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
             goto cleanup;
+
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
+            if (qemuDomainVerifyPCIBridges(def, addrs->maxbus + 1) < 0)
+                goto cleanup;
+        } else if (addrs->maxbus) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Only PCI bus 0 is available"));
+            goto cleanup;
+        }
     }
 
     if (obj && obj->privateData) {
@@ -1366,15 +1536,23 @@ int qemuDomainAssignAddresses(virDomainDefPtr def,
     return qemuDomainAssignPCIAddresses(def, qemuCaps, obj);
 }
 
-qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def)
+qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
+                                                         unsigned int nbuses)
 {
     qemuDomainPCIAddressSetPtr addrs;
+    int i;
 
     if (VIR_ALLOC(addrs) < 0)
         goto no_memory;
 
-    if (VIR_ALLOC_N(addrs->used, 1) < 0)
+    if (VIR_ALLOC_N(addrs->used, nbuses) < 0)
         goto no_memory;
+    addrs->nbuses = nbuses;
+
+    /* reserve slot 0 in every bus - it's used by the host bridge on bus 0
+     * and unusable on PCI bridges */
+    for (i = 0; i < nbuses; i++)
+        addrs->used[i][0] = 0xFF;
 
     if (virDomainDeviceInfoIterate(def, qemuCollectPCIAddress, addrs) < 0)
         goto error;
@@ -1409,6 +1587,9 @@ int 
qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
 {
     char *str;
 
+    if (qemuPCIAddressSetGrow(addrs, addr) < 0)
+        return -1;
+
     if (qemuPCIAddressCheck(addrs, addr) < 0)
         return -1;
 
@@ -1439,6 +1620,9 @@ int 
qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
 {
     char *str;
 
+    if (qemuPCIAddressSetGrow(addrs, addr) < 0)
+        return -1;
+
     if (qemuPCIAddressCheck(addrs, addr) < 0)
         return -1;
 
@@ -1524,7 +1708,9 @@ 
qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs,
     tmp_addr.slot++;
     for (i = 0; i < QEMU_PCI_ADDRESS_LAST_SLOT; i++, tmp_addr.slot++) {
         if (QEMU_PCI_ADDRESS_LAST_SLOT <= tmp_addr.slot) {
-            tmp_addr.slot = 0;
+            /* slot 0 is unusable */
+            tmp_addr.slot = 1;
+            i++;
         }
 
         if (!(addr = qemuPCIAddressAsString(&tmp_addr)))
@@ -1620,11 +1806,6 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
     unsigned int *func = &tmp_addr.function;
 
 
-    /* Reserve slot 0 for the host bridge */
-    memset(&tmp_addr, 0, sizeof(tmp_addr));
-    if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr) < 0)
-        goto error;
-
     /* Verify that first IDE and USB controllers (if any) is on the PIIX3, fn 
1 */
     for (i = 0; i < def->ncontrollers ; i++) {
         /* First IDE controller lives on the PIIX3 at slot=1, function=1 */
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 17687f4..56da69d 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -196,7 +196,8 @@ int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def,
 int qemuDomainAssignPCIAddresses(virDomainDefPtr def,
                                  virQEMUCapsPtr qemuCaps,
                                  virDomainObjPtr obj);
-qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def);
+qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
+                                                         unsigned int nbuses);
 int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
                                     virDevicePCIAddressPtr addr);
 int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
-- 
1.8.1.5

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to