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.
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 5b671b6..541ccaf 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 e39c57a..3955e4d 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); } @@ -449,15 +463,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
