The DMA FAULT region contains the fault ring buffer.
There is benefit to let the userspace mmap this area.
Expose this mmappable area through a sparse mmap entry
and implement the mmap operation.

Signed-off-by: Eric Auger <eric.au...@redhat.com>

---

v8 -> v9:
- remove unused index local variable in vfio_pci_fault_mmap
---
 drivers/vfio/pci/vfio_pci.c | 61 +++++++++++++++++++++++++++++++++++--
 1 file changed, 58 insertions(+), 3 deletions(-)

diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index ad3fe0ce2e64..a528b72b57d2 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -316,21 +316,75 @@ static void vfio_pci_dma_fault_release(struct 
vfio_pci_device *vdev,
        kfree(vdev->fault_pages);
 }
 
+static int vfio_pci_dma_fault_mmap(struct vfio_pci_device *vdev,
+                                  struct vfio_pci_region *region,
+                                  struct vm_area_struct *vma)
+{
+       u64 phys_len, req_len, pgoff, req_start;
+       unsigned long long addr;
+       unsigned int ret;
+
+       phys_len = region->size;
+
+       req_len = vma->vm_end - vma->vm_start;
+       pgoff = vma->vm_pgoff &
+               ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+       req_start = pgoff << PAGE_SHIFT;
+
+       /* only the second page of the producer fault region is mmappable */
+       if (req_start < PAGE_SIZE)
+               return -EINVAL;
+
+       if (req_start + req_len > phys_len)
+               return -EINVAL;
+
+       addr = virt_to_phys(vdev->fault_pages);
+       vma->vm_private_data = vdev;
+       vma->vm_pgoff = (addr >> PAGE_SHIFT) + pgoff;
+
+       ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                             req_len, vma->vm_page_prot);
+       return ret;
+}
+
 static int vfio_pci_dma_fault_add_capability(struct vfio_pci_device *vdev,
                                             struct vfio_pci_region *region,
                                             struct vfio_info_cap *caps)
 {
+       struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
        struct vfio_region_info_cap_fault cap = {
                .header.id = VFIO_REGION_INFO_CAP_DMA_FAULT,
                .header.version = 1,
                .version = 1,
        };
-       return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
+       size_t size = sizeof(*sparse) + sizeof(*sparse->areas);
+       int ret;
+
+       ret = vfio_info_add_capability(caps, &cap.header, sizeof(cap));
+       if (ret)
+               return ret;
+
+       sparse = kzalloc(size, GFP_KERNEL);
+       if (!sparse)
+               return -ENOMEM;
+
+       sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+       sparse->header.version = 1;
+       sparse->nr_areas = 1;
+       sparse->areas[0].offset = PAGE_SIZE;
+       sparse->areas[0].size = region->size - PAGE_SIZE;
+
+       ret = vfio_info_add_capability(caps, &sparse->header, size);
+       if (ret)
+               kfree(sparse);
+
+       return ret;
 }
 
 static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
        .rw             = vfio_pci_dma_fault_rw,
        .release        = vfio_pci_dma_fault_release,
+       .mmap           = vfio_pci_dma_fault_mmap,
        .add_capability = vfio_pci_dma_fault_add_capability,
 };
 
@@ -404,7 +458,8 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
                VFIO_REGION_TYPE_NESTED,
                VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT,
                &vfio_pci_dma_fault_regops, size,
-               VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE,
+               VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE |
+               VFIO_REGION_INFO_FLAG_MMAP,
                vdev->fault_pages);
        if (ret)
                goto out;
@@ -412,7 +467,7 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device 
*vdev)
        header = (struct vfio_region_dma_fault *)vdev->fault_pages;
        header->entry_size = sizeof(struct iommu_fault);
        header->nb_entries = DMA_FAULT_RING_LENGTH;
-       header->offset = sizeof(struct vfio_region_dma_fault);
+       header->offset = PAGE_SIZE;
 
        ret = iommu_register_device_fault_handler(&vdev->pdev->dev,
                                        vfio_pci_iommu_dev_fault_handler,
-- 
2.26.2

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to