[PATCH 1/4] KVM: Using kfifo for irq recording

Sheng Yang
2008-12-23T08:12:55Z

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 <sheng@linux.intel.com>
---
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