|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v3 06/11] vpci: Hide legacy capability when it fails to initialize
On 2025/5/7 00:00, Roger Pau Monné wrote:
> On Mon, Apr 21, 2025 at 02:18:58PM +0800, Jiqian Chen wrote:
>> When vpci fails to initialize a legacy capability of device, it just
>> return error instead of catching and processing exception. That makes
>> the entire device unusable.
>
> I think "catching and processing exception" is a weird terminology to
> use when writing C. It's IMo more accurate to use:
>
> "When vpci fails to initialize a legacy capability of device, it just
> returns an error and vPCI gets disabled for the whole device. That
> most likely renders the device unusable, plus possibly causing issues
> to Xen itself if guest attempts to program the native MSI or MSI-X
> capabilities if present."
Thanks, will change.
>
>> So, add new a function to hide legacy capability when initialization
>> fails. And remove the failed legacy capability from the vpci emulated
>> legacy capability list.
>>
>> Signed-off-by: Jiqian Chen <Jiqian.Chen@xxxxxxx>
>> ---
>> cc: "Roger Pau Monné" <roger.pau@xxxxxxxxxx>
>> ---
>> 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 adds a new helper function vpci_get_register() and uses it to
>> get
>> target handler and previous handler from vpci->handlers, then remove the
>> target.
>>
>> 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 | 133 +++++++++++++++++++++++++++++++++-------
>> 1 file changed, 112 insertions(+), 21 deletions(-)
>>
>> diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
>> index 5474b66668c1..f97c7cc460a0 100644
>> --- a/xen/drivers/vpci/vpci.c
>> +++ b/xen/drivers/vpci/vpci.c
>> @@ -35,6 +35,22 @@ struct vpci_register {
>> uint32_t rsvdz_mask;
>> };
>>
>> +static int vpci_register_cmp(const struct vpci_register *r1,
>> + const struct vpci_register *r2)
>> +{
>> + /* Return 0 if registers overlap. */
>> + if ( r1->offset < r2->offset + r2->size &&
>> + r2->offset < r1->offset + r1->size )
>> + return 0;
>> + if ( r1->offset < r2->offset )
>> + return -1;
>> + if ( r1->offset > r2->offset )
>> + return 1;
>> +
>> + ASSERT_UNREACHABLE();
>> + return 0;
>> +}
>> +
>> #ifdef __XEN__
>> extern vpci_capability_t *const __start_vpci_array[];
>> extern vpci_capability_t *const __end_vpci_array[];
>> @@ -83,7 +99,91 @@ static int assign_virtual_sbdf(struct pci_dev *pdev)
>>
>> #endif /* CONFIG_HAS_VPCI_GUEST_SUPPORT */
>>
>> -static int vpci_init_capabilities(struct pci_dev *pdev)
>> +static struct vpci_register *vpci_get_register(struct vpci *vpci,
>> + const unsigned int offset,
>> + const unsigned int size)
>
> We don't usually use const attributes for scalar function parameters.
>
>> +{
>> + const struct vpci_register r = { .offset = offset, .size = size };
>> + struct vpci_register *rm;
>> +
>> + ASSERT(spin_is_locked(&vpci->lock));
>> + list_for_each_entry ( rm, &vpci->handlers, node )
>> + {
>> + int cmp = vpci_register_cmp(&r, rm);
>> +
>> + if ( !cmp && rm->offset == offset && rm->size == size )
>> + return rm;
>> + if ( cmp <= 0 )
>> + break;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static struct vpci_register *vpci_get_previous_cap_register
>> + (struct vpci *vpci, const unsigned int offset)
>
> The style preference here would be:
>
> static struct vpci_register *vpci_get_previous_cap_register(
> struct vpci *vpci, unsigned int offset)
> {
> ...
>
>> +{
>> + uint32_t next;
>> + struct vpci_register *r;
>> +
>> + if ( offset < 0x40 )
>
> I would possibly add an ASSERT_UNREACHABLE() here, as attempting to
> pass an offset below 0x40 is a sign of a bug elsewhere?
Probably yes, I will add an ASSERT_UNREACHABLE() here.
>
>> + return NULL;
>> +
>> + r = vpci_get_register(vpci, PCI_CAPABILITY_LIST, 1);
>> + ASSERT(r);
>> +
>> + next = (uint32_t)(uintptr_t)r->private;
>> + while ( next >= 0x40 && next != offset )
>> + {
>> + r = vpci_get_register(vpci, next + PCI_CAP_LIST_NEXT, 1);
>> + ASSERT(r);
>> + next = (uint32_t)(uintptr_t)r->private;
>> + }
>> +
>> + if ( next < 0x40 )
>> + return NULL;
>> +
>> + return r;
>> +}
>> +
>> +static void vpci_capability_mask(struct pci_dev *pdev,
>
> This possibly needs to return an error code, as it can fail, and just
> adding ASSERTs all around seems a bit clumsy, plus we might really
> want to prevent assigning the device to the domain if
> vpci_capability_mask() fails for whatever reason.
Make sense. Will change to return error instead of ASSERT.
>
>> + const unsigned int cap)
>> +{
>> + const unsigned int offset = pci_find_cap_offset(pdev->sbdf, cap);
>> + struct vpci_register *prev_next_r, *next_r;
>> + struct vpci *vpci = pdev->vpci;
>> +
>> + spin_lock(&vpci->lock);
>> + next_r = vpci_get_register(vpci, offset + PCI_CAP_LIST_NEXT, 1);
>> + if ( !next_r )
>> + {
>> + spin_unlock(&vpci->lock);
>> + return;
>> + }
>> +
>> + prev_next_r = vpci_get_previous_cap_register(vpci, offset);
>> + ASSERT(prev_next_r);
>> +
>> + prev_next_r->private = next_r->private;
>> +
>> + if ( !is_hardware_domain(pdev->domain) )
>> + {
>> + struct vpci_register *id_r =
>> + vpci_get_register(vpci, offset + PCI_CAP_LIST_ID, 1);
>> +
>> + ASSERT(id_r);
>> + /* PCI_CAP_LIST_ID register of target capability */
>> + list_del(&id_r->node);
>> + xfree(id_r);
>
> You could use vpci_remove_register() here?
Right.
>
>> + }
>> +
>> + /* PCI_CAP_LIST_NEXT register of target capability */
>> + list_del(&next_r->node);
>> + spin_unlock(&vpci->lock);
>> + xfree(next_r);
>
> Here vpci_remove_register() could also be used, but it will involve
> searching again for the register to remove, which is a bit pointless.
Yes, so just keeping things here instead of calling vpci_remove_register()?
>
>> +}
>> +
>> +static void vpci_init_capabilities(struct pci_dev *pdev)
>> {
>> for ( unsigned int i = 0; i < NUM_VPCI_INIT; i++ )
>> {
>> @@ -107,10 +207,17 @@ static int vpci_init_capabilities(struct pci_dev *pdev)
>> rc = capability->init(pdev);
>>
>> if ( rc )
>> - return rc;
>> + {
>> + if ( capability->fini )
>> + capability->fini(pdev);
>> +
>> + printk(XENLOG_WARNING "%pd %pp: %s cap %u init fail rc=%d, mask
>> it\n",
>
> Best to split to next line:
>
> printk(XENLOG_WARNING
> "%pd %pp: %s cap %u init fail rc=%d, mask it\n",
>
> Thanks, Roger.
--
Best regards,
Jiqian Chen.
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |