[libvirt] [PATCH v3 5/5] qemu: auto-add bridges and allow using them
Eric Blake
eblake at redhat.com
Mon Apr 22 22:24:29 UTC 2013
On 04/22/2013 12:43 PM, Ján Tomko wrote:
> 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.
> ---
In addition to Laine's comments,
> + virBitmapPtr bitmap = NULL;
>
> /* verify init path for container based domains */
> if (STREQ(def->os.type, "exe") && !def->os.init) {
> @@ -2653,11 +2656,30 @@ virDomainDefPostParseInternal(virDomainDefPtr def,
> }
> }
>
> - return 0;
> + /* Verify that PCI controller indexes are unique */
> + bitmap = virBitmapNew(def->ncontrollers);
This limits the bitmap to the number of controllers that I passed in,
but your commit message makes it sound like I can pass in a controller
for index 1 and index 2 while letting the code auto-insert the
controller for index 0. If that's true, then...
> + for (i = 0; i < def->ncontrollers; i++) {
> + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
> + ignore_value(virBitmapGetBit(bitmap, def->controllers[i]->idx, &b));
...attempting to get bit 2 (based on the explicit 2 in
def->controllers[i]->idx) will fail as out-of-range,...
> + if (b) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("Multiple PCI controllers with index %d"),
> + def->controllers[i]->idx);
> + goto cleanup;
> + }
> + ignore_value(virBitmapSetBit(bitmap, def->controllers[i]->idx));
> + }
> + }
...and the attempt to set will also fail. Which means that a similar
example of passing in two controllers that both try to use index 2, and
let 0 and 1 be auto-populated, won't detect the collision. Do we know
what the maximum index will be? Is it time to add a growable bitmap?
Should we separate this duplicate detection code into a separate patch?
> +/* Ensure addr fits in the address set, by expanding it if needed.
> + * Return value:
> + * -1 = OOM
> + * 0 = no action performed
> + * >0 = number of buses added
> + */
> +static int
> +qemuDomainPCIAddressSetGrow(qemuDomainPCIAddressSetPtr addrs,
> + virDevicePCIAddressPtr addr)
Indentation is off.
> @@ -1326,15 +1369,54 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def,
> qemuDomainObjPrivatePtr priv = NULL;
>
> if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
> + int max_idx = 0;
> int nbuses = 0;
> int i;
> + int rv;
>
> for (i = 0; i < def->ncontrollers; i++) {
> - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI)
> + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
> + if (def->controllers[i]->idx > max_idx)
> + max_idx = def->controllers[i]->idx;
> nbuses++;
> + }
> + }
This looks like a more accurate determination of the max bus number;
should you move the duplicate detection here instead?
> +
> + if (nbuses > 0 && max_idx >= nbuses)
> + nbuses = max_idx + 1;
You change nbuses, but only if it is already > 1, but then...
> +
> + 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 (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;
> + }
> + nbuses = addrs->nbuses;
> + qemuDomainPCIAddressSetFree(addrs);
> + addrs = NULL;
> +
> + } else if (nbuses > 1) {
...read nbuses if the capability is not present. Would it be any
simpler to just change this to 'else if (max_idx > 0)'?
> + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
> + _("PCI bridges are not supported "
> + "by this QEMU binary"));
> + goto cleanup;
> }
>
> - if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses)))
> + if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, false)))
> goto cleanup;
>
> if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0)
> @@ -1519,41 +1609,58 @@ void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs)
> VIR_FREE(addrs);
> }
>
> -
> static int
I think we've been settling on two blank lines between functions lately,
although I'm not bothered if you leave this change in.
> qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs,
> virDevicePCIAddressPtr next_addr)
> {
> - virDevicePCIAddress tmp_addr = addrs->lastaddr;
> - int i;
> - char *addr;
> + virDevicePCIAddress a = addrs->lastaddr;
>
> - 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;
> - }
> + if (addrs->nbuses == 0) {
> + virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses available"));
> + return -1;
> + }
addrs->nbuses should always be >= 1, now that we allocate it, right? Is
it possible to hit this error?
>
> - if (!(addr = qemuPCIAddressAsString(&tmp_addr)))
> - return -1;
> + /* Start the search at the last used bus and slot */
> + for (a.slot++; a.bus < addrs->nbuses; a.bus++) {
> + for ( ; a.slot < QEMU_PCI_ADDRESS_SLOT_LAST; a.slot++) {
> + if (!qemuDomainPCIAddressSlotInUse(addrs, &a))
> + goto success;
>
> - if (qemuDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
> - VIR_DEBUG("PCI addr %s already in use", addr);
> - VIR_FREE(addr);
> - continue;
> + VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
> + a.domain, a.bus, a.slot);
> }
> + a.slot = 1;
> + }
>
> - VIR_DEBUG("Found free PCI addr %s", addr);
> - VIR_FREE(addr);
> + /* There were no free slots after the last used one */
> + if (addrs->dryRun) {
> + /* a is already set to the first new bus and slot 1 */
> + if (qemuDomainPCIAddressSetGrow(addrs, &a) < 0)
> + return -1;
> + goto success;
> + } else {
> + /* Check the buses from 0 up to the last used one */
> + for (a.bus = 0; a.bus <= addrs->lastaddr.bus; a.bus++) {
> + for (a.slot = 1; a.slot < QEMU_PCI_ADDRESS_SLOT_LAST; a.slot++) {
> + if (!qemuDomainPCIAddressSlotInUse(addrs, &a))
> + goto success;
>
> - addrs->lastaddr = tmp_addr;
> - *next_addr = tmp_addr;
> - return 0;
> + VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
> + a.domain, a.bus, a.slot);
> + }
> +
> + }
> }
This wraparound logic was definitely easier to read than the v2 attempt.
>
> virReportError(VIR_ERR_INTERNAL_ERROR,
> "%s", _("No more available PCI addresses"));
> return -1;
> +
> +success:
> + VIR_DEBUG("Found free PCI slot %.4x:%.2x:%.2x",
> + a.domain, a.bus, a.slot);
> + *next_addr = a;
> + return 0;
> }
>
> int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs,
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 621 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20130422/1d02d807/attachment-0001.sig>
More information about the libvir-list
mailing list