Recomputing the whole GIC state when injecting a single interrupt is a tiny bit silly. Instead, just computing the new state for this particular IRQ saves a bit of processing and makes things noticeably faster. Signed-off-by: Marc Zyngier <marc.zyngier@xxxxxxx> --- arch/arm/include/asm/kvm_vgic.h | 1 + arch/arm/kvm/vgic.c | 69 ++++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h index 633e697..1f7fd32 100644 --- a/arch/arm/include/asm/kvm_vgic.h +++ b/arch/arm/include/asm/kvm_vgic.h @@ -183,6 +183,7 @@ struct vgic_dist { u8 irq_sgi_sources[VGIC_MAX_CPUS][16]; /* Target CPU for each IRQ */ + u8 irq_spi_cpu[VGIC_NR_SHARED_IRQS]; struct vgic_bitmap irq_spi_target[VGIC_MAX_CPUS]; /* Bitmap indicating which CPU has something pending */ diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c index 5177792..5f32772 100644 --- a/arch/arm/kvm/vgic.c +++ b/arch/arm/kvm/vgic.c @@ -47,6 +47,9 @@ * registers, stored on each vcpu. We only keep one bit of * information per interrupt, making sure that only one vcpu can * accept the interrupt. + * The same is true when injecting an interrupt, except that we only + * consider a single interrupt at a time. The irq_spi_cpu array + * contains the target CPU for each SPI. * * The handling of level interrupts adds some extra complexity. We * need to track when the interrupt has been EOIed, so we can sample @@ -313,6 +316,7 @@ static void vgic_set_target_reg(struct kvm *kvm, u32 val, int irq) int shift = i * 8; target = ffs((val >> shift) & 0xffU); target = target ? (target - 1) : 0; + dist->irq_spi_cpu[irq] = target; kvm_for_each_vcpu(c, vcpu, kvm) { bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]); if (c == target) @@ -866,23 +870,19 @@ static void vgic_kick_vcpus(struct kvm *kvm) } } -int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq) +static bool vgic_update_irq_state(struct kvm *kvm, int cpuid, + const struct kvm_irq_level *irq) { struct vgic_dist *dist = &kvm->arch.vgic; - int nrcpus = atomic_read(&kvm->online_vcpus); - int is_edge, state; + struct kvm_vcpu *vcpu; + int is_edge, state, level; + int pend, enabled; unsigned long flags; - bool updated_state = false; - if (cpuid >= nrcpus) - return -EINVAL; - - /* Only PPIs or SPIs */ - if (irq->irq >= VGIC_NR_IRQS || irq->irq < 16) - return -EINVAL; + level = !!irq->level; - kvm_debug("Inject IRQ%d\n", irq->irq); spin_lock_irqsave(&dist->lock, flags); + is_edge = vgic_irq_is_edge(dist, irq->irq); state = vgic_bitmap_get_irq_val(&dist->irq_state, cpuid, irq->irq); @@ -891,16 +891,49 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level * * - level triggered and we change level * - edge triggered and we have a rising edge */ - if ((!is_edge && (state ^ !!irq->level)) || - (is_edge && !state && irq->level)) { - vgic_bitmap_set_irq_val(&dist->irq_state, cpuid, - irq->irq, !!irq->level); - vgic_update_state(kvm); - updated_state = true; + if (!((!is_edge && (state ^ level)) || + (is_edge && !state && level))) { + spin_unlock_irqrestore(&dist->lock, flags); + return false; + } + + vgic_bitmap_set_irq_val(&dist->irq_state, cpuid, irq->irq, level); + + enabled = vgic_bitmap_get_irq_val(&dist->irq_enabled, cpuid, irq->irq); + pend = level && enabled; + + if (irq->irq >= 32) { + cpuid = dist->irq_spi_cpu[irq->irq - 32]; + pend &= vgic_bitmap_get_irq_val(&dist->irq_spi_target[cpuid], + 0, irq->irq); } + + kvm_debug("Inject IRQ%d level %d CPU%d\n", irq->irq, level, cpuid); + + vcpu = kvm_get_vcpu(kvm, cpuid); + if (pend) { + set_bit(irq->irq, vcpu->arch.vgic_cpu.pending); + set_bit(cpuid, &dist->irq_pending_on_cpu); + } else + clear_bit(irq->irq, vcpu->arch.vgic_cpu.pending); + spin_unlock_irqrestore(&dist->lock, flags); - if (updated_state) + return true; +} + +int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, const struct kvm_irq_level *irq) +{ + int nrcpus = atomic_read(&kvm->online_vcpus); + + if (cpuid >= nrcpus) + return -EINVAL; + + /* Only PPIs or SPIs */ + if (irq->irq >= VGIC_NR_IRQS || irq->irq < 16) + return -EINVAL; + + if (vgic_update_irq_state(kvm, cpuid, irq)) vgic_kick_vcpus(kvm); return 0; -- 1.7.12 _______________________________________________ kvmarm mailing list kvmarm@xxxxxxxxxxxxxxxxxxxxx https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm