[libvirt] [PATCH v2 10/11] qemu: auto-add bridges and allow using them

Ján Tomko jtomko at redhat.com
Wed Apr 17 19:00:32 UTC 2013


Add a "dry run" address allocation to figure out how many bridges
will be needed for all the devices without explicit addresses.

Auto-add just enough bridges to put all the devices on, or up to the
bridge with the largest specified index.
---
 src/conf/domain_conf.c   |   9 +--
 src/conf/domain_conf.h   |   5 ++
 src/libvirt_private.syms |   1 +
 src/qemu/qemu_command.c  | 177 +++++++++++++++++++++++++++++++++++++++--------
 src/qemu/qemu_command.h  |   4 +-
 5 files changed, 161 insertions(+), 35 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index a5764fb..68518a7 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -9762,10 +9762,11 @@ virDomainLookupVcpuPin(virDomainDefPtr def,
     return NULL;
 }
 
-static int virDomainDefMaybeAddController(virDomainDefPtr def,
-                                          int type,
-                                          int idx,
-                                          int model)
+int
+virDomainDefMaybeAddController(virDomainDefPtr def,
+                               int type,
+                               int idx,
+                               int model)
 {
     int i;
     virDomainControllerDefPtr cont;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index e2fd079..72603bf 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2548,6 +2548,11 @@ int virDomainObjListExport(virDomainObjListPtr doms,
 virDomainVcpuPinDefPtr virDomainLookupVcpuPin(virDomainDefPtr def,
                                               int vcpuid);
 
+int virDomainDefMaybeAddController(virDomainDefPtr def,
+                                   int type,
+                                   int idx,
+                                   int model);
+
 char *virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps);
 
 #endif /* __DOMAIN_CONF_H */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 30fdcd7..a749500 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -116,6 +116,7 @@ virDomainDefFree;
 virDomainDefGenSecurityLabelDef;
 virDomainDefGetDefaultEmulator;
 virDomainDefGetSecurityLabelDef;
+virDomainDefMaybeAddController;
 virDomainDefParseFile;
 virDomainDefParseNode;
 virDomainDefParseString;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index ce86eea..df0077a 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1195,6 +1195,9 @@ cleanup:
 typedef uint8_t qemuDomainPCIAddressBus[QEMU_PCI_ADDRESS_SLOT_LAST];
 struct _qemuDomainPCIAddressSet {
     qemuDomainPCIAddressBus *used;
+    size_t nbuses;        /* allocation of used */
+    bool dryRun;          /* on a dry run, new buses are auto-added
+                             and addresses aren't saved in device infos */
     virDevicePCIAddress lastaddr;
 };
 
@@ -1211,9 +1214,10 @@ static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UN
                        _("Only PCI domain 0 is available"));
         return false;
     }
-    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 false;
     }
     if (addr->function >= QEMU_PCI_ADDRESS_FUNCTION_LAST) {
@@ -1228,9 +1232,44 @@ static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UN
                        QEMU_PCI_ADDRESS_SLOT_LAST);
         return false;
     }
+    if (addr->slot == 0) {
+        if (addr->bus) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Slot 0 is unusable on PCI bridges"));
+            return false;
+        } else {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                          _("Slot 0 on bus 0 is reserved for the host bridge"));
+            return false;
+        }
+    }
     return true;
 }
 
+/* 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 +1307,9 @@ static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
         return 0;
     }
 
+    if (addrs->dryRun && qemuPCIAddressSetGrow(addrs, addr) < 0)
+        return -1;
+
     if (!qemuPCIAddressValidate(addrs, addr))
         return -1;
 
@@ -1321,7 +1363,39 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
     qemuDomainObjPrivatePtr priv = NULL;
 
     if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
-        if (!(addrs = qemuDomainPCIAddressSetCreate(def)))
+        int nbuses = 1;
+        int i;
+        int rv;
+
+        for (i = 0; i < def->ncontrollers; i++) {
+            if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
+                def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE)
+                nbuses++;
+        }
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
+            virDomainDeviceInfo info;
+            /* 1st pass to figure out how many PCI bridges we need */
+            if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, true)))
+                goto cleanup;
+            if (nbuses && qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
+                goto cleanup;
+            /* Reserve 1 extra slot for a (potential) bridge */
+            if (qemuDomainPCIAddressSetNextAddr(addrs, &info) < 0)
+                goto cleanup;
+
+            for (i = 1; i < addrs->nbuses; i++) {
+                if ((rv = virDomainDefMaybeAddController(
+                        def, VIR_DOMAIN_CONTROLLER_TYPE_PCI,
+                        i, VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE)) < 0)
+                    goto cleanup;
+                /* If we added a new bridge, we will need one more address */
+                if (rv > 0 && qemuDomainPCIAddressSetNextAddr(addrs, &info) < 0)
+                    goto cleanup;
+            }
+            qemuDomainPCIAddressSetFree(addrs);
+            addrs = NULL;
+        }
+        if (!(addrs = qemuDomainPCIAddressSetCreate(def, addrs->nbuses, false)))
             goto cleanup;
 
         if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
