|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v3 10/11] vpci: Add initial support for virtual PCI bus topology
On Thu, Sep 30, 2021 at 10:52:22AM +0300, Oleksandr Andrushchenko wrote:
> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
>
> Assign SBDF to the PCI devices being passed through with bus 0.
> The resulting topology is where PCIe devices reside on the bus 0 of the
> root complex itself (embedded endpoints).
> This implementation is limited to 32 devices which are allowed on
> a single PCI bus.
>
> Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
>
> ---
> Since v2:
> - remove casts that are (a) malformed and (b) unnecessary
> - add new line for better readability
> - remove CONFIG_HAS_VPCI_GUEST_SUPPORT ifdef's as the relevant vPCI
> functions are now completely gated with this config
> - gate common code with CONFIG_HAS_VPCI_GUEST_SUPPORT
> New in v2
> ---
> xen/common/domain.c | 3 ++
> xen/drivers/passthrough/pci.c | 60 +++++++++++++++++++++++++++++++++++
> xen/drivers/vpci/vpci.c | 14 +++++++-
> xen/include/xen/pci.h | 22 +++++++++++++
> xen/include/xen/sched.h | 8 +++++
> 5 files changed, 106 insertions(+), 1 deletion(-)
>
> diff --git a/xen/common/domain.c b/xen/common/domain.c
> index 40d67ec34232..e0170087612d 100644
> --- a/xen/common/domain.c
> +++ b/xen/common/domain.c
> @@ -601,6 +601,9 @@ struct domain *domain_create(domid_t domid,
>
> #ifdef CONFIG_HAS_PCI
> INIT_LIST_HEAD(&d->pdev_list);
> +#ifdef CONFIG_HAS_VPCI_GUEST_SUPPORT
> + INIT_LIST_HEAD(&d->vdev_list);
> +#endif
> #endif
>
> /* All error paths can depend on the above setup. */
> diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
> index 805ab86ed555..5b963d75d1ba 100644
> --- a/xen/drivers/passthrough/pci.c
> +++ b/xen/drivers/passthrough/pci.c
> @@ -831,6 +831,66 @@ int pci_remove_device(u16 seg, u8 bus, u8 devfn)
> return ret;
> }
>
> +#ifdef CONFIG_HAS_VPCI_GUEST_SUPPORT
> +static struct vpci_dev *pci_find_virtual_device(const struct domain *d,
> + const struct pci_dev *pdev)
> +{
> + struct vpci_dev *vdev;
> +
> + list_for_each_entry ( vdev, &d->vdev_list, list )
> + if ( vdev->pdev == pdev )
> + return vdev;
> + return NULL;
> +}
> +
> +int pci_add_virtual_device(struct domain *d, const struct pci_dev *pdev)
> +{
> + struct vpci_dev *vdev;
> +
> + ASSERT(!pci_find_virtual_device(d, pdev));
> +
> + /* Each PCI bus supports 32 devices/slots at max. */
> + if ( d->vpci_dev_next > 31 )
> + return -ENOSPC;
> +
> + vdev = xzalloc(struct vpci_dev);
> + if ( !vdev )
> + return -ENOMEM;
> +
> + /* We emulate a single host bridge for the guest, so segment is always
> 0. */
> + vdev->seg = 0;
> +
> + /*
> + * The bus number is set to 0, so virtual devices are seen
> + * as embedded endpoints behind the root complex.
> + */
> + vdev->bus = 0;
> + vdev->devfn = PCI_DEVFN(d->vpci_dev_next++, 0);
This would likely be better as a bitmap where you set the bits of
in-use slots. Then you can use find_first_bit or similar to get a free
slot.
Long term you might want to allow the caller to provide a pre-selected
slot, as it's possible for users to request the device to appear at a
specific slot on the emulated bus.
> +
> + vdev->pdev = pdev;
> + vdev->domain = d;
> +
> + pcidevs_lock();
> + list_add_tail(&vdev->list, &d->vdev_list);
> + pcidevs_unlock();
> +
> + return 0;
> +}
> +
> +int pci_remove_virtual_device(struct domain *d, const struct pci_dev *pdev)
> +{
> + struct vpci_dev *vdev;
> +
> + pcidevs_lock();
> + vdev = pci_find_virtual_device(d, pdev);
> + if ( vdev )
> + list_del(&vdev->list);
> + pcidevs_unlock();
> + xfree(vdev);
> + return 0;
> +}
> +#endif /* CONFIG_HAS_VPCI_GUEST_SUPPORT */
> +
> /* Caller should hold the pcidevs_lock */
> static int deassign_device(struct domain *d, uint16_t seg, uint8_t bus,
> uint8_t devfn)
> diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
> index 702f7b5d5dda..d787f13e679e 100644
> --- a/xen/drivers/vpci/vpci.c
> +++ b/xen/drivers/vpci/vpci.c
> @@ -91,20 +91,32 @@ int __hwdom_init vpci_add_handlers(struct pci_dev *pdev)
> /* Notify vPCI that device is assigned to guest. */
> int vpci_assign_device(struct domain *d, const struct pci_dev *dev)
> {
> + int rc;
> +
> /* It only makes sense to assign for hwdom or guest domain. */
> if ( is_system_domain(d) || !has_vpci(d) )
> return 0;
>
> - return vpci_bar_add_handlers(d, dev);
> + rc = vpci_bar_add_handlers(d, dev);
> + if ( rc )
> + return rc;
> +
> + return pci_add_virtual_device(d, dev);
> }
>
> /* Notify vPCI that device is de-assigned from guest. */
> int vpci_deassign_device(struct domain *d, const struct pci_dev *dev)
> {
> + int rc;
> +
> /* It only makes sense to de-assign from hwdom or guest domain. */
> if ( is_system_domain(d) || !has_vpci(d) )
> return 0;
>
> + rc = pci_remove_virtual_device(d, dev);
> + if ( rc )
> + return rc;
> +
> return vpci_bar_remove_handlers(d, dev);
> }
> #endif /* CONFIG_HAS_VPCI_GUEST_SUPPORT */
> diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
> index 43b8a0817076..33033a3a8f8d 100644
> --- a/xen/include/xen/pci.h
> +++ b/xen/include/xen/pci.h
> @@ -137,6 +137,24 @@ struct pci_dev {
> struct vpci *vpci;
> };
>
> +#ifdef CONFIG_HAS_VPCI_GUEST_SUPPORT
> +struct vpci_dev {
> + struct list_head list;
> + /* Physical PCI device this virtual device is connected to. */
> + const struct pci_dev *pdev;
> + /* Virtual SBDF of the device. */
> + union {
> + struct {
> + uint8_t devfn;
> + uint8_t bus;
> + uint16_t seg;
> + };
> + pci_sbdf_t sbdf;
> + };
> + struct domain *domain;
> +};
> +#endif
I wonder whether this is strictly needed. Won't it be enough to store
the virtual (ie: guest) sbdf inside the existing vpci struct?
It would avoid the overhead of the translation you do from pdev ->
vdev, and there doesn't seem to be anything relevant stored in
vpci_dev apart from the virtual sbdf.
Thanks, Roger.
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |