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/qemu/qemu_command.c | 138 ++++++++++++++++++++++++++++++++++++++---------- src/qemu/qemu_command.h | 3 +- 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7073844..19fdf39 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1197,6 +1197,8 @@ struct _qemuDomainPCIAddressSet { _qemuDomainPCIAddressBus *used; size_t nbuses; /* allocation of used */ unsigned int maxbus; /* maximum used bus number */ + bool dryRun; /* on a dry run, new buses are auto-added + and addresses aren't saved in device infos */ virDevicePCIAddress lastaddr; }; @@ -1308,7 +1310,7 @@ static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, return 0; } - if (qemuPCIAddressSetGrow(addrs, addr) < 0) + if (addrs->dryRun && qemuPCIAddressSetGrow(addrs, addr) < 0) return -1; if (qemuPCIAddressCheck(addrs, addr) < 0) @@ -1472,6 +1474,57 @@ cleanup: } +/* + * Add bridges from 1 to addrs->nbuses + * or the highest bridge index in def + */ +static int +qemuDomainMaybeAddPCIBridges(virDomainDefPtr def, + qemuDomainPCIAddressSetPtr addrs) +{ + virDomainControllerDefPtr cont = NULL; + int i; + int max = addrs->nbuses - 1; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI_BRIDGE) + if (def->controllers[i]->idx > max) + max = def->controllers[i]->idx; + } + + for (max++, i = 1; i < max; i++) { + if (virDomainControllerFind(def, + VIR_DOMAIN_CONTROLLER_TYPE_PCI_BRIDGE, + i) >= 0) + continue; + + if (VIR_ALLOC(cont) < 0) + goto no_memory; + + cont->type = VIR_DOMAIN_CONTROLLER_TYPE_PCI_BRIDGE; + cont->idx = i; + cont->model = -1; + + if (virDomainControllerInsert(def, cont) < 0) + goto no_memory; + + /* This might change addrs->nbuses */ + if (qemuDomainPCIAddressSetNextAddr(addrs, &cont->info) < 0) + goto cleanup; + + if (addrs->nbuses > max) + max = addrs->nbuses; + } + return max; + +no_memory: + virReportOOMError(); +cleanup: + VIR_FREE(cont); + return -1; +} + + int qemuDomainAssignPCIAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, @@ -1480,21 +1533,33 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, int ret = -1; qemuDomainPCIAddressSetPtr addrs = NULL; qemuDomainObjPrivatePtr priv = NULL; + int nbuses = 1; if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { - if (!(addrs = qemuDomainPCIAddressSetCreate(def, 1))) + 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, 1, true))) + goto cleanup; + /* Reserve 1 extra slot for a bridge */ + if (qemuDomainPCIAddressSetNextAddr(addrs, &info) < 0) + goto cleanup; + + nbuses = qemuDomainMaybeAddPCIBridges(def, addrs); + if (nbuses < 0) + goto cleanup; + qemuDomainPCIAddressSetFree(addrs); + addrs = NULL; + } + if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, false))) 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) + if (qemuDomainVerifyPCIBridges(def, addrs->nbuses) < 0) goto cleanup; - } else if (addrs->maxbus) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Only PCI bus 0 is available")); - goto cleanup; } } @@ -1537,7 +1602,8 @@ int qemuDomainAssignAddresses(virDomainDefPtr def, } qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def, - unsigned int nbuses) + unsigned int nbuses, + bool dryRun) { qemuDomainPCIAddressSetPtr addrs; int i; @@ -1548,6 +1614,7 @@ qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def, 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 */ @@ -1587,7 +1654,7 @@ int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, { char *str; - if (qemuPCIAddressSetGrow(addrs, addr) < 0) + if (addrs->dryRun && qemuPCIAddressSetGrow(addrs, addr) < 0) return -1; if (qemuPCIAddressCheck(addrs, addr) < 0) @@ -1620,7 +1687,7 @@ int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, { char *str; - if (qemuPCIAddressSetGrow(addrs, addr) < 0) + if (addrs->dryRun && qemuPCIAddressSetGrow(addrs, addr) < 0) return -1; if (qemuPCIAddressCheck(addrs, addr) < 0) @@ -1702,32 +1769,43 @@ 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_LAST_SLOT; i++, tmp_addr.slot++) { - if (QEMU_PCI_ADDRESS_LAST_SLOT <= tmp_addr.slot) { - /* slot 0 is unusable */ + for (j = 0; j < addrs->nbuses; j++, 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; - i++; } + for (i = 0; i < QEMU_PCI_ADDRESS_LAST_SLOT; i++, tmp_addr.slot++) { + if (QEMU_PCI_ADDRESS_LAST_SLOT <= 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 (qemuDomainPCIAddressCheckSlot(addrs, &tmp_addr) < 0) { - VIR_DEBUG("PCI addr %s already in use", addr); - VIR_FREE(addr); - continue; - } + if (qemuDomainPCIAddressCheckSlot(addrs, &tmp_addr) < 0) { + 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; + } } virReportError(VIR_ERR_INTERNAL_ERROR, @@ -1745,8 +1823,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; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 56da69d..0f8a248 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -197,7 +197,8 @@ int qemuDomainAssignPCIAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainObjPtr obj); qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def, - unsigned int nbuses); + unsigned int nbuses, + bool dryRun); 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