KVM: PPC: Support irq routing and irqfd for in-kernel MPIC
authorAlexander Graf <agraf@suse.de>
Tue, 16 Apr 2013 15:42:19 +0000 (17:42 +0200)
committerAlexander Graf <agraf@suse.de>
Fri, 26 Apr 2013 18:27:25 +0000 (20:27 +0200)
Now that all the irq routing and irqfd pieces are generic, we can expose
real irqchip support to all of KVM's internal helpers.

This allows us to use irqfd with the in-kernel MPIC.

Signed-off-by: Alexander Graf <agraf@suse.de>
Documentation/virtual/kvm/devices/mpic.txt
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/include/uapi/asm/kvm.h
arch/powerpc/kvm/Kconfig
arch/powerpc/kvm/Makefile
arch/powerpc/kvm/irq.h [new file with mode: 0644]
arch/powerpc/kvm/mpic.c

index ce98e3264fa8bd7219f4a5d54ab79834910d95b5..ad0ac778f20af8a8b8322faeb0093149246a562c 100644 (file)
@@ -35,3 +35,22 @@ Groups:
 
     "attr" is the IRQ number.  IRQ numbers for standard sources are the
     byte offset of the relevant IVPR from EIVPR0, divided by 32.
+
+IRQ Routing:
+
+  The MPIC emulation supports IRQ routing. Only a single MPIC device can
+  be instantiated. Once that device has been created, it's available as
+  irqchip id 0.
+
+  This irqchip 0 has 256 interrupt pins, which expose the interrupts in
+  the main array of interrupt sources (a.k.a. "SRC" interrupts).
+
+  The numbering is the same as the MPIC device tree binding -- based on
+  the register offset from the beginning of the sources array, without
+  regard to any subdivisions in chip documentation such as "internal"
+  or "external" interrupts.
+
+  Default routes are established for these pins, with the GSI being equal
+  to the pin number.
+
+  Access to non-SRC interrupts is not implemented through IRQ routing mechanisms.
index c3f8ceffa412dd6974d9dbf18bf5637642c85e2f..13740a645a6dadc55a5c387628e9b4fb4937a8a7 100644 (file)
 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1
 #endif
 
+/* These values are internal and can be increased later */
+#define KVM_NR_IRQCHIPS          1
+#define KVM_IRQCHIP_NUM_PINS     256
+
 #if !defined(CONFIG_KVM_440)
 #include <linux/mmu_notifier.h>
 
@@ -256,6 +260,9 @@ struct kvm_arch {
 #ifdef CONFIG_PPC_BOOK3S_64
        struct list_head spapr_tce_tables;
 #endif
+#ifdef CONFIG_KVM_MPIC
+       struct openpic *mpic;
+#endif
 };
 
 /*
index 02ad96606860628f77c21b2d64310b7b01a607a6..ca871067a69beb2f894336360f8fd7b2c2146dbb 100644 (file)
@@ -25,6 +25,7 @@
 /* Select powerpc specific features in <linux/kvm.h> */
 #define __KVM_HAVE_SPAPR_TCE
 #define __KVM_HAVE_PPC_SMT
+#define __KVM_HAVE_IRQCHIP
 
 struct kvm_regs {
        __u64 pc;
index f47e95e0b6dea154f3ce474c43a687a81983ef2c..4bf10b520765a207fa54b0452d30de4145efaaff 100644 (file)
@@ -154,6 +154,9 @@ config KVM_E500MC
 config KVM_MPIC
        bool "KVM in-kernel MPIC emulation"
        depends on KVM
+       select HAVE_KVM_IRQCHIP
+       select HAVE_KVM_IRQ_ROUTING
+       select HAVE_KVM_MSI
        help
          Enable support for emulating MPIC devices inside the
           host kernel, rather than relying on userspace to emulate.
index 4a2277a221bba6556041fcc9abd65d4026f68c50..4eada0c010829f76d35c209454b5190630cb96d7 100644 (file)
@@ -104,6 +104,7 @@ kvm-book3s_32-objs := \
 kvm-objs-$(CONFIG_KVM_BOOK3S_32) := $(kvm-book3s_32-objs)
 
 kvm-objs-$(CONFIG_KVM_MPIC) += mpic.o
+kvm-objs-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(addprefix ../../../virt/kvm/, irqchip.o)
 
 kvm-objs := $(kvm-objs-m) $(kvm-objs-y)
 
diff --git a/arch/powerpc/kvm/irq.h b/arch/powerpc/kvm/irq.h
new file mode 100644 (file)
index 0000000..f1e27fd
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef __IRQ_H
+#define __IRQ_H
+
+#include <linux/kvm_host.h>
+
+static inline int irqchip_in_kernel(struct kvm *kvm)
+{
+       int ret = 0;
+
+#ifdef CONFIG_KVM_MPIC
+       ret = ret || (kvm->arch.mpic != NULL);
+#endif
+       smp_rmb();
+       return ret;
+}
+
+#endif
index 10bc08a246fd38e90a03950edea8f5f549bbfbb4..89fe1d66a7fbea96ed4d3de72b546ae4ab029706 100644 (file)
@@ -1076,7 +1076,9 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
        case 0xA0:              /* IACK */
                /* Read-only register */
                break;
-       case 0xB0:              /* EOI */
+       case 0xB0: {            /* EOI */
+               int notify_eoi;
+
                pr_debug("EOI\n");
                s_IRQ = IRQ_get_next(opp, &dst->servicing);
 
@@ -1087,6 +1089,8 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
                }
 
                IRQ_resetbit(&dst->servicing, s_IRQ);
+               /* Notify listeners that the IRQ is over */
+               notify_eoi = s_IRQ;
                /* Set up next servicing IRQ */
                s_IRQ = IRQ_get_next(opp, &dst->servicing);
                /* Check queued interrupts. */
@@ -1099,7 +1103,13 @@ static int openpic_cpu_write_internal(void *opaque, gpa_t addr,
                                idx, n_IRQ);
                        mpic_irq_raise(opp, dst, ILR_INTTGT_INT);
                }
+
+               spin_unlock(&opp->lock);
+               kvm_notify_acked_irq(opp->kvm, 0, notify_eoi);
+               spin_lock(&opp->lock);
+
                break;
+       }
        default:
                break;
        }
@@ -1639,14 +1649,34 @@ static void mpic_destroy(struct kvm_device *dev)
                unmap_mmio(opp);
        }
 
+       dev->kvm->arch.mpic = NULL;
        kfree(opp);
 }
 
+static int mpic_set_default_irq_routing(struct openpic *opp)
+{
+       struct kvm_irq_routing_entry *routing;
+
+       /* Create a nop default map, so that dereferencing it still works */
+       routing = kzalloc((sizeof(*routing)), GFP_KERNEL);
+       if (!routing)
+               return -ENOMEM;
+
+       kvm_set_irq_routing(opp->kvm, routing, 0, 0);
+
+       kfree(routing);
+       return 0;
+}
+
 static int mpic_create(struct kvm_device *dev, u32 type)
 {
        struct openpic *opp;
        int ret;
 
+       /* We only support one MPIC at a time for now */
+       if (dev->kvm->arch.mpic)
+               return -EINVAL;
+
        opp = kzalloc(sizeof(struct openpic), GFP_KERNEL);
        if (!opp)
                return -ENOMEM;
@@ -1691,7 +1721,15 @@ static int mpic_create(struct kvm_device *dev, u32 type)
                goto err;
        }
 
+       ret = mpic_set_default_irq_routing(opp);
+       if (ret)
+               goto err;
+
        openpic_reset(opp);
+
+       smp_wmb();
+       dev->kvm->arch.mpic = opp;
+
        return 0;
 
 err:
@@ -1761,3 +1799,74 @@ void kvmppc_mpic_disconnect_vcpu(struct openpic *opp, struct kvm_vcpu *vcpu)
        opp->dst[vcpu->arch.irq_cpu_id].vcpu = NULL;
        kvm_device_put(opp->dev);
 }
+
+/*
+ * Return value:
+ *  < 0   Interrupt was ignored (masked or not delivered for other reasons)
+ *  = 0   Interrupt was coalesced (previous irq is still pending)
+ *  > 0   Number of CPUs interrupt was delivered to
+ */
+static int mpic_set_irq(struct kvm_kernel_irq_routing_entry *e,
+                       struct kvm *kvm, int irq_source_id, int level,
+                       bool line_status)
+{
+       u32 irq = e->irqchip.pin;
+       struct openpic *opp = kvm->arch.mpic;
+       unsigned long flags;
+
+       spin_lock_irqsave(&opp->lock, flags);
+       openpic_set_irq(opp, irq, level);
+       spin_unlock_irqrestore(&opp->lock, flags);
+
+       /* All code paths we care about don't check for the return value */
+       return 0;
+}
+
+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
+               struct kvm *kvm, int irq_source_id, int level, bool line_status)
+{
+       struct openpic *opp = kvm->arch.mpic;
+       unsigned long flags;
+
+       spin_lock_irqsave(&opp->lock, flags);
+
+       /*
+        * XXX We ignore the target address for now, as we only support
+        *     a single MSI bank.
+        */
+       openpic_msi_write(kvm->arch.mpic, MSIIR_OFFSET, e->msi.data);
+       spin_unlock_irqrestore(&opp->lock, flags);
+
+       /* All code paths we care about don't check for the return value */
+       return 0;
+}
+
+int kvm_set_routing_entry(struct kvm_irq_routing_table *rt,
+                         struct kvm_kernel_irq_routing_entry *e,
+                         const struct kvm_irq_routing_entry *ue)
+{
+       int r = -EINVAL;
+
+       switch (ue->type) {
+       case KVM_IRQ_ROUTING_IRQCHIP:
+               e->set = mpic_set_irq;
+               e->irqchip.irqchip = ue->u.irqchip.irqchip;
+               e->irqchip.pin = ue->u.irqchip.pin;
+               if (e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS)
+                       goto out;
+               rt->chip[ue->u.irqchip.irqchip][e->irqchip.pin] = ue->gsi;
+               break;
+       case KVM_IRQ_ROUTING_MSI:
+               e->set = kvm_set_msi;
+               e->msi.address_lo = ue->u.msi.address_lo;
+               e->msi.address_hi = ue->u.msi.address_hi;
+               e->msi.data = ue->u.msi.data;
+               break;
+       default:
+               goto out;
+       }
+
+       r = 0;
+out:
+       return r;
+}