Commit f7c0c57b authored by Eric Auger's avatar Eric Auger Committed by Zheng Zengkai
Browse files

vfio/pci: Register an iommu fault handler

virt inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I401IF


CVE: NA

------------------------------

Register an IOMMU fault handler which records faults in
the DMA FAULT region ring buffer. In a subsequent patch, we
will add the signaling of a specific eventfd to allow the
userspace to be notified whenever a new fault has shown up.

Signed-off-by: default avatarEric Auger <eric.auger@redhat.com>
Signed-off-by: default avatarKunkun <Jiang&lt;jiangkunkun@huawei.com>
Reviewed-by: default avatarKeqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 20b23b13
Loading
Loading
Loading
Loading
+47 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/vgaarb.h>
#include <linux/nospec.h>
#include <linux/sched/mm.h>
#include <linux/circ_buf.h>

#include "vfio_pci_private.h"

@@ -333,6 +334,41 @@ static const struct vfio_pci_regops vfio_pci_dma_fault_regops = {
	.add_capability = vfio_pci_dma_fault_add_capability,
};

static int
vfio_pci_iommu_dev_fault_handler(struct iommu_fault *fault, void *data)
{
	struct vfio_pci_device *vdev = (struct vfio_pci_device *)data;
	struct vfio_region_dma_fault *reg =
		(struct vfio_region_dma_fault *)vdev->fault_pages;
	struct iommu_fault *new;
	u32 head, tail, size;
	int ret = -EINVAL;

	if (WARN_ON(!reg))
		return ret;

	mutex_lock(&vdev->fault_queue_lock);

	head = reg->head;
	tail = reg->tail;
	size = reg->nb_entries;

	new = (struct iommu_fault *)(vdev->fault_pages + reg->offset +
				     head * reg->entry_size);

	if (CIRC_SPACE(head, tail, size) < 1) {
		ret = -ENOSPC;
		goto unlock;
	}

	*new = *fault;
	reg->head = (head + 1) % size;
	ret = 0;
unlock:
	mutex_unlock(&vdev->fault_queue_lock);
	return ret;
}

#define DMA_FAULT_RING_LENGTH 512

static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
@@ -377,6 +413,13 @@ static int vfio_pci_dma_fault_init(struct vfio_pci_device *vdev)
	header->entry_size = sizeof(struct iommu_fault);
	header->nb_entries = DMA_FAULT_RING_LENGTH;
	header->offset = sizeof(struct vfio_region_dma_fault);

	ret = iommu_register_device_fault_handler(&vdev->pdev->dev,
					vfio_pci_iommu_dev_fault_handler,
					vdev);
	if (ret) /* the dma fault region is freed in vfio_pci_disable() */
		goto out;

	return 0;
out:
	kfree(vdev->fault_pages);
@@ -500,7 +543,7 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
	struct pci_dev *pdev = vdev->pdev;
	struct vfio_pci_dummy_resource *dummy_res, *tmp;
	struct vfio_pci_ioeventfd *ioeventfd, *ioeventfd_tmp;
	int i, bar;
	int i, bar, ret;

	/* Stop the device from further DMA */
	pci_clear_master(pdev);
@@ -509,6 +552,9 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)
				VFIO_IRQ_SET_ACTION_TRIGGER,
				vdev->irq_type, 0, 0, NULL);

	ret = iommu_unregister_device_fault_handler(&vdev->pdev->dev);
	WARN_ON(ret == -EBUSY);

	/* Device closed, don't need mutex here */
	list_for_each_entry_safe(ioeventfd, ioeventfd_tmp,
				 &vdev->ioeventfds_list, next) {