static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, int vector, int fd, bool msix) { struct pci_dev *pdev = vdev->pdev; int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector; char *name = msix ? "vfio-msix" : "vfio-msi"; struct eventfd_ctx *trigger; int ret; if (vector >= vdev->num_ctx) return -EINVAL; if (vdev->ctx[vector].trigger) { free_irq(irq, vdev->ctx[vector].trigger); kfree(vdev->ctx[vector].name); eventfd_ctx_put(vdev->ctx[vector].trigger); vdev->ctx[vector].trigger = NULL; } if (fd < 0) return 0; vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)", name, vector, pci_name(pdev)); if (!vdev->ctx[vector].name) return -ENOMEM; trigger = eventfd_ctx_fdget(fd); if (IS_ERR(trigger)) { kfree(vdev->ctx[vector].name); return PTR_ERR(trigger); } /* * The MSIx vector table resides in device memory which may be cleared * via backdoor resets. We don't allow direct access to the vector * table so even if a userspace driver attempts to save/restore around * such a reset it would be unsuccessful. To avoid this, restore the * cached value of the message prior to enabling. */ if (msix) { struct msi_msg msg; get_cached_msi_msg(irq, &msg); write_msi_msg(irq, &msg); } ret = request_irq(irq, vfio_msihandler, 0, vdev->ctx[vector].name, trigger); if (ret) { kfree(vdev->ctx[vector].name); eventfd_ctx_put(trigger); return ret; } vdev->ctx[vector].trigger = trigger; return 0; }
static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) { struct pci_dev *pdev = vdev->pdev; unsigned long irqflags = IRQF_SHARED; struct eventfd_ctx *trigger; unsigned long flags; int ret; if (vdev->ctx[0].trigger) { free_irq(pdev->irq, vdev); kfree(vdev->ctx[0].name); eventfd_ctx_put(vdev->ctx[0].trigger); vdev->ctx[0].trigger = NULL; } if (fd < 0) /* Disable only */ return 0; vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)", pci_name(pdev)); if (!vdev->ctx[0].name) return -ENOMEM; trigger = eventfd_ctx_fdget(fd); if (IS_ERR(trigger)) { kfree(vdev->ctx[0].name); return PTR_ERR(trigger); } vdev->ctx[0].trigger = trigger; if (!vdev->pci_2_3) irqflags = 0; ret = request_irq(pdev->irq, vfio_intx_handler, irqflags, vdev->ctx[0].name, vdev); if (ret) { vdev->ctx[0].trigger = NULL; kfree(vdev->ctx[0].name); eventfd_ctx_put(trigger); return ret; } /* * INTx disable will stick across the new irq setup, * disable_irq won't. */ spin_lock_irqsave(&vdev->irqlock, flags); if (!vdev->pci_2_3 && vdev->ctx[0].masked) disable_irq_nosync(pdev->irq); spin_unlock_irqrestore(&vdev->irqlock, flags); return 0; }
static int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx, unsigned int count, uint32_t flags, void *data) { /* DATA_NONE/DATA_BOOL enables loopback testing */ if (flags & VFIO_IRQ_SET_DATA_NONE) { if (*ctx) { if (count) { eventfd_signal(*ctx, 1); } else { eventfd_ctx_put(*ctx); *ctx = NULL; } return 0; } } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { uint8_t trigger; if (!count) return -EINVAL; trigger = *(uint8_t *)data; if (trigger && *ctx) eventfd_signal(*ctx, 1); return 0; } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { int32_t fd; if (!count) return -EINVAL; fd = *(int32_t *)data; if (fd == -1) { if (*ctx) eventfd_ctx_put(*ctx); *ctx = NULL; } else if (fd >= 0) { struct eventfd_ctx *efdctx; efdctx = eventfd_ctx_fdget(fd); if (IS_ERR(efdctx)) return PTR_ERR(efdctx); if (*ctx) eventfd_ctx_put(*ctx); *ctx = efdctx; } return 0; } return -EINVAL; }
static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev, int vector, int fd, bool msix) { struct pci_dev *pdev = vdev->pdev; int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector; char *name = msix ? "vfio-msix" : "vfio-msi"; struct eventfd_ctx *trigger; int ret; if (vector >= vdev->num_ctx) return -EINVAL; if (vdev->ctx[vector].trigger) { free_irq(irq, vdev->ctx[vector].trigger); kfree(vdev->ctx[vector].name); eventfd_ctx_put(vdev->ctx[vector].trigger); vdev->ctx[vector].trigger = NULL; } if (fd < 0) return 0; vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)", name, vector, pci_name(pdev)); if (!vdev->ctx[vector].name) return -ENOMEM; trigger = eventfd_ctx_fdget(fd); if (IS_ERR(trigger)) { kfree(vdev->ctx[vector].name); return PTR_ERR(trigger); } ret = request_irq(irq, vfio_msihandler, 0, vdev->ctx[vector].name, trigger); if (ret) { kfree(vdev->ctx[vector].name); eventfd_ctx_put(trigger); return ret; } vdev->ctx[vector].trigger = trigger; 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; if ((index != VFIO_PCI_ERR_IRQ_INDEX) || !(flags & VFIO_IRQ_SET_DATA_TYPE_MASK)) return -EINVAL; /* DATA_NONE/DATA_BOOL enables loopback testing */ if (flags & VFIO_IRQ_SET_DATA_NONE) { if (vdev->err_trigger) eventfd_signal(vdev->err_trigger, 1); return 0; } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { uint8_t trigger = *(uint8_t *)data; if (trigger && vdev->err_trigger) eventfd_signal(vdev->err_trigger, 1); return 0; } /* Handle SET_DATA_EVENTFD */ if (fd == -1) { if (vdev->err_trigger) eventfd_ctx_put(vdev->err_trigger); vdev->err_trigger = NULL; return 0; } else if (fd >= 0) { struct eventfd_ctx *efdctx; efdctx = eventfd_ctx_fdget(fd); if (IS_ERR(efdctx)) return PTR_ERR(efdctx); if (vdev->err_trigger) eventfd_ctx_put(vdev->err_trigger); vdev->err_trigger = efdctx; return 0; } else return -EINVAL; }