Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dad9f897 authored by Vijay Mohan Pandarathil's avatar Vijay Mohan Pandarathil Committed by Alex Williamson
Browse files

VFIO-AER: Vfio-pci driver changes for supporting AER



- New VFIO_SET_IRQ ioctl option to pass the eventfd that is signaled when
  an error occurs in the vfio_pci_device

- Register pci_error_handler for the vfio_pci driver

- When the device encounters an error, the error handler registered by
  the vfio_pci driver gets invoked by the AER infrastructure

- In the error handler, signal the eventfd registered for the device.

- This results in the qemu eventfd handler getting invoked and
  appropriate action taken for the guest.

Signed-off-by: default avatarVijay Mohan Pandarathil <vijaymohan.pandarathil@hp.com>
Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent 44f50716
Loading
Loading
Loading
Loading
+43 −1
Original line number Diff line number Diff line
@@ -201,7 +201,9 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)

			return (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
		}
	}
	} else if (irq_type == VFIO_PCI_ERR_IRQ_INDEX)
		if (pci_is_pcie(vdev->pdev))
			return 1;

	return 0;
}
@@ -317,6 +319,17 @@ static long vfio_pci_ioctl(void *device_data,
		if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
			return -EINVAL;

		switch (info.index) {
		case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX:
			break;
		case VFIO_PCI_ERR_IRQ_INDEX:
			if (pci_is_pcie(vdev->pdev))
				break;
		/* pass thru to return error */
		default:
			return -EINVAL;
		}

		info.flags = VFIO_IRQ_INFO_EVENTFD;

		info.count = vfio_pci_get_irq_count(vdev, info.index);
@@ -551,11 +564,40 @@ static void vfio_pci_remove(struct pci_dev *pdev)
	kfree(vdev);
}

static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
						  pci_channel_state_t state)
{
	struct vfio_pci_device *vdev;
	struct vfio_device *device;

	device = vfio_device_get_from_dev(&pdev->dev);
	if (device == NULL)
		return PCI_ERS_RESULT_DISCONNECT;

	vdev = vfio_device_data(device);
	if (vdev == NULL) {
		vfio_device_put(device);
		return PCI_ERS_RESULT_DISCONNECT;
	}

	if (vdev->err_trigger)
		eventfd_signal(vdev->err_trigger, 1);

	vfio_device_put(device);

	return PCI_ERS_RESULT_CAN_RECOVER;
}

static struct pci_error_handlers vfio_err_handlers = {
	.error_detected = vfio_pci_aer_err_detected,
};

static struct pci_driver vfio_pci_driver = {
	.name		= "vfio-pci",
	.id_table	= NULL, /* only dynamic ids */
	.probe		= vfio_pci_probe,
	.remove		= vfio_pci_remove,
	.err_handler	= &vfio_err_handlers,
};

static void __exit vfio_pci_cleanup(void)
+64 −0
Original line number Diff line number Diff line
@@ -745,6 +745,63 @@ static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
	return 0;
}

static int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev,
				    unsigned index, unsigned start,
				    unsigned count, uint32_t flags, void *data)
{
	int32_t fd = *(int32_t *)data;
	struct pci_dev *pdev = vdev->pdev;

	if ((index != VFIO_PCI_ERR_IRQ_INDEX) ||
	    !(flags & VFIO_IRQ_SET_DATA_TYPE_MASK))
		return -EINVAL;

	/*
	 * device_lock synchronizes setting and checking of
	 * err_trigger. The vfio_pci_aer_err_detected() is also
	 * called with device_lock held.
	 */

	/* DATA_NONE/DATA_BOOL enables loopback testing */

	if (flags & VFIO_IRQ_SET_DATA_NONE) {
		device_lock(&pdev->dev);
		if (vdev->err_trigger)
			eventfd_signal(vdev->err_trigger, 1);
		device_unlock(&pdev->dev);
		return 0;
	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
		uint8_t trigger = *(uint8_t *)data;
		device_lock(&pdev->dev);
		if (trigger && vdev->err_trigger)
			eventfd_signal(vdev->err_trigger, 1);
		device_unlock(&pdev->dev);
		return 0;
	}

	/* Handle SET_DATA_EVENTFD */

	if (fd == -1) {
		device_lock(&pdev->dev);
		if (vdev->err_trigger)
			eventfd_ctx_put(vdev->err_trigger);
		vdev->err_trigger = NULL;
		device_unlock(&pdev->dev);
		return 0;
	} else if (fd >= 0) {
		struct eventfd_ctx *efdctx;
		efdctx = eventfd_ctx_fdget(fd);
		if (IS_ERR(efdctx))
			return PTR_ERR(efdctx);
		device_lock(&pdev->dev);
		if (vdev->err_trigger)
			eventfd_ctx_put(vdev->err_trigger);
		vdev->err_trigger = efdctx;
		device_unlock(&pdev->dev);
		return 0;
	} else
		return -EINVAL;
}
int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
			    unsigned index, unsigned start, unsigned count,
			    void *data)
@@ -779,6 +836,13 @@ int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
			break;
		}
		break;
	case VFIO_PCI_ERR_IRQ_INDEX:
		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
		case VFIO_IRQ_SET_ACTION_TRIGGER:
			if (pci_is_pcie(vdev->pdev))
				func = vfio_pci_set_err_trigger;
			break;
		}
	}

	if (!func)
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ struct vfio_pci_device {
	bool			has_vga;
	struct pci_saved_state	*pci_saved_state;
	atomic_t		refcnt;
	struct eventfd_ctx	*err_trigger;
};

#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
+1 −0
Original line number Diff line number Diff line
@@ -319,6 +319,7 @@ enum {
	VFIO_PCI_INTX_IRQ_INDEX,
	VFIO_PCI_MSI_IRQ_INDEX,
	VFIO_PCI_MSIX_IRQ_INDEX,
	VFIO_PCI_ERR_IRQ_INDEX,
	VFIO_PCI_NUM_IRQS
};