@@ -1366,15 +1440,25 @@ int qemuDomainAssignAddresses(virDomainDefPtr def,
     return qemuDomainAssignPCIAddresses(def, qemuCaps, obj);
 }
 
-qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def)
+qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
+                                                         unsigned int nbuses,
+                                                         bool dryRun)
 {
     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;
+    addrs->dryRun = dryRun;
+
+    /* 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;
@@ -1402,6 +1486,9 @@ int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
 {
     char *str;
 
+    if (addrs->dryRun && qemuPCIAddressSetGrow(addrs, addr) < 0)
+        return -1;
+
     if (!(str = qemuPCIAddressAsString(addr)))
         return -1;
 
@@ -1428,6 +1515,9 @@ int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
 {
     char *str;
 
+    if (addrs->dryRun && qemuPCIAddressSetGrow(addrs, addr) < 0)
+        return -1;
+
     if (!(str = qemuPCIAddressAsString(addr)))
         return -1;
 
@@ -1503,30 +1593,44 @@ qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs,
                                 virDevicePCIAddressPtr next_addr)
 {
     virDevicePCIAddress tmp_addr = addrs->lastaddr;
-    int i;
+    int i,j;
     char *addr;
 
     tmp_addr.slot++;
-    for (i = 0; i < QEMU_PCI_ADDRESS_SLOT_LAST; i++, tmp_addr.slot++) {
-        if (QEMU_PCI_ADDRESS_SLOT_LAST <= tmp_addr.slot) {
-            tmp_addr.slot = 0;
-        }
+    for (j = 0; j < addrs->nbuses; j++) {
+        for (i = 0; i < QEMU_PCI_ADDRESS_SLOT_LAST; i++, tmp_addr.slot++) {
+            if (QEMU_PCI_ADDRESS_SLOT_LAST <= tmp_addr.slot) {
+                /* slot 0 is unusable */
+                tmp_addr.slot = 1;
+                i++;
+            }
 
-        if (!(addr = qemuPCIAddressAsString(&tmp_addr)))
-            return -1;
+            if (!(addr = qemuPCIAddressAsString(&tmp_addr)))
+                return -1;
 
-        if (qemuDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
-            VIR_DEBUG("PCI addr %s already in use", addr);
-            VIR_FREE(addr);
-            continue;
-        }
+            if (qemuDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
+                VIR_DEBUG("PCI addr %s already in use", addr);
+                VIR_FREE(addr);
+                continue;
+            }
 
-        VIR_DEBUG("Found free PCI addr %s", addr);
-        VIR_FREE(addr);
+            VIR_DEBUG("Found free PCI addr %s", addr);
+            VIR_FREE(addr);
 
-        addrs->lastaddr = tmp_addr;
-        *next_addr = tmp_addr;
-        return 0;
+            addrs->lastaddr = tmp_addr;
+            *next_addr = tmp_addr;
+            return 0;
+        }
+        tmp_addr.bus++;
+        if (addrs->nbuses <= tmp_addr.bus) {
+            if (addrs->dryRun) {
+                if (qemuPCIAddressSetGrow(addrs, &tmp_addr) < 0)
+                    return -1;
+            } else {
+                tmp_addr.bus = 0;
+            }
+            tmp_addr.slot = 1;
+        }
     }
 
     virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -1544,8 +1648,10 @@ int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
     if (qemuDomainPCIAddressReserveSlot(addrs, &addr) < 0)
         return -1;
 
-    dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
-    dev->addr.pci = addr;
+    if (!addrs->dryRun) {
+        dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+        dev->addr.pci = addr;
+    }
 
     addrs->lastaddr = addr;
     return 0;
@@ -1605,11 +1711,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 */
@@ -1723,6 +1824,18 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
         }
     }
 
+    /* PCI controllers */
+    for (i = 0; i < def->ncontrollers; i++) {
+        if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
+            if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT)
+                continue;
+            if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+                continue;
+            if (qemuDomainPCIAddressSetNextAddr(addrs, &def->controllers[i]->info) < 0)
+                goto error;
+        }
+    }
+
     for (i = 0; i < def->nfss ; i++) {
         if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
             continue;
@@ -1762,6 +1875,10 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
 
     /* Device controllers (SCSI, USB, but not IDE, FDC or CCID) */
     for (i = 0; i < def->ncontrollers ; i++) {
+        /* PCI controllers have been dealt with earlier */
+        if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI)
+            continue;
+
         /* FDC lives behind the ISA bridge; CCID is a usb device */
         if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC ||
             def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID)
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 1789c20..5f04001 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -196,7 +196,9 @@ int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def,
 int qemuDomainAssignPCIAddresses(virDomainDefPtr def,
                                  virQEMUCapsPtr qemuCaps,
                                  virDomainObjPtr obj);
-qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def);
+qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
+                                                         unsigned int nbuses,
+                                                         bool dryRun);
 int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
                                     virDevicePCIAddressPtr addr);
 int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
-- 
1.8.1.5




More information about the libvir-list mailing list