[libvirt] [PATCH v2 1/2] Tests for ACS in PCIe switches

Daniel Veillard veillard at redhat.com
Wed Dec 23 08:09:44 UTC 2009


On Tue, Dec 22, 2009 at 06:21:15PM +0100, Jiri Denemark wrote:
> New pciDeviceIsAssignable() function for checking whether a given PCI
> device can be assigned to a guest was added. Currently it only checks
> for ACS being enabled on all PCIe switches between root and the PCI
> device. In the future, it could be the right place to check whether a
> device is unbound or bound to a stub driver.
> 
> Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
> ---
>  src/libvirt_private.syms |    1 +
>  src/util/pci.c           |  151 ++++++++++++++++++++++++++++++++++++++++++++++
>  src/util/pci.h           |    4 +
>  3 files changed, 156 insertions(+), 0 deletions(-)
> 
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index f90f269..0f97e79 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -438,6 +438,7 @@ pciDeviceListGet;
>  pciDeviceListLock;
>  pciDeviceListUnlock;
>  pciDeviceListSteal;
> +pciDeviceIsAssignable;
>  
>  
>  # processinfo.h
> diff --git a/src/util/pci.c b/src/util/pci.c
> index 1e003c2..8cf66a2 100644
> --- a/src/util/pci.c
> +++ b/src/util/pci.c
> @@ -140,6 +140,28 @@ struct _pciDeviceList {
>  #define PCI_AF_CAP              0x3     /* Advanced features capabilities */
>  #define  PCI_AF_CAP_FLR         0x2     /* Function Level Reset */
>  
> +#define PCI_EXP_FLAGS           0x2
> +#define PCI_EXP_FLAGS_TYPE      0x00f0
> +#define PCI_EXP_TYPE_DOWNSTREAM 0x6
> +
> +#define PCI_EXT_CAP_BASE          0x100
> +#define PCI_EXT_CAP_LIMIT         0x1000
> +#define PCI_EXT_CAP_ID_MASK       0x0000ffff
> +#define PCI_EXT_CAP_OFFSET_SHIFT  20
> +#define PCI_EXT_CAP_OFFSET_MASK   0x00000ffc
> +
> +#define PCI_EXT_CAP_ID_ACS      0x000d
> +#define PCI_EXT_ACS_CTRL        0x06
> +
> +#define PCI_EXT_CAP_ACS_SV      0x01
> +#define PCI_EXT_CAP_ACS_RR      0x04
> +#define PCI_EXT_CAP_ACS_CR      0x08
> +#define PCI_EXT_CAP_ACS_UF      0x10
> +#define PCI_EXT_CAP_ACS_ENABLED (PCI_EXT_CAP_ACS_SV | \
> +                                 PCI_EXT_CAP_ACS_RR | \
> +                                 PCI_EXT_CAP_ACS_CR | \
> +                                 PCI_EXT_CAP_ACS_UF)

  As was commented by Matthias before IIRC, it would be nice to have
a more descriptive comment for the defines, but that's not a blocker.

