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