For MSI-X, we have to deal with multiply IRQ with same IRQ handler, so it's
necessary to record the IRQ that trigger the IRQ handler.

And this one is also useful for fixing kvm_free_assigned_irq().

Signed-off-by: Sheng Yang <[email protected]>
---
 include/linux/kvm_host.h |    4 ++++
 virt/kvm/kvm_main.c      |   30 +++++++++++++++++++++++++++---
 2 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index fbf102c..84b11d5 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -17,6 +17,7 @@
 #include <linux/preempt.h>
 #include <linux/marker.h>
 #include <linux/msi.h>
+#include <linux/kfifo.h>
 #include <asm/signal.h>
 
 #include <linux/kvm.h>
@@ -313,6 +314,9 @@ struct kvm_assigned_dev_kernel {
        int host_irq;
        bool host_irq_disabled;
        int guest_irq;
+#define KVM_ASSIGNED_DEV_IRQ_FIFO_LEN  0x100
+       struct kfifo *irq_fifo;
+       spinlock_t irq_fifo_lock;
 #define KVM_ASSIGNED_DEV_GUEST_INTX    (1 << 0)
 #define KVM_ASSIGNED_DEV_GUEST_MSI     (1 << 1)
 #define KVM_ASSIGNED_DEV_HOST_INTX     (1 << 8)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index a51e630..1863942 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -99,6 +99,8 @@ static struct kvm_assigned_dev_kernel 
*kvm_find_assigned_dev(struct list_head *h
 static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
 {
        struct kvm_assigned_dev_kernel *assigned_dev;
+       int irq;
+       u32 gsi;
 
        assigned_dev = container_of(work, struct kvm_assigned_dev_kernel,
                                    interrupt_work);
@@ -109,14 +111,22 @@ static void 
kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
         */
        mutex_lock(&assigned_dev->kvm->lock);
 
-       kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id,
-                   assigned_dev->guest_irq, 1);
+handle_irq:
+       kfifo_get(assigned_dev->irq_fifo,
+                 (unsigned char *)&irq, sizeof(int));
+
+       gsi = assigned_dev->guest_irq;
+
+       kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id, gsi, 1);
 
        if (assigned_dev->irq_requested_type & KVM_ASSIGNED_DEV_GUEST_MSI) {
                enable_irq(assigned_dev->host_irq);
                assigned_dev->host_irq_disabled = false;
        }
 
+       if (kfifo_len(assigned_dev->irq_fifo) != 0)
+               goto handle_irq;
+
        mutex_unlock(&assigned_dev->kvm->lock);
        kvm_put_kvm(assigned_dev->kvm);
 }
@@ -128,6 +138,9 @@ static irqreturn_t kvm_assigned_dev_intr(int irq, void 
*dev_id)
 
        kvm_get_kvm(assigned_dev->kvm);
 
+       kfifo_put(assigned_dev->irq_fifo,
+                 (unsigned char *)&irq, sizeof(int));
+
        schedule_work(&assigned_dev->interrupt_work);
 
        disable_irq_nosync(irq);
@@ -201,6 +214,7 @@ static void kvm_free_assigned_device(struct kvm *kvm,
        pci_dev_put(assigned_dev->dev);
 
        list_del(&assigned_dev->list);
+       kfifo_free(assigned_dev->irq_fifo);
        kfree(assigned_dev);
 }
 
@@ -448,15 +462,25 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
 
        list_add(&match->list, &kvm->arch.assigned_dev_head);
 
+       spin_lock_init(&match->irq_fifo_lock);
+       match->irq_fifo = kfifo_alloc(sizeof(unsigned char) *
+                                     KVM_ASSIGNED_DEV_IRQ_FIFO_LEN,
+                                     GFP_KERNEL | __GFP_ZERO,
+                                     &match->irq_fifo_lock);
+       if (!match->irq_fifo)
+               goto out_list_del;
+
        if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) {
                r = kvm_iommu_map_guest(kvm, match);
                if (r)
-                       goto out_list_del;
+                       goto out_fifo_del;
        }
 
 out:
        mutex_unlock(&kvm->lock);
        return r;
+out_fifo_del:
+       kfifo_free(match->irq_fifo);
 out_list_del:
        list_del(&match->list);
        pci_release_regions(dev);
-- 
1.5.4.5

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to