>  static int
>  pciOpenConfig(pciDevice *dev)
>  {
> @@ -326,6 +348,30 @@ pciFindCapabilityOffset(pciDevice *dev, unsigned capability)
>      return 0;
>  }
>  
> +static unsigned int
> +pciFindExtendedCapabilityOffset(pciDevice *dev, unsigned capability)
> +{
> +    int ttl;
> +    unsigned int pos;
> +    uint32_t header;
> +
> +    /* minimum 8 bytes per capability */
> +    ttl = (PCI_EXT_CAP_LIMIT - PCI_EXT_CAP_BASE) / 8;
> +    pos = PCI_EXT_CAP_BASE;
> +
> +    while (ttl > 0 && pos >= PCI_EXT_CAP_BASE) {
> +        header = pciRead32(dev, pos);
> +
> +        if ((header & PCI_EXT_CAP_ID_MASK) == capability)
> +            return pos;
> +
> +        pos = (header >> PCI_EXT_CAP_OFFSET_SHIFT) & PCI_EXT_CAP_OFFSET_MASK;
> +        ttl--;
> +    }
> +
> +    return 0;
> +}
> +
>  static unsigned
>  pciDetectFunctionLevelReset(pciDevice *dev)
>  {
> @@ -1110,3 +1156,108 @@ cleanup:
>      VIR_FREE(pcidir);
>      return ret;
>  }
> +
> +static int
> +pciDeviceDownstreamLacksACS(virConnectPtr conn,
> +                            pciDevice *dev)
> +{
> +    uint16_t flags;
> +    uint16_t ctrl;
> +    unsigned int pos;
> +
> +    if (!dev->initted && pciInitDevice(conn, dev) < 0)
> +        return -1;
> +
> +    pos = dev->pcie_cap_pos;
> +    if (!pos || !pciRead16(dev, PCI_CLASS_DEVICE) == PCI_CLASS_BRIDGE_PCI)
> +        return 0;
> +
> +    flags = pciRead16(dev, pos + PCI_EXP_FLAGS);
> +    if (((flags & PCI_EXP_FLAGS_TYPE) >> 4) != PCI_EXP_TYPE_DOWNSTREAM)
> +        return 0;
> +
> +    pos = pciFindExtendedCapabilityOffset(dev, PCI_EXT_CAP_ID_ACS);
> +    if (!pos) {
> +        VIR_DEBUG("%s %s: downstream port lacks ACS", dev->id, dev->name);
> +        return 1;
> +    }
> +
> +    ctrl = pciRead16(dev, pos + PCI_EXT_ACS_CTRL);
> +    if ((ctrl & PCI_EXT_CAP_ACS_ENABLED) != PCI_EXT_CAP_ACS_ENABLED) {
> +        VIR_DEBUG("%s %s: downstream port has ACS disabled",
> +                  dev->id, dev->name);
> +        return 1;
> +    }
> +
> +    return 0;
> +}
> +
> +static int
> +pciDeviceIsBehindSwitchLackingACS(virConnectPtr conn,
> +                                  pciDevice *dev)
> +{
> +    pciDevice *parent;
> +
> +    if (!(parent = pciGetParentDevice(conn, dev))) {
> +        pciReportError(conn, VIR_ERR_NO_SUPPORT,
> +                       _("Failed to find parent device for %s"),
> +                       dev->name);
> +        return -1;
> +    }
> +
> +    /* XXX we should rather fail when we can't find device's parent and
> +     * stop the loop when we get to root instead of just stopping when no
> +     * parent can be found
> +     */
> +    do {
> +        pciDevice *tmp;
> +        int acs;
> +
> +        acs = pciDeviceDownstreamLacksACS(conn, parent);
> +
> +        if (acs) {
> +            pciFreeDevice(conn, parent);
> +            if (acs < 0)
> +                return -1;
> +            else
> +                return 1;
> +        }
> +
> +        tmp = parent;
> +        parent = pciGetParentDevice(conn, parent);
> +        pciFreeDevice(conn, tmp);
> +    } while (parent);
> +
> +    return 0;
> +}
> +
> +int pciDeviceIsAssignable(virConnectPtr conn,
> +                          pciDevice *dev,
> +                          int strict_acs_check)
> +{
> +    int ret;
> +
> +    /* XXX This could be a great place to actually check that a non-managed
> +     * device isn't in use, e.g. by checking that device is either un-bound
> +     * or bound to a stub driver.
> +     */
> +
> +    ret = pciDeviceIsBehindSwitchLackingACS(conn, dev);
> +    if (ret < 0)
> +        return 0;
> +
> +    if (ret) {
> +        if (!strict_acs_check) {
> +            VIR_DEBUG("%s %s: strict ACS check disabled; device assignment allowed",

  Okay, I was wondering why checking the PCI dev and then looking at
strict_acs_check, but allowing a debug is worth the extra computation.

> +                      dev->id, dev->name);
> +        } else {
> +            pciReportError(conn, VIR_ERR_NO_SUPPORT,
> +                           _("Device %s is behind a switch lacking ACS and "
> +                             "cannot be assigned"),
> +                           dev->name);
> +            return 0;
> +        }
> +    }
> +
> +    return 1;
> +}
> diff --git a/src/util/pci.h b/src/util/pci.h
> index 1f0b9d2..a1a19e7 100644
> --- a/src/util/pci.h
> +++ b/src/util/pci.h
> @@ -78,4 +78,8 @@ int pciDeviceFileIterate(virConnectPtr conn,
>                           pciDeviceFileActor actor,
>                           void *opaque);
>  
> +int pciDeviceIsAssignable(virConnectPtr conn,
> +                          pciDevice *dev,
> +                          int strict_acs_check);
> +
>  #endif /* __VIR_PCI_H__ */

  ACK

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