[libvirt] [PATCH] pci: Save and restore each devices/functions behind the bus

Daniel Veillard veillard at redhat.com
Thu Sep 6 06:17:49 UTC 2012


On Mon, Sep 03, 2012 at 04:57:01PM +0800, Osier Yang wrote:
> Previously it refuses to do the secondary bus reset as long as
> there is(are) devices/functions behind the same bus, regardless
> of whether the devices/functions are being used or not. And it
> only save and restore the device itself's PCI config space.
> 
> But later it was changed to allow the secondary bus reset as long
> as the devices/functions behind the same bus are not being
> used. Unfortunately, it still just saves and restores the device
> itself's PCI config space. It means we will lose the PCI config
> space for the devices share same bus when doing passthrough.
> 
> Also, (hope my guess is right) as it assumes the secondary reset
> is allowed unless the device doesn't have devices/functions behind
> the same bus, so it only reads the bridge control register from the
> device, but not the parent.
> 
> This patch fixes the problem by finding out all the devices/functions
> behind the same bus of the device to be reset, and save/restore
> PCI config space for all of them. And read the bridge control register
> from the device's parent (bridge) before resetting.
> 
> * src/util/pci.c:
>   - New helper pciSharesBus to check if two devices share same bus.
> 
>   - New helper pciDevicesShareBus to return a list containg all of
>     the devices/functions which share same bus with the device
> 
>   - pciTrySecondaryBusReset: Save and restore PCI config space for
>     all the devices/functions behind the same bus; Read the bridge
>     control register from the device's parent instead before resetting.
> ---
>  src/util/pci.c |   80 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  1 files changed, 75 insertions(+), 5 deletions(-)
> 
> diff --git a/src/util/pci.c b/src/util/pci.c
> index 0742d07..1a9777a 100644
> --- a/src/util/pci.c
> +++ b/src/util/pci.c
> @@ -517,6 +517,39 @@ pciBusContainsActiveDevices(pciDevice *dev,
>      return active;
>  }
>  
> +/*
> + * Check if the @dev and @check share bus.
> + */
> +static int
> +pciSharesBus(pciDevice *dev, pciDevice *check, void *data ATTRIBUTE_UNUSED)
> +{
> +    if ((dev->domain == check->domain) &&
> +        (dev->bus == check->bus) &&
> +        (dev->slot == check->slot))
> +        return 1;
> +
> +    return 0;
> +}

  I'm a bit puzzled ... seems to me the function check if the devices
share the same slot on the same bus, not just sharing of the same bus

> +/*
> + * Return all the devices/functions share same bus with @dev
> + * as a list.
> + */
> +static pciDeviceList *
> +pciDevicesShareBus(pciDevice *dev)
> +{
> +    pciDevice *match = NULL;
> +    pciDeviceList *pcis = NULL;
> +
> +    if (!(pcis = pciDeviceListNew()))
> +        return NULL;
> +
> +    if (pciIterDevices(pciSharesBus, dev, &match, NULL))
> +        pciDeviceListAdd(pcis, match);
> +
> +    return pcis;
> +}
> +
>  /* Is @check the parent of @dev ? */
>  static int
>  pciIsParent(pciDevice *dev, pciDevice *check, void *data)
> @@ -604,6 +637,9 @@ pciTrySecondaryBusReset(pciDevice *dev,
>      uint8_t config_space[PCI_CONF_LEN];
>      uint16_t ctl;
>      int ret = -1;
> +    pciDeviceList *list = NULL;
> +    uint8_t (*config_spaces)[PCI_CONF_LEN];
> +    int i;
>  
>      /* Refuse to do a secondary bus reset if there are other
>       * devices/functions behind the bus are used by the host
> @@ -628,10 +664,7 @@ pciTrySecondaryBusReset(pciDevice *dev,
>  
>      VIR_DEBUG("%s %s: doing a secondary bus reset", dev->id, dev->name);
>  
> -    /* Save and restore the device's config space; we only do this
> -     * for the supplied device since we refuse to do a reset if there
> -     * are multiple devices/functions
> -     */
> +    /* Save the device's config space */
>      if (pciRead(dev, 0, config_space, PCI_CONF_LEN) < 0) {
>          virReportError(VIR_ERR_INTERNAL_ERROR,
>                         _("Failed to read PCI config space for %s"),
> @@ -639,10 +672,29 @@ pciTrySecondaryBusReset(pciDevice *dev,
>          goto out;
>      }
>  
> +    /* Save the config space of devices behind the same bus  */
> +    if ((list = pciDevicesShareBus(dev))) {
> +        if (VIR_ALLOC_N(config_spaces, list->count) < 0) {
> +            virReportOOMError();
> +            goto out;
> +        }
> +
> +        for (i = 0; i < list->count; i++) {
> +            pciDevice *pci = list->devs[i];
> +
> +            if (pciRead(pci, 0, config_spaces[i], PCI_CONF_LEN) < 0) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("Failed to read PCI config space for %s"),
> +                               pci->name);
> +                goto out;
> +            }
> +        }
> +    }
> +
>      /* Read the control register, set the reset flag, wait 200ms,
>       * unset the reset flag and wait 200ms.
>       */
> -    ctl = pciRead16(dev, PCI_BRIDGE_CONTROL);
> +    ctl = pciRead16(parent, PCI_BRIDGE_CONTROL);
>  
>      pciWrite16(parent, PCI_BRIDGE_CONTROL, ctl | PCI_BRIDGE_CTL_RESET);
>  
> @@ -652,14 +704,32 @@ pciTrySecondaryBusReset(pciDevice *dev,
>  
>      usleep(200 * 1000); /* sleep 200ms */
>  
> +    /* Restore the device's config space */
>      if (pciWrite(dev, 0, config_space, PCI_CONF_LEN) < 0) {
>          virReportError(VIR_ERR_INTERNAL_ERROR,
>                         _("Failed to restore PCI config space for %s"),
>                         dev->name);
>          goto out;
>      }
> +
> +    /* Restore the config space of devices behind the same bus */
> +    if (list) {
> +        for (i = 0; i < list->count; i++) {
> +            pciDevice *pci = list->devs[i];
> +
> +            if (pciWrite(pci, 0, config_spaces[i], PCI_CONF_LEN) < 0) {
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("Failed to restore PCI config space for %s"),
> +                               pci->name);
> +                goto out;
> +            }
> +        }
> +    }
> +
>      ret = 0;
>  out:
> +    pciDeviceListFree(list);
> +    VIR_FREE(config_spaces);
>      pciFreeDevice(parent);
>      return ret;
>  }

  Otherwise this seems to make sense to me, but it would be good
if someone else could review too before ACK'ing as this is rather
sensitive code

   ACK/2

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list