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@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list