diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index bc561b5..de1b885 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -43,6 +43,9 @@
 #include "monitor/qdev.h"
 #include "qemu/config-file.h"
 
+#define ENABLE_IRQFD 1
+void vfio_setup_irqfd(SysBusDevice *s, int index, int virq);
+
 #define NUM_VIRTIO_TRANSPORTS 32
 
 /* Number of external interrupt lines to configure the GIC with */
@@ -380,6 +383,7 @@ static int vfio_init_func(QemuOpts *opts, void *opaque)
     Error *errp = NULL;
     bool is_amba = false;
     int compat_str_len;
+    bool irqfd_allowed;
 
     if (!driver) {
         qerror_report(QERR_MISSING_PARAMETER, "driver");
@@ -417,6 +421,13 @@ static int vfio_init_func(QemuOpts *opts, void *opaque)
                          error_get_pretty(errp));
             exit(1);
            }
+        irqfd_allowed = object_property_get_bool(OBJECT(s), "irqfd", &errp);
+        if (errp != NULL) {
+            error_report("Couldn't retrieve irqfd flag: %s\n",
+                         error_get_pretty(errp));
+            exit(1);
+           }
+
 
         /*
          * collect region info and build reg property as tuplets
@@ -502,6 +513,9 @@ static int vfio_init_func(QemuOpts *opts, void *opaque)
             irq_attr[3*i] = cpu_to_be32(0);
             irq_attr[3*i+1] = cpu_to_be32(irq_start+i);
             irq_attr[3*i+2] = cpu_to_be32(0x4);
+            if (irqfd_allowed) {
+                vfio_setup_irqfd(s, i, irq_start+i);
+            }
         }
 
         ret = qemu_fdt_setprop(vbi->fdt, nodename, "interrupts",
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index 5038885..18a6204 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -576,6 +576,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
                             KVM_DEV_ARM_VGIC_GRP_ADDR,
                             KVM_VGIC_V2_ADDR_TYPE_CPU,
                             s->dev_fd);
+    kvm_irqfds_allowed = true;
 }
 
 static void kvm_arm_gic_class_init(ObjectClass *klass, void *data)
diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c
index 377783b..d87fcc3 100644
--- a/hw/vfio/platform.c
+++ b/hw/vfio/platform.c
@@ -23,6 +23,7 @@
 #include "hw/sysbus.h"
 
 #include "vfio-common.h"
+#include "sysemu/kvm.h"
 
 typedef struct VFIOINTp {
     QLIST_ENTRY(VFIOINTp) next; /* entry for IRQ list */
@@ -34,6 +35,7 @@ typedef struct VFIOINTp {
     int state; /* inactive, pending, active */
     bool kvm_accel; /* set when QEMU bypass through KVM enabled */
     uint8_t pin; /* index */
+    uint8_t virtualID; /* virtual IRQ */
 } VFIOINTp;
 
 
@@ -44,6 +46,7 @@ typedef struct VFIOPlatformDevice {
     /* queue of pending IRQ */
     QSIMPLEQ_HEAD(pending_intp_queue, VFIOINTp) pending_intp_queue;
     char *compat; /* compatibility string */
+    bool irqfd_allowed;
 } VFIOPlatformDevice;
 
 
@@ -54,6 +57,7 @@ static const MemoryRegionOps vfio_region_ops = {
 };
 
 static void vfio_intp_interrupt(void *opaque);
+void vfio_setup_irqfd(SysBusDevice *s, int index, int virq);
 
 /*
  * It is mandatory to pass a VFIOPlatformDevice since VFIODevice
@@ -419,29 +423,138 @@ static int vfio_platform_get_device_interrupts(VFIODevice *vdev)
 }
 
 
-static void vfio_disable_intp(VFIODevice *vdev)
+static void vfio_disable_intp(VFIOINTp *intp)
 {
+    int fd = event_notifier_get_fd(&intp->interrupt);
+    DPRINTF("close IRQ pin=%d fd=%d\n", intp->pin, fd);
+
+    /* remove the IRQ handler */
+    vfio_disable_irqindex(&intp->vdev->vdev, intp->pin);
+    intp->state = VFIO_IRQ_INACTIVE;
+    qemu_set_irq(intp->qemuirq, 0);
+    qemu_set_fd_handler(fd, NULL, NULL, NULL);
+    event_notifier_cleanup(&intp->interrupt);
+
+}
+
+
+/* IRQFD */
+
+static void resampler_handler(void *opaque)
+{
+    VFIOINTp *intp = (VFIOINTp *)opaque;
+    DPRINTF("%s index %d virtual ID = %d fd = %d\n",
+            __func__,
+            intp->pin, intp->virtualID,
+            event_notifier_get_fd(&intp->unmask));
+    vfio_unmask_irqindex(&intp->vdev->vdev, intp->pin);
+}
+
+
+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,
+    };
+
+    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);
+    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);
+    qemu_set_fd_handler(irqfd.resamplefd, resampler_handler, NULL, intp);
+
+
+    if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
+        error_report("vfio: Error: Failed to setup resample irqfd: %m");
+        goto fail_irqfd;
+    }
+    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_irqfd:
+    event_notifier_cleanup(&intp->unmask);
+fail:
+    qemu_set_fd_handler(irqfd.fd, vfio_intp_interrupt, NULL, intp);
+    vfio_unmask_irqindex(&intp->vdev->vdev, intp->pin);
+#endif
+}
+
+void vfio_setup_irqfd(SysBusDevice *s, int index, int virq)
+{
+    VFIOPlatformDevice *vdev = container_of(s, VFIOPlatformDevice, sbdev);
     VFIOINTp *intp;
-    VFIOPlatformDevice *vplatdev = container_of(vdev, VFIOPlatformDevice, vdev);
-    int fd;
+    QLIST_FOREACH(intp, &vdev->intp_list, next) {
+        if (intp->pin == index) {
+            intp->virtualID = virq;
+            vfio_enable_intp_kvm(intp);
+        }
+    }
+}
 
-    QLIST_FOREACH(intp, &vplatdev->intp_list, next) {
-        fd = event_notifier_get_fd(&intp->interrupt);
-        DPRINTF("close IRQ pin=%d fd=%d\n", intp->pin, fd);
+static void vfio_disable_intp_kvm(VFIOINTp *intp)
+{
+#ifdef CONFIG_KVM
 
-        vfio_disable_irqindex(vdev, intp->pin);
-        intp->state = VFIO_IRQ_INACTIVE;
-        qemu_set_irq(intp->qemuirq, 0);
+    struct kvm_irqfd irqfd = {
+        .fd = event_notifier_get_fd(&intp->interrupt),
+        .gsi = intp->virtualID,
+        .flags = KVM_IRQFD_FLAG_DEASSIGN,
+        };
 
-        qemu_set_fd_handler(fd, NULL, NULL, NULL);
-        event_notifier_cleanup(&intp->interrupt);
+    if (!intp->kvm_accel) {
+        return;
     }
 
-    /* restore fast path */
-    vfio_mmap_set_enabled(vdev, true);
+    /*
+     * Get to a known state, hardware masked, QEMU ready to accept new
+     * interrupts, QEMU IRQ de-asserted.
+     */
+    intp->state = VFIO_IRQ_INACTIVE;
+    /* Tell KVM to stop listening for an INTp irqfd */
+    if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
+        error_report("vfio: Error: Failed to disable INTx irqfd: %m");
+    }
+
+    /* We only need to close the eventfd for VFIO to cleanup the kernel side */
+    event_notifier_cleanup(&intp->unmask);
+
+    /* QEMU starts listening for interrupt events. */
+    qemu_set_fd_handler(irqfd.fd, vfio_intp_interrupt, NULL, intp->vdev);
+
+    intp->kvm_accel = false;
 
+    /* If we've missed an event, let it re-fire through QEMU */
+    vfio_unmask_irqindex(&intp->vdev->vdev, intp->pin);
+
+    DPRINTF("%s: KVM INTx accel disabled\n", __func__);
+#endif
 }
 
+
+
+
 static bool vfio_platform_is_device_already_attached(VFIODevice *vdev,
                                                      VFIOGroup *group)
 {
@@ -489,6 +602,25 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
     }
 }
 
+
+static void vfio_disable_all_intp(VFIODevice *vdev)
+{
+    VFIOINTp *intp;
+    VFIOPlatformDevice *vplatdev =
+        container_of(vdev, VFIOPlatformDevice, vdev);
+
+    QLIST_FOREACH(intp, &vplatdev->intp_list, next) {
+        /* first disable IRQFD handled IRQ and turn them in QEMU handled ones */
+        vfio_disable_intp_kvm(intp);
+        /* actually disable IRQ */
+        vfio_disable_intp(intp);
+    }
+
+    /* restore fast path */
+    vfio_mmap_set_enabled(vdev, true);
+
+}
+
 static void vfio_platform_unrealize(DeviceState *dev, Error **errp)
 {
     int i;
@@ -505,7 +637,7 @@ static void vfio_platform_unrealize(DeviceState *dev, Error **errp)
      * timer free
      * g_free vdev dynamic fields
     */
-    vfio_disable_intp(vbasedev);
+    vfio_disable_all_intp(vbasedev);
 
     while (!QSIMPLEQ_EMPTY(&vplatdev->pending_intp_queue)) {
             QSIMPLEQ_REMOVE_HEAD(&vplatdev->pending_intp_queue, pqnext);
@@ -532,6 +664,10 @@ static void vfio_platform_unrealize(DeviceState *dev, Error **errp)
 
 }
 
+
+
+
+
 static const VMStateDescription vfio_platform_vmstate = {
     .name = TYPE_VFIO_PLATFORM,
     .unmigratable = 1,
@@ -557,6 +693,7 @@ DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice,
                    vdev.mmap_timeout, 1100),
 DEFINE_PROP_UINT32("num_irqs", VFIOPlatformDevice, vdev.num_irqs, 0),
 DEFINE_PROP_UINT32("num_regions", VFIOPlatformDevice, vdev.num_regions, 0),
+DEFINE_PROP_BOOL("irqfd", VFIOPlatformDevice, irqfd_allowed, true),
 DEFINE_PROP_END_OF_LIST(),
 };
 
