Add support for IOV interconnect by provding ops for map/unmap and match interconnect. Note that the xarray is populated with entries of type struct range. The range struct contains the start and end address of the memory region.
Cc: Jason Gunthorpe <[email protected]> Cc: Christian Koenig <[email protected]> Cc: Sumit Semwal <[email protected]> Cc: Thomas Hellström <[email protected]> Cc: Simona Vetter <[email protected]> Signed-off-by: Vivek Kasireddy <[email protected]> --- drivers/vfio/pci/vfio_pci_dmabuf.c | 141 ++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 1 deletion(-) diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c index eaba010777f3..c45c1a7923f8 100644 --- a/drivers/vfio/pci/vfio_pci_dmabuf.c +++ b/drivers/vfio/pci/vfio_pci_dmabuf.c @@ -4,6 +4,7 @@ #include <linux/dma-buf.h> #include <linux/pci-p2pdma.h> #include <linux/dma-resv.h> +#include <linux/range.h> #include "vfio_pci_priv.h" @@ -16,15 +17,138 @@ struct vfio_pci_dma_buf { size_t size; struct phys_vec *phys_vec; struct p2pdma_provider *provider; + struct dma_buf_iov_interconnect *iov_ic; u32 nr_ranges; u8 revoked : 1; }; +static int +vfio_pci_create_iov_match(struct vfio_pci_dma_buf *priv, + struct vfio_device_feature_dma_buf *dma_buf) +{ + struct dma_buf_iov_interconnect *iov_ic; + + iov_ic = kzalloc(sizeof(*iov_ic), GFP_KERNEL); + if (!iov_ic) + return -ENOMEM; + + iov_ic->base.type = DMA_BUF_INTERCONNECT_IOV; + iov_ic->pdev = priv->vdev->pdev; + iov_ic->bar = dma_buf->region_index; + + priv->iov_ic = iov_ic; + return 0; +} + +static int vfio_pci_map_iov_interconnect(struct vfio_pci_dma_buf *priv, + struct xarray *ranges) +{ + struct phys_vec *phys_vec = priv->phys_vec; + struct range *range; + unsigned long i; + void *entry; + int ret; + + range = kmalloc_array(priv->nr_ranges, sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + + for (i = 0; i < priv->nr_ranges; i++) { + entry = &range[i]; + range[i].start = phys_vec[i].paddr; + range[i].end = phys_vec[i].paddr + phys_vec[i].len - 1; + + entry = xa_store(ranges, i, entry, GFP_KERNEL); + if (xa_is_err(entry)) { + ret = xa_err(entry); + goto err_free_range; + } + } + return 0; + +err_free_range: + kfree(range); + return ret; +} + +static int vfio_pci_map_interconnect(struct dma_buf_attachment *attachment, + struct dma_buf_ranges *ranges) +{ + enum dma_buf_interconnect_type type = attachment->interconnect.type; + struct vfio_pci_dma_buf *priv = attachment->dmabuf->priv; + int ret = -EINVAL; + + ranges->nranges = priv->nr_ranges; + + if (type == DMA_BUF_INTERCONNECT_IOV) + ret = vfio_pci_map_iov_interconnect(priv, &ranges->ranges); + return ret; +} + +static void vfio_pci_unmap_interconnect(struct dma_buf_attachment *attachment, + struct dma_buf_ranges *ranges) +{ + void *entry; + + entry = xa_load(&ranges->ranges, 0); + kfree(entry); +} + +static bool +vfio_pci_match_iov_interconnect(const struct dma_buf_interconnect *exp, + const struct dma_buf_interconnect *imp) +{ + const struct dma_buf_iov_interconnect *exp_ic = + container_of(exp, struct dma_buf_iov_interconnect, base); + const struct dma_buf_iov_interconnect *imp_ic = + container_of(imp, struct dma_buf_iov_interconnect, base); + + return imp_ic->pdev == pci_physfn(exp_ic->pdev) && + imp_ic->bar == exp_ic->bar; +} + +static bool +vfio_pci_match_interconnect(const struct dma_buf_interconnect *exp, + const struct dma_buf_interconnect *imp) +{ + enum dma_buf_interconnect_type type = exp->type; + + switch (type) { + case DMA_BUF_INTERCONNECT_IOV: + return vfio_pci_match_iov_interconnect(exp, imp); + default: + return false; + } +} + +static bool +vfio_pci_match_interconnects(struct vfio_pci_dma_buf *priv, + struct dma_buf_attachment *attachment) +{ + const struct dma_buf_attach_ops *aops = attachment->importer_ops; + struct pci_dev *pdev = priv->vdev->pdev; + unsigned int bar = priv->iov_ic->bar; + const struct dma_buf_interconnect_match supports_ics[] = { + CREATE_IOV_INTERCONNECT(pdev, bar), + }; + + if (attachment->allow_ic) { + if (aops->supports_interconnects(attachment, supports_ics, + ARRAY_SIZE(supports_ics))) + return true; + } + return false; +} + static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf, struct dma_buf_attachment *attachment) { struct vfio_pci_dma_buf *priv = dmabuf->priv; + if (vfio_pci_match_interconnects(priv, attachment)) { + return 0; + } + if (!attachment->peer2peer) return -EOPNOTSUPP; @@ -189,6 +313,7 @@ vfio_pci_dma_buf_map(struct dma_buf_attachment *attachment, return ERR_PTR(ret); } + static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment, struct sg_table *sgt, enum dma_data_direction dir) @@ -228,15 +353,23 @@ static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf) vfio_device_put_registration(&priv->vdev->vdev); } kfree(priv->phys_vec); + kfree(priv->iov_ic); kfree(priv); } +static const struct dma_buf_interconnect_ops vfio_pci_interconnect_ops = { + .match_interconnect = vfio_pci_match_interconnect, + .map_interconnect = vfio_pci_map_interconnect, + .unmap_interconnect = vfio_pci_unmap_interconnect, +}; + static const struct dma_buf_ops vfio_pci_dmabuf_ops = { .attach = vfio_pci_dma_buf_attach, .detach = vfio_pci_dma_buf_detach, .map_dma_buf = vfio_pci_dma_buf_map, .release = vfio_pci_dma_buf_release, .unmap_dma_buf = vfio_pci_dma_buf_unmap, + .interconnect_ops = &vfio_pci_interconnect_ops, }; static void dma_ranges_to_p2p_phys(struct vfio_pci_dma_buf *priv, @@ -365,6 +498,10 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, goto err_free_phys; } + ret = vfio_pci_create_iov_match(priv, &get_dma_buf); + if (ret) + goto err_dev_put; + exp_info.ops = &vfio_pci_dmabuf_ops; exp_info.size = priv->size; exp_info.flags = get_dma_buf.open_flags; @@ -373,7 +510,7 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, priv->dmabuf = dma_buf_export(&exp_info); if (IS_ERR(priv->dmabuf)) { ret = PTR_ERR(priv->dmabuf); - goto err_dev_put; + goto err_free_iov; } /* dma_buf_put() now frees priv */ @@ -391,6 +528,8 @@ int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags, */ return dma_buf_fd(priv->dmabuf, get_dma_buf.open_flags); +err_free_iov: + kfree(priv->iov_ic); err_dev_put: vfio_device_put_registration(&vdev->vdev); err_free_phys: -- 2.50.1
