KVM: arm/arm64: GICv4: Wire mapping/unmapping of VLPIs in VFIO irq bypass
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>
This commit is contained in:
parent
74fe55dc9a
commit
196b136498
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue