diff options
author | Vijay Mohan Pandarathil <vijaymohan.pandarathil@hp.com> | 2013-03-11 11:31:22 -0400 |
---|---|---|
committer | Alex Williamson <alex.williamson@redhat.com> | 2013-03-11 11:31:22 -0400 |
commit | dad9f8972e04cd081a028d8fb1249d746d97fc03 (patch) | |
tree | 8f3f6bbfa41f3a60853007e33846d6fac2b139ea /drivers/vfio | |
parent | 44f507163d9e51238458ee6904b4d71fb0723723 (diff) |
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: Vijay Mohan Pandarathil <vijaymohan.pandarathil@hp.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r-- | drivers/vfio/pci/vfio_pci.c | 44 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_intrs.c | 64 | ||||
-rw-r--r-- | drivers/vfio/pci/vfio_pci_private.h | 1 |
3 files changed, 108 insertions, 1 deletions
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 8189cb6a86af..acfcb1ae77bc 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c | |||
@@ -201,7 +201,9 @@ static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type) | |||
201 | 201 | ||
202 | return (flags & PCI_MSIX_FLAGS_QSIZE) + 1; | 202 | return (flags & PCI_MSIX_FLAGS_QSIZE) + 1; |
203 | } | 203 | } |
204 | } | 204 | } else if (irq_type == VFIO_PCI_ERR_IRQ_INDEX) |
205 | if (pci_is_pcie(vdev->pdev)) | ||
206 | return 1; | ||
205 | 207 | ||
206 | return 0; | 208 | return 0; |
207 | } | 209 | } |
@@ -317,6 +319,17 @@ static long vfio_pci_ioctl(void *device_data, | |||
317 | if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) | 319 | if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS) |
318 | return -EINVAL; | 320 | return -EINVAL; |
319 | 321 | ||
322 | switch (info.index) { | ||
323 | case VFIO_PCI_INTX_IRQ_INDEX ... VFIO_PCI_MSIX_IRQ_INDEX: | ||
324 | break; | ||
325 | case VFIO_PCI_ERR_IRQ_INDEX: | ||
326 | if (pci_is_pcie(vdev->pdev)) | ||
327 | break; | ||
328 | /* pass thru to return error */ | ||
329 | default: | ||
330 | return -EINVAL; | ||
331 | } | ||
332 | |||
320 | info.flags = VFIO_IRQ_INFO_EVENTFD; | 333 | info.flags = VFIO_IRQ_INFO_EVENTFD; |
321 | 334 | ||
322 | info.count = vfio_pci_get_irq_count(vdev, info.index); | 335 | info.count = vfio_pci_get_irq_count(vdev, info.index); |
@@ -551,11 +564,40 @@ static void vfio_pci_remove(struct pci_dev *pdev) | |||
551 | kfree(vdev); | 564 | kfree(vdev); |
552 | } | 565 | } |
553 | 566 | ||
567 | static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, | ||
568 | pci_channel_state_t state) | ||
569 | { | ||
570 | struct vfio_pci_device *vdev; | ||
571 | struct vfio_device *device; | ||
572 | |||
573 | device = vfio_device_get_from_dev(&pdev->dev); | ||
574 | if (device == NULL) | ||
575 | return PCI_ERS_RESULT_DISCONNECT; | ||
576 | |||
577 | vdev = vfio_device_data(device); | ||
578 | if (vdev == NULL) { | ||
579 | vfio_device_put(device); | ||
580 | return PCI_ERS_RESULT_DISCONNECT; | ||
581 | } | ||
582 | |||
583 | if (vdev->err_trigger) | ||
584 | eventfd_signal(vdev->err_trigger, 1); | ||
585 | |||
586 | vfio_device_put(device); | ||
587 | |||
588 | return PCI_ERS_RESULT_CAN_RECOVER; | ||
589 | } | ||
590 | |||
591 | static struct pci_error_handlers vfio_err_handlers = { | ||
592 | .error_detected = vfio_pci_aer_err_detected, | ||
593 | }; | ||
594 | |||
554 | static struct pci_driver vfio_pci_driver = { | 595 | static struct pci_driver vfio_pci_driver = { |
555 | .name = "vfio-pci", | 596 | .name = "vfio-pci", |
556 | .id_table = NULL, /* only dynamic ids */ | 597 | .id_table = NULL, /* only dynamic ids */ |
557 | .probe = vfio_pci_probe, | 598 | .probe = vfio_pci_probe, |
558 | .remove = vfio_pci_remove, | 599 | .remove = vfio_pci_remove, |
600 | .err_handler = &vfio_err_handlers, | ||
559 | }; | 601 | }; |
560 | 602 | ||
561 | static void __exit vfio_pci_cleanup(void) | 603 | static void __exit vfio_pci_cleanup(void) |
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 3639371fa697..b84bf2210a91 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c | |||
@@ -745,6 +745,63 @@ static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev, | |||
745 | return 0; | 745 | return 0; |
746 | } | 746 | } |
747 | 747 | ||
748 | static int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev, | ||
749 | unsigned index, unsigned start, | ||
750 | unsigned count, uint32_t flags, void *data) | ||
751 | { | ||
752 | int32_t fd = *(int32_t *)data; | ||
753 | struct pci_dev *pdev = vdev->pdev; | ||
754 | |||
755 | if ((index != VFIO_PCI_ERR_IRQ_INDEX) || | ||
756 | !(flags & VFIO_IRQ_SET_DATA_TYPE_MASK)) | ||
757 | return -EINVAL; | ||
758 | |||
759 | /* | ||
760 | * device_lock synchronizes setting and checking of | ||
761 | * err_trigger. The vfio_pci_aer_err_detected() is also | ||
762 | * called with device_lock held. | ||
763 | */ | ||
764 | |||
765 | /* DATA_NONE/DATA_BOOL enables loopback testing */ | ||
766 | |||
767 | if (flags & VFIO_IRQ_SET_DATA_NONE) { | ||
768 | device_lock(&pdev->dev); | ||
769 | if (vdev->err_trigger) | ||
770 | eventfd_signal(vdev->err_trigger, 1); | ||
771 | device_unlock(&pdev->dev); | ||
772 | return 0; | ||
773 | } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { | ||
774 | uint8_t trigger = *(uint8_t *)data; | ||
775 | device_lock(&pdev->dev); | ||
776 | if (trigger && vdev->err_trigger) | ||
777 | eventfd_signal(vdev->err_trigger, 1); | ||
778 | device_unlock(&pdev->dev); | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | /* Handle SET_DATA_EVENTFD */ | ||
783 | |||
784 | if (fd == -1) { | ||
785 | device_lock(&pdev->dev); | ||
786 | if (vdev->err_trigger) | ||
787 | eventfd_ctx_put(vdev->err_trigger); | ||
788 | vdev->err_trigger = NULL; | ||
789 | device_unlock(&pdev->dev); | ||
790 | return 0; | ||
791 | } else if (fd >= 0) { | ||
792 | struct eventfd_ctx *efdctx; | ||
793 | efdctx = eventfd_ctx_fdget(fd); | ||
794 | if (IS_ERR(efdctx)) | ||
795 | return PTR_ERR(efdctx); | ||
796 | device_lock(&pdev->dev); | ||
797 | if (vdev->err_trigger) | ||
798 | eventfd_ctx_put(vdev->err_trigger); | ||
799 | vdev->err_trigger = efdctx; | ||
800 | device_unlock(&pdev->dev); | ||
801 | return 0; | ||
802 | } else | ||
803 | return -EINVAL; | ||
804 | } | ||
748 | int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, | 805 | int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, |
749 | unsigned index, unsigned start, unsigned count, | 806 | unsigned index, unsigned start, unsigned count, |
750 | void *data) | 807 | void *data) |
@@ -779,6 +836,13 @@ int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, | |||
779 | break; | 836 | break; |
780 | } | 837 | } |
781 | break; | 838 | break; |
839 | case VFIO_PCI_ERR_IRQ_INDEX: | ||
840 | switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { | ||
841 | case VFIO_IRQ_SET_ACTION_TRIGGER: | ||
842 | if (pci_is_pcie(vdev->pdev)) | ||
843 | func = vfio_pci_set_err_trigger; | ||
844 | break; | ||
845 | } | ||
782 | } | 846 | } |
783 | 847 | ||
784 | if (!func) | 848 | if (!func) |
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index d7e55d03f49e..9c6d5d0f3b02 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h | |||
@@ -56,6 +56,7 @@ struct vfio_pci_device { | |||
56 | bool has_vga; | 56 | bool has_vga; |
57 | struct pci_saved_state *pci_saved_state; | 57 | struct pci_saved_state *pci_saved_state; |
58 | atomic_t refcnt; | 58 | atomic_t refcnt; |
59 | struct eventfd_ctx *err_trigger; | ||
59 | }; | 60 | }; |
60 | 61 | ||
61 | #define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) | 62 | #define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) |