On Fri, Jul 04, 2025 at 03:07:59PM +0800, Jiqian Chen wrote:
> When vpci fails to initialize a extended capability of device, it
> just returns an error and vPCI gets disabled for the whole device.
> 
> So, add function to hide extended capability when initialization
> fails. And remove the failed extended capability handler from vpci
> extended capability list.
> 
> Signed-off-by: Jiqian Chen <jiqian.c...@amd.com>
> ---
> Comment left over for the situation that "capability in 0x100 can't be 
> removed case"
> from Jan in last version, need Roger's input.
> 
> Jan:
> Can we rely on OSes recognizing ID 0 as "just skip"?
> Since the size isn't encoded in the header, there might be issues lurking 
> here.

I think it's the best we can aim to do TBH, I don't see any other way
to workaround this.  Size is indeed not encoded, but the next
capability pointer doesn't need to know the size of the current
capability to fetch the next one.

> ---
> cc: "Roger Pau Monné" <roger....@citrix.com>
> cc: Andrew Cooper <andrew.coop...@citrix.com>
> cc: Anthony PERARD <anthony.per...@vates.tech>
> cc: Michal Orzel <michal.or...@amd.com>
> cc: Jan Beulich <jbeul...@suse.com>
> cc: Julien Grall <jul...@xen.org>
> cc: Stefano Stabellini <sstabell...@kernel.org>
> ---
> v6->v7 changes:
> * Change the pointer parameter of vpci_get_previous_ext_cap_register()
>   and vpci_ext_capability_hide() to be const.
> 
> v5->v6 changes:
> * Change to use for loop to compact code of 
> vpci_get_previous_ext_cap_register().
> * Rename parameter rm to r in vpci_ext_capability_hide().
> * Change comment to describ the case that hide capability of position
>   0x100U.
> 
> v4->v5 changes:
> * Modify the hex digits of PCI_EXT_CAP_NEXT_MASK and PCI_EXT_CAP_NEXT to be 
> low case.
> * Rename vpci_ext_capability_mask to vpci_ext_capability_hide.
> 
> v3->v4 changes:
> * Change definition of PCI_EXT_CAP_NEXT to be "#define 
> PCI_EXT_CAP_NEXT(header)
>   (MASK_EXTR(header, PCI_EXT_CAP_NEXT_MASK) & 0xFFCU)" to avoid redundancy.
> * Modify the commit message.
> * Change vpci_ext_capability_mask() to return error instead of using ASSERT.
> * Set the capability ID part to be zero when we need to hide the capability 
> of position 0x100U.
> * Add check "if ( !offset )" in vpci_ext_capability_mask().
> 
> v2->v3 changes:
> * Separated from the last version patch "vpci: Hide capability when it fails 
> to initialize".
> * Whole implementation changed because last version is wrong.
>   This version gets target handler and previous handler from vpci->handlers, 
> then remove the target.
> * Note: a case in function vpci_ext_capability_mask() needs to be discussed,
>   because it may change the offset of next capability when the offset of 
> target
>   capability is 0x100U(the first extended capability), my implementation is 
> just to
>   ignore and let hardware to handle the target capability.
> 
> v1->v2 changes:
> * Removed the "priorities" of initializing capabilities since it isn't used 
> anymore.
> * Added new function vpci_capability_mask() and vpci_ext_capability_mask() to
>   remove failed capability from list.
> * Called vpci_make_msix_hole() in the end of init_msix().
> 
> Best regards,
> Jiqian Chen.
> ---
>  xen/drivers/vpci/vpci.c    | 88 ++++++++++++++++++++++++++++++++++++++
>  xen/include/xen/pci_regs.h |  5 ++-
>  2 files changed, 92 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
> index a91c3d4a1415..8be4b53533a3 100644
> --- a/xen/drivers/vpci/vpci.c
> +++ b/xen/drivers/vpci/vpci.c
> @@ -165,6 +165,92 @@ static int vpci_capability_hide(const struct pci_dev 
> *pdev, unsigned int cap)
>      return 0;
>  }
>  
> +static struct vpci_register *vpci_get_previous_ext_cap_register(
> +    const struct vpci *vpci, unsigned int offset)
> +{
> +    unsigned int pos = PCI_CFG_SPACE_SIZE;
> +    struct vpci_register *r;
> +
> +    if ( offset <= PCI_CFG_SPACE_SIZE )
> +    {
> +        ASSERT_UNREACHABLE();
> +        return NULL;
> +    }
> +
> +    for ( r = vpci_get_register(vpci, pos, 4); r;
> +          r = pos > PCI_CFG_SPACE_SIZE ? vpci_get_register(vpci, pos, 4)
> +                                       : NULL )
> +    {
> +        uint32_t header = (uint32_t)(uintptr_t)r->private;
> +
> +        ASSERT(header == (uintptr_t)r->private);
> +
> +        pos = PCI_EXT_CAP_NEXT(header);
> +        if ( pos == offset )
> +            break;
> +    }
> +
> +    return r;
> +}
> +
> +static int vpci_ext_capability_hide(
> +    const struct pci_dev *pdev, unsigned int cap)
> +{
> +    const unsigned int offset = pci_find_ext_capability(pdev->sbdf, cap);
> +    struct vpci_register *r, *prev_r;
> +    struct vpci *vpci = pdev->vpci;
> +    uint32_t header, pre_header;
> +
> +    if ( offset < PCI_CFG_SPACE_SIZE )
> +    {
> +        ASSERT_UNREACHABLE();
> +        return 0;
> +    }
> +
> +    spin_lock(&vpci->lock);
> +    r = vpci_get_register(vpci, offset, 4);
> +    if ( !r )
> +    {
> +        spin_unlock(&vpci->lock);
> +        return -ENODEV;
> +    }
> +
> +    header = (uint32_t)(uintptr_t)r->private;
> +    if ( offset == PCI_CFG_SPACE_SIZE )
> +    {
> +        if ( PCI_EXT_CAP_NEXT(header) <= PCI_CFG_SPACE_SIZE )
> +            r->private = (void *)(uintptr_t)0;
> +        else
> +            /*
> +             * The first extended capability (0x100) can not be removed from
> +             * the linked list, so instead mask its capability ID to return 0
> +             * and force OSes to skip it.

s/and force/hopefully forcing/.  I think that's the best Xen can
possibly do in this situation.  Unless someone has a more reliable
idea.

With that adjusted:

Reviewed-by: Roger Pau Monné <roger....@citrix.com>

Thanks.

Reply via email to