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