[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