KVM: arm/arm64: GICv4: Wire mapping/unmapping of VLPIs in VFIO irq bypass
authorMarc Zyngier <marc.zyngier@arm.com>
Fri, 27 Oct 2017 14:28:39 +0000 (15:28 +0100)
committerChristoffer Dall <christoffer.dall@linaro.org>
Fri, 10 Nov 2017 08:28:52 +0000 (09:28 +0100)
Let's use the irq bypass mechanism also used for x86 posted interrupts
to intercept the virtual PCIe endpoint configuration and establish our
LPI->VLPI mapping.

Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
include/kvm/arm_vgic.h
virt/kvm/arm/arm.c
virt/kvm/arm/vgic/vgic-v4.c

index 7eeb6c2..2f750c7 100644 (file)
@@ -373,4 +373,12 @@ int kvm_vgic_setup_default_irq_routing(struct kvm *kvm);
 
 int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner);
 
+struct kvm_kernel_irq_routing_entry;
+
+int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int irq,
+                              struct kvm_kernel_irq_routing_entry *irq_entry);
+
+int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int irq,
+                                struct kvm_kernel_irq_routing_entry *irq_entry);
+
 #endif /* __KVM_ARM_VGIC_H */
index e1e947f..34a15c0 100644 (file)
@@ -1471,7 +1471,8 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
        struct kvm_kernel_irqfd *irqfd =
                container_of(cons, struct kvm_kernel_irqfd, consumer);
 
-       return 0;
+       return kvm_vgic_v4_set_forwarding(irqfd->kvm, prod->irq,
+                                         &irqfd->irq_entry);
 }
 void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
                                      struct irq_bypass_producer *prod)
@@ -1479,7 +1480,8 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
        struct kvm_kernel_irqfd *irqfd =
                container_of(cons, struct kvm_kernel_irqfd, consumer);
 
-       return;
+       kvm_vgic_v4_unset_forwarding(irqfd->kvm, prod->irq,
+                                    &irqfd->irq_entry);
 }
 
 void kvm_arch_irq_bypass_stop(struct irq_bypass_consumer *cons)
index c794f0c..2edfe23 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/interrupt.h>
 #include <linux/irqdomain.h>
 #include <linux/kvm_host.h>
+#include <linux/irqchip/arm-gic-v3.h>
 
 #include "vgic.h"
 
@@ -81,3 +82,106 @@ void vgic_v4_teardown(struct kvm *kvm)
        its_vm->nr_vpes = 0;
        its_vm->vpes = NULL;
 }
+
+static struct vgic_its *vgic_get_its(struct kvm *kvm,
+                                    struct kvm_kernel_irq_routing_entry *irq_entry)
+{
+       struct kvm_msi msi  = (struct kvm_msi) {
+               .address_lo     = irq_entry->msi.address_lo,
+               .address_hi     = irq_entry->msi.address_hi,
+               .data           = irq_entry->msi.data,
+               .flags          = irq_entry->msi.flags,
+               .devid          = irq_entry->msi.devid,
+       };
+
+       return vgic_msi_to_its(kvm, &msi);
+}
+
+int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int virq,
+                              struct kvm_kernel_irq_routing_entry *irq_entry)
+{
+       struct vgic_its *its;
+       struct vgic_irq *irq;
+       struct its_vlpi_map map;
+       int ret;
+
+       if (!vgic_supports_direct_msis(kvm))
+               return 0;
+
+       /*
+        * Get the ITS, and escape early on error (not a valid
+        * doorbell for any of our vITSs).
+        */
+       its = vgic_get_its(kvm, irq_entry);
+       if (IS_ERR(its))
+               return 0;
+
+       mutex_lock(&its->its_lock);
+
+       /* Perform then actual DevID/EventID -> LPI translation. */
+       ret = vgic_its_resolve_lpi(kvm, its, irq_entry->msi.devid,
+                                  irq_entry->msi.data, &irq);
+       if (ret)
+               goto out;
+
+       /*
+        * Emit the mapping request. If it fails, the ITS probably
+        * isn't v4 compatible, so let's silently bail out. Holding
+        * the ITS lock should ensure that nothing can modify the
+        * target vcpu.
+        */
+       map = (struct its_vlpi_map) {
+               .vm             = &kvm->arch.vgic.its_vm,
+               .vpe            = &irq->target_vcpu->arch.vgic_cpu.vgic_v3.its_vpe,
+               .vintid         = irq->intid,
+               .properties     = ((irq->priority & 0xfc) |
+                                  (irq->enabled ? LPI_PROP_ENABLED : 0) |
+                                  LPI_PROP_GROUP1),
+               .db_enabled     = true,
+       };
+
+       ret = its_map_vlpi(virq, &map);
+       if (ret)
+               goto out;
+
+       irq->hw         = true;
+       irq->host_irq   = virq;
+
+out:
+       mutex_unlock(&its->its_lock);
+       return ret;
+}
+
+int kvm_vgic_v4_unset_forwarding(struct kvm *kvm, int virq,
+                                struct kvm_kernel_irq_routing_entry *irq_entry)
+{
+       struct vgic_its *its;
+       struct vgic_irq *irq;
+       int ret;
+
+       if (!vgic_supports_direct_msis(kvm))
+               return 0;
+
+       /*
+        * Get the ITS, and escape early on error (not a valid
+        * doorbell for any of our vITSs).
+        */
+       its = vgic_get_its(kvm, irq_entry);
+       if (IS_ERR(its))
+               return 0;
+
+       mutex_lock(&its->its_lock);
+
+       ret = vgic_its_resolve_lpi(kvm, its, irq_entry->msi.devid,
+                                  irq_entry->msi.data, &irq);
+       if (ret)
+               goto out;
+
+       WARN_ON(!(irq->hw && irq->host_irq == virq));
+       irq->hw = false;
+       ret = its_unmap_vlpi(virq);
+
+out:
+       mutex_unlock(&its->its_lock);
+       return ret;
+}