[libvirt] [PATCH 3/7] qemu: eliminate almost-duplicate code in qemu_command.c

Doug Goldstein cardoe at gentoo.org
Mon Aug 5 00:09:33 UTC 2013


On Fri, Aug 2, 2013 at 11:55 AM, Laine Stump <laine at laine.org> wrote:
> * The functions qemuDomainPCIAddressReserveAddr and
> qemuDomainPCIAddressReserveSlot were very similar (and should have
> been more similar) and were about to get more code added to them which
> would create even more duplicated code, so this patch gives
> qemuDomainPCIAddressReserveAddr a "reserveEntireSlot" arg, then
> replaces the body of qemuDomainPCIAddressReserveSlot with a call to
> qemuDomainPCIAddressReserveAddr.
>
> You will notice that addrs->lastaddr was previously set in
> qemuDomainPCIAddressReserveAddr (but *not* set in
> qemuDomainPCIAddressReserveSlot). For consistency and cleanliness of
> code, that bit was removed and put into the one caller of
> qemuDomainPCIAddressReserveAddr (there is a similar place where the
> caller of qemuDomainPCIAddressReserveSlot sets lastaddr). This does
> guarantee identical functionality to pre-patch code, but in practice
> isn't really critical, because lastaddr is just keeping track of where
> to start when looking for a free slot - if it isn't updated, we will
> just start looking on a slot that's already occupied, then skip up to
> one that isn't.
>
> * qemuCollectPCIAddress was essentially doing the same thing as
> qemuDomainPCIAddressReserveAddr, but with some extra special case
> checking at the beginning. The duplicate code has been replaced with
> a call to qemuDomainPCIAddressReserveAddr. This required adding a
> "fromConfig" boolean, which is only used to change the log error
> code from VIR_ERR_INTERNAL_ERROR (when the address was
> auto-generated by libvirt) to VIR_ERR_XML_ERROR (when the address is
> coming from the config); without this differentiation, it would be
> difficult to tell if an error was caused by something wrong in
> libvirt's auto-allocate code or just bad config.
>
> * the bit of code in qemuDomainPCIAddressValidate that checks the
> connect type flags is going to be used in a couple more places where
> we don't need to also check the slot limits (because we're generating
> the slot number ourselves), so that has been pulled out into a
> separate qemuDomainPCIAddressFlagsCompatible function.
> ---
>  src/qemu/qemu_command.c | 232 ++++++++++++++++++++++++------------------------
>  src/qemu/qemu_command.h |   4 +-
>  2 files changed, 119 insertions(+), 117 deletions(-)
>
> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
> index 4345456..abc973a 100644
> --- a/src/qemu/qemu_command.c
> +++ b/src/qemu/qemu_command.c
> @@ -1434,9 +1434,53 @@ struct _qemuDomainPCIAddressSet {
>  };
>
>
> -/*
> - * Check that the PCI address is valid for use
> - * with the specified PCI address set.
> +static bool
> +qemuDomainPCIAddressFlagsCompatible(virDevicePCIAddressPtr addr,
> +                                    qemuDomainPCIConnectFlags busFlags,
> +                                    qemuDomainPCIConnectFlags devFlags,
> +                                    bool reportError)
> +{
> +    /* If this bus doesn't allow the type of connection (PCI
> +     * vs. PCIe) required by the device, or if the device requires
> +     * hot-plug and this bus doesn't have it, return false.
> +     */
> +    if (!(devFlags & busFlags & QEMU_PCI_CONNECT_TYPES_MASK)) {
> +        if (reportError) {
> +            if (devFlags & QEMU_PCI_CONNECT_TYPE_PCI) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("PCI bus %.4x:%.2x is not compatible with the "
> +                                 "device. Device requires a standard PCI slot, "
> +                                 "which is not provided by this bus"),
> +                               addr->domain, addr->bus);

So even though this patch has been ACK'd and committed. I really would
have liked to see some info about the device printed in the error
message because when you're defining a domain its really not clear
which device is the problem.

> +            } else {
> +                /* this should never happen. If it does, there is a
> +                 * bug in the code that sets the flag bits for devices.
> +                 */
> +                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                           _("The device information has no PCI "
> +                             "connection types listed"));
> +            }
> +        }
> +        return false;
> +    }
> +    if ((devFlags & QEMU_PCI_CONNECT_HOTPLUGGABLE) &&
> +        !(busFlags & QEMU_PCI_CONNECT_HOTPLUGGABLE)) {
> +        if (reportError) {
> +            virReportError(VIR_ERR_INTERNAL_ERROR,
> +                           _("PCI bus %.4x:%.2x is not compatible with the "
> +                             "device. Device requires hot-plug capability, "
> +                             "which is not provided by the bus"),
> +                           addr->domain, addr->bus);

Same as above, when you're defining the whole domain its really not
clear which device is the problem so provide some details on which one
it is.

> +        }
> +        return false;
> +    }
> +    return true;
> +}
> +
> +
> +/* Verify that the address is in bounds for the chosen bus, and
> + * that the bus is of the correct type for the device (via
> + * comparing the flags).
>   */
>  static bool
>  qemuDomainPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs,
> @@ -1466,24 +1510,9 @@ qemuDomainPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs,
>      /* assure that at least one of the requested connection types is
>       * provided by this bus
>       */
> -    if (!(flags & bus->flags & QEMU_PCI_CONNECT_TYPES_MASK)) {
> -        virReportError(VIR_ERR_XML_ERROR,
> -                       _("Invalid PCI address: The PCI controller "
> -                         "providing bus %04x doesn't support "
> -                         "connections appropriate for the device "
> -                         "(%x required vs. %x provided by bus)"),
> -                       addr->bus, flags & QEMU_PCI_CONNECT_TYPES_MASK,
> -                       bus->flags & QEMU_PCI_CONNECT_TYPES_MASK);
> -        return false;
> -    }
> -    /* make sure this bus allows hot-plug if the caller demands it */
> -    if ((flags & QEMU_PCI_CONNECT_HOTPLUGGABLE) &&
> -        !(bus->flags &  QEMU_PCI_CONNECT_HOTPLUGGABLE)) {
> -        virReportError(VIR_ERR_XML_ERROR,
> -                       _("Invalid PCI address: hot-pluggable slot requested, "
> -                         "but bus %04x doesn't support hot-plug"), addr->bus);
> +    if (!qemuDomainPCIAddressFlagsCompatible(addr, bus->flags, flags, true))
>          return false;
> -    }
> +
>      /* some "buses" are really just a single port */
>      if (bus->minSlot && addr->slot < bus->minSlot) {
>          virReportError(VIR_ERR_XML_ERROR,
> @@ -1597,10 +1626,10 @@ qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
>                        virDomainDeviceInfoPtr info,
>                        void *opaque)
>  {
> +    qemuDomainPCIAddressSetPtr addrs = opaque;
>      int ret = -1;
> -    char *str = NULL;
>      virDevicePCIAddressPtr addr = &info->addr.pci;
> -    qemuDomainPCIAddressSetPtr addrs = opaque;
> +    bool entireSlot;
>      /* FIXME: flags should be set according to the requirements of @device */
>      qemuDomainPCIConnectFlags flags = (QEMU_PCI_CONNECT_HOTPLUGGABLE |
>                                         QEMU_PCI_CONNECT_TYPE_PCI);
> @@ -1639,56 +1668,15 @@ qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
>              return 0;
>      }
>
> -    /* add an additional bus of the correct type if needed */
> -    if (addrs->dryRun &&
> -        qemuDomainPCIAddressSetGrow(addrs, addr, flags) < 0)
> -        return -1;
> -
> -    /* verify that the address is in bounds for the chosen bus, and
> -     * that the bus is of the correct type for the device (via
> -     * comparing the flags).
> -     */
> -    if (!qemuDomainPCIAddressValidate(addrs, addr, flags))
> -        return -1;
> -
> -    if (!(str = qemuDomainPCIAddressAsString(addr)))
> -        goto cleanup;
> +    entireSlot = (addr->function == 0 &&
> +                  addr->multi != VIR_DEVICE_ADDRESS_PCI_MULTI_ON);
>
> -    /* check if already in use */
> -    if (addrs->buses[addr->bus].slots[addr->slot] & (1 << addr->function)) {
> -        if (info->addr.pci.function != 0) {
> -            virReportError(VIR_ERR_XML_ERROR,
> -                           _("Attempted double use of PCI Address '%s' "
> -                             "(may need \"multifunction='on'\" for device on function 0)"),
> -                           str);
> -        } else {
> -            virReportError(VIR_ERR_XML_ERROR,
> -                           _("Attempted double use of PCI Address '%s'"), str);
> -        }
> +    if (qemuDomainPCIAddressReserveAddr(addrs, addr, flags,
> +                                        entireSlot, true) < 0)
>          goto cleanup;
> -    }
>
> -    /* mark as in use */
> -    if ((info->addr.pci.function == 0) &&
> -        (info->addr.pci.multi != VIR_DEVICE_ADDRESS_PCI_MULTI_ON)) {
> -        /* a function 0 w/o multifunction=on must reserve the entire slot */
> -        if (addrs->buses[addr->bus].slots[addr->slot]) {
> -            virReportError(VIR_ERR_XML_ERROR,
> -                           _("Attempted double use of PCI Address on slot '%s' "
> -                             "(need \"multifunction='off'\" for device "
> -                             "on function 0)"),
> -                           str);
> -            goto cleanup;
> -        }
> -        addrs->buses[addr->bus].slots[addr->slot] = 0xFF;
> -        VIR_DEBUG("Remembering PCI slot: %s (multifunction=off)", str);
> -    } else {
> -        VIR_DEBUG("Remembering PCI addr: %s", str);
> -        addrs->buses[addr->bus].slots[addr->slot] |= 1 << addr->function;
> -    }
>      ret = 0;
>  cleanup:
> -    VIR_FREE(str);
>      return ret;
>  }
>
> @@ -1871,46 +1859,73 @@ static bool qemuDomainPCIAddressSlotInUse(qemuDomainPCIAddressSetPtr addrs,
>  }
>
>
> +/*
> + * Reserve a slot (or just one function) for a device. If
> + * reserveEntireSlot is true, all functions for the slot are reserved,
> + * otherwise only one. If fromConfig is true, the address being
> + * requested came directly from the config and errors should be worded
> + * appropriately. If fromConfig is false, the address was
> + * automatically created by libvirt, so it is an internal error (not
> + * XML).
> + */
>  int
>  qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
>                                  virDevicePCIAddressPtr addr,
> -                                qemuDomainPCIConnectFlags flags)
> +                                qemuDomainPCIConnectFlags flags,
> +                                bool reserveEntireSlot,
> +                                bool fromConfig)
>  {
> -    char *str;
> +    int ret = -1;
> +    char *str = NULL;
>      qemuDomainPCIAddressBusPtr bus;
> -
> -    if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr, flags) < 0)
> -        return -1;
> +    virErrorNumber errType = (fromConfig
> +                              ? VIR_ERR_XML_ERROR : VIR_ERR_INTERNAL_ERROR);
>
>      if (!(str = qemuDomainPCIAddressAsString(addr)))
> -        return -1;
> +        goto cleanup;
>
> -    VIR_DEBUG("Reserving PCI addr %s", str);
> +    /* Add an extra bus if necessary */
> +    if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr, flags) < 0)
> +        goto cleanup;
> +    /* Check that the requested bus exists, is the correct type, and we
> +     * are asking for a valid slot
> +     */
> +    if (!qemuDomainPCIAddressValidate(addrs, addr, flags))
> +        goto cleanup;
>
>      bus = &addrs->buses[addr->bus];
> -    if ((bus->minSlot && addr->slot < bus->minSlot) ||
> -        addr->slot > bus->maxSlot) {
> -        virReportError(VIR_ERR_INTERNAL_ERROR,
> -                       _("Unable to reserve PCI address %s. "
> -                         "Slot %02x can't be used on bus %04x, "
> -                         "only slots %02zx - %02zx are permitted on this bus."),
> -                       str, addr->slot, addr->bus, bus->minSlot, bus->maxSlot);
> -    }
>
> -    if (bus->slots[addr->slot] & (1 << addr->function)) {
> -        virReportError(VIR_ERR_INTERNAL_ERROR,
> -                       _("unable to reserve PCI address %s already in use"), str);
> -        VIR_FREE(str);
> -        return -1;
> +    if (reserveEntireSlot) {
> +        if (bus->slots[addr->slot]) {
> +            virReportError(errType,
> +                           _("Attempted double use of PCI slot %s "
> +                             "(may need \"multifunction='on'\" for "
> +                             "device on function 0)"), str);
> +            goto cleanup;
> +        }
> +        bus->slots[addr->slot] = 0xFF; /* reserve all functions of slot */
> +        VIR_DEBUG("Reserving PCI slot %s (multifunction='off')", str);
> +    } else {
> +        if (bus->slots[addr->slot] & (1 << addr->function)) {
> +            if (addr->function == 0) {
> +                virReportError(errType,
> +                               _("Attempted double use of PCI Address %s"), str);
> +            } else {
> +                virReportError(errType,
> +                               _("Attempted double use of PCI Address %s "
> +                                 "(may need \"multifunction='on'\" "
> +                                 "for device on function 0)"), str);
> +            }
> +            goto cleanup;
> +        }
> +        bus->slots[addr->slot] |= (1 << addr->function);
> +        VIR_DEBUG("Reserving PCI address %s", str);
>      }
>
> +    ret = 0;
> +cleanup:
>      VIR_FREE(str);
> -
> -    addrs->lastaddr = *addr;
> -    addrs->lastaddr.function = 0;
> -    addrs->lastaddr.multi = 0;
> -    bus->slots[addr->slot] |= 1 << addr->function;
> -    return 0;
> +    return ret;
>  }
>
>
> @@ -1919,26 +1934,7 @@ qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
>                                  virDevicePCIAddressPtr addr,
>                                  qemuDomainPCIConnectFlags flags)
>  {
> -    char *str;
> -
> -    if (addrs->dryRun && qemuDomainPCIAddressSetGrow(addrs, addr, flags) < 0)
> -        return -1;
> -
> -    if (!(str = qemuDomainPCIAddressAsString(addr)))
> -        return -1;
> -
> -    VIR_DEBUG("Reserving PCI slot %s", str);
> -
> -    if (addrs->buses[addr->bus].slots[addr->slot]) {
> -        virReportError(VIR_ERR_INTERNAL_ERROR,
> -                       _("unable to reserve PCI slot %s"), str);
> -        VIR_FREE(str);
> -        return -1;
> -    }
> -
> -    VIR_FREE(str);
> -    addrs->buses[addr->bus].slots[addr->slot] = 0xFF;
> -    return 0;
> +    return qemuDomainPCIAddressReserveAddr(addrs, addr, flags, true, false);
>  }
>
>  int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs,
> @@ -2044,12 +2040,11 @@ qemuDomainPCIAddressGetNextSlot(qemuDomainPCIAddressSetPtr addrs,
>                  VIR_DEBUG("PCI slot %.4x:%.2x:%.2x already in use",
>                            a.domain, a.bus, a.slot);
>              }
> -
>          }
>      }
>
>      virReportError(VIR_ERR_INTERNAL_ERROR,
> -                   "%s", _("No more available PCI addresses"));
> +                   "%s", _("No more available PCI slots"));
>      return -1;
>
>  success:
> @@ -2409,9 +2404,14 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
>
>                  addr.bus = tmp_addr.bus;
>                  addr.slot = tmp_addr.slot;
> +
> +                addrs->lastaddr = addr;
> +                addrs->lastaddr.function = 0;
> +                addrs->lastaddr.multi = 0;
>              }
>              /* Finally we can reserve the slot+function */
> -            if (qemuDomainPCIAddressReserveAddr(addrs, &addr, flags) < 0)
> +            if (qemuDomainPCIAddressReserveAddr(addrs, &addr, flags,
> +                                                false, false) < 0)
>                  goto error;
>
>              def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
> diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
> index c9f1600..bf4953a 100644
> --- a/src/qemu/qemu_command.h
> +++ b/src/qemu/qemu_command.h
> @@ -253,7 +253,9 @@ int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs,
>                                      qemuDomainPCIConnectFlags flags);
>  int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs,
>                                      virDevicePCIAddressPtr addr,
> -                                    qemuDomainPCIConnectFlags flags);
> +                                    qemuDomainPCIConnectFlags flags,
> +                                    bool reserveEntireSlot,
> +                                    bool fromConfig);
>  int qemuDomainPCIAddressReserveNextSlot(qemuDomainPCIAddressSetPtr addrs,
>                                          virDomainDeviceInfoPtr dev,
>                                          qemuDomainPCIConnectFlags flags);
> --
> 1.7.11.7
>
> --
> libvir-list mailing list
> libvir-list at redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list



-- 
Doug Goldstein




More information about the libvir-list mailing list