Add a new specific DMA_FAULT region aiming to exposed nested mode
translation faults. This region only is exposed if the device
is attached to a nested domain.
The region has a ring buffer that contains the actual fault
records plus a header allowing to handle it (tail/head indices,
max capacity, entry size). At the moment the region is dimensionned
for 512 fault records.
Signed-off-by: Eric Auger
---
v11 -> v12:
- set fault_pages to NULL after free
- check new_tail >= header->nb_entries (Zenghui)
v10 -> v11:
- rename vfio_pci_init_dma_fault_region into
vfio_pci_dma_fault_init
- free fault_pages in vfio_pci_dma_fault_release
- only register the region if the device is attached
to a nested domain
v8 -> v9:
- Use a single region instead of a prod/cons region
v4 -> v5
- check cons is not null in vfio_pci_check_cons_fault
v3 -> v4:
- use 2 separate regions, respectively in read and write modes
- add the version capability
---
drivers/vfio/pci/vfio_pci.c | 76 +
drivers/vfio/pci/vfio_pci_private.h | 6 +++
drivers/vfio/pci/vfio_pci_rdwr.c| 44 +
include/uapi/linux/vfio.h | 34 +
4 files changed, 160 insertions(+)
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e6190173482c..7546a81e7fb6 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -311,6 +311,78 @@ int vfio_pci_set_power_state(struct vfio_pci_device *vdev,
pci_power_t state)
return ret;
}
+static void vfio_pci_dma_fault_release(struct vfio_pci_device *vdev,
+ struct vfio_pci_region *region)
+{
+ kfree(vdev->fault_pages);
+}
+
+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_fault cap = {
+ .header.id = VFIO_REGION_INFO_CAP_DMA_FAULT,
+ .header.version = 1,
+ .version = 1,
+ };
+ return vfio_info_add_capability(caps, , sizeof(cap));
+}
+
+static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
+ .rw = vfio_pci_dma_fault_rw,
+ .release= vfio_pci_dma_fault_release,
+ .add_capability = vfio_pci_dma_fault_add_capability,
+};
+
+#define DMA_FAULT_RING_LENGTH 512
+
+static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
+{
+ struct vfio_region_dma_fault *header;
+ struct iommu_domain *domain;
+ size_t size;
+ bool nested;
+ int ret;
+
+ domain = iommu_get_domain_for_dev(>pdev->dev);
+ ret = iommu_domain_get_attr(domain, DOMAIN_ATTR_NESTING, );
+ if (ret || !nested)
+ return ret;
+
+ mutex_init(>fault_queue_lock);
+
+ /*
+* We provision 1 page for the header and space for
+* DMA_FAULT_RING_LENGTH fault records in the ring buffer.
+*/
+ size = ALIGN(sizeof(struct iommu_fault) *
+DMA_FAULT_RING_LENGTH, PAGE_SIZE) + PAGE_SIZE;
+
+ vdev->fault_pages = kzalloc(size, GFP_KERNEL);
+ if (!vdev->fault_pages)
+ return -ENOMEM;
+
+ ret = vfio_pci_register_dev_region(vdev,
+ VFIO_REGION_TYPE_NESTED,
+ VFIO_REGION_SUBTYPE_NESTED_DMA_FAULT,
+ _pci_dma_fault_regops, size,
+ VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE,
+ vdev->fault_pages);
+ if (ret)
+ goto out;
+
+ 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);
+ return 0;
+out:
+ kfree(vdev->fault_pages);
+ vdev->fault_pages = NULL;
+ return ret;
+}
+
static int vfio_pci_enable(struct vfio_pci_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
@@ -409,6 +481,10 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
}
}
+ ret = vfio_pci_dma_fault_init(vdev);
+ if (ret)
+ goto disable_exit;
+
vfio_pci_probe_mmaps(vdev);
return 0;
diff --git a/drivers/vfio/pci/vfio_pci_private.h
b/drivers/vfio/pci/vfio_pci_private.h
index 5c90e560c5c7..1d9b0f648133 100644
--- a/drivers/vfio/pci/vfio_pci_private.h
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -134,6 +134,8 @@ struct vfio_pci_device {
int ioeventfds_nr;
struct eventfd_ctx *err_trigger;
struct eventfd_ctx *req_trigger;
+ u8 *fault_pages;
+ struct mutexfault_queue_lock;
struct list_headdummy_resources_list;
struct mutexioeventfds_lock;
struct list_head