@@ -23,6 +23,7 @@
#include "exec/memory.h"
#include "qemu/queue.h"
#include "hw/sysbus.h"
+#include "sysemu/kvm.h"
extern const MemoryRegionOps vfio_region_ops;
extern const MemoryListener vfio_memory_listener;
@@ -367,6 +368,99 @@ static int vfio_populate_interrupts(VFIODevice *vbasedev)
return 0;
}
+static void vfio_enable_intp_kvm(VFIOINTp *intp)
+{
+#ifdef CONFIG_KVM
+ struct kvm_irqfd irqfd = {
+ .fd = event_notifier_get_fd(&intp->interrupt),
+ .gsi = intp->virtualID,
+ .flags = KVM_IRQFD_FLAG_RESAMPLE,
+ };
+
+ struct vfio_irq_set *irq_set;
+ int ret, argsz;
+ int32_t *pfd;
+ VFIODevice *vbasedev = &intp->vdev->vbasedev;
+
+ if (!kvm_irqfds_enabled() ||
+ !kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) {
+ return;
+ }
+
+ /* Get to a known interrupt state */
+ qemu_set_fd_handler(irqfd.fd, NULL, NULL, NULL);
+ vfio_mask_irqindex(vbasedev, intp->pin);
+ intp->state = VFIO_IRQ_INACTIVE;
+ qemu_set_irq(intp->qemuirq, 0);
+
+ /* Get an eventfd for resample/unmask */
+ if (event_notifier_init(&intp->unmask, 0)) {
+ error_report("vfio: Error: event_notifier_init failed eoi");
+ goto fail;
+ }
+
+ /* KVM triggers it, VFIO listens for it */
+ irqfd.resamplefd = event_notifier_get_fd(&intp->unmask);
+
+ if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
+ error_report("vfio: Error: Failed to setup resample irqfd: %m");
+ goto fail_irqfd;
+ }
+
+ argsz = sizeof(*irq_set) + sizeof(*pfd);
+
+ irq_set = g_malloc0(argsz);
+ irq_set->argsz = argsz;
+ irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK;
+ irq_set->index = intp->pin;
+ irq_set->start = 0;
+ irq_set->count = 1;
+ pfd = (int32_t *)&irq_set->data;
+
+ *pfd = irqfd.resamplefd;
+
+ ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+ g_free(irq_set);
+ if (ret) {
+ error_report("vfio: Error: Failed to setup IRQ unmask fd: %m");
+ goto fail_vfio;
+ }
+
+ vfio_unmask_irqindex(vbasedev, intp->pin);
+
+ intp->kvm_accel = true;
+
+ DPRINTF("%s irqfd pin=%d to virtID = %d fd=%d, resamplefd=%d)\n",
+ __func__, intp->pin, intp->virtualID,
+ irqfd.fd, irqfd.resamplefd);
+ return;
+
+fail_vfio:
+ irqfd.flags = KVM_IRQFD_FLAG_DEASSIGN;
+ kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd);
+fail_irqfd:
+ event_notifier_cleanup(&intp->unmask);
+fail:
+ qemu_set_fd_handler(irqfd.fd, vfio_intp_interrupt, NULL, intp);
+ vfio_unmask_irqindex(vbasedev, intp->pin);
+#endif
+}
+
+void vfio_setup_irqfd(SysBusDevice *s, int index, int virq)
+{
+ VFIOPlatformDevice *vdev = container_of(s, VFIOPlatformDevice, sbdev);
+ VFIOINTp *intp;
+
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->pin == index) {
+ intp->virtualID = virq;
+ DPRINTF("enable irqfd for irq index %d (virtual IRQ %d)\n",
+ index, virq);
+ vfio_enable_intp_kvm(intp);
+ }
+ }
+}
+
static VFIODeviceOps vfio_platform_ops = {
.vfio_compute_needs_reset = vfio_platform_compute_needs_reset,
.vfio_hot_reset_multi = vfio_platform_hot_reset_multi,