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