Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm64: WARN if unmapping a vLPI fails in any path

When unmapping a vLPI, WARN if nullifying vCPU affinity fails, not just if
failure occurs when freeing an ITE. If undoing vCPU affinity fails, then
odds are very good that vLPI state tracking has has gotten out of whack,
i.e. that KVM and the GIC disagree on the state of an IRQ/vLPI. At best,
inconsistent state means there is a lurking bug/flaw somewhere. At worst,
the inconsistency could eventually be fatal to the host, e.g. if an ITS
command fails because KVM's view of things doesn't match reality/hardware.

Note, only the call from kvm_arch_irq_bypass_del_producer() by way of
kvm_vgic_v4_unset_forwarding() doesn't already WARN. Common KVM's
kvm_irq_routing_update() WARNs if kvm_arch_update_irqfd_routing() fails.
For that path, if its_unmap_vlpi() fails in kvm_vgic_v4_unset_forwarding(),
the only possible causes are that the GIC doesn't have a v4 ITS (from
its_irq_set_vcpu_affinity()):

/* Need a v4 ITS */
if (!is_v4(its_dev->its))
return -EINVAL;

guard(raw_spinlock)(&its_dev->event_map.vlpi_lock);

/* Unmap request? */
if (!info)
return its_vlpi_unmap(d);

or that KVM has gotten out of sync with the GIC/ITS (from its_vlpi_unmap()):

if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d))
return -EINVAL;

All of the above failure scenarios are warnable offences, as they should
never occur absent a kernel/KVM bug.

Acked-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/all/aFWY2LTVIxz5rfhh@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

+6 -6
+1 -1
arch/arm64/kvm/vgic/vgic-its.c
··· 758 758 if (irq) { 759 759 scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { 760 760 if (irq->hw) 761 - WARN_ON(its_unmap_vlpi(ite->irq->host_irq)); 761 + its_unmap_vlpi(ite->irq->host_irq); 762 762 763 763 irq->hw = false; 764 764 }
+2 -2
arch/arm64/kvm/vgic/vgic-v4.c
··· 545 545 if (irq->hw) { 546 546 atomic_dec(&irq->target_vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vlpi_count); 547 547 irq->hw = false; 548 - ret = its_unmap_vlpi(host_irq); 548 + its_unmap_vlpi(host_irq); 549 549 } 550 550 551 551 raw_spin_unlock_irqrestore(&irq->irq_lock, flags); 552 552 vgic_put_irq(kvm, irq); 553 - return ret; 553 + return 0; 554 554 }
+2 -2
drivers/irqchip/irq-gic-v4.c
··· 342 342 return irq_set_vcpu_affinity(irq, &info); 343 343 } 344 344 345 - int its_unmap_vlpi(int irq) 345 + void its_unmap_vlpi(int irq) 346 346 { 347 347 irq_clear_status_flags(irq, IRQ_DISABLE_UNLAZY); 348 - return irq_set_vcpu_affinity(irq, NULL); 348 + WARN_ON_ONCE(irq_set_vcpu_affinity(irq, NULL)); 349 349 } 350 350 351 351 int its_prop_update_vlpi(int irq, u8 config, bool inv)
+1 -1
include/linux/irqchip/arm-gic-v4.h
··· 146 146 int its_invall_vpe(struct its_vpe *vpe); 147 147 int its_map_vlpi(int irq, struct its_vlpi_map *map); 148 148 int its_get_vlpi(int irq, struct its_vlpi_map *map); 149 - int its_unmap_vlpi(int irq); 149 + void its_unmap_vlpi(int irq); 150 150 int its_prop_update_vlpi(int irq, u8 config, bool inv); 151 151 int its_prop_update_vsgi(int irq, u8 priority, bool group); 152 152