KVM: arm64: Unmap vLPIs affected by changes to GSI routing information
authorOliver Upton <oliver.upton@linux.dev>
Fri, 23 May 2025 19:47:21 +0000 (12:47 -0700)
committerMarc Zyngier <maz@kernel.org>
Fri, 30 May 2025 08:11:29 +0000 (09:11 +0100)
KVM's interrupt infrastructure is dodgy at best, allowing for some ugly
'off label' usage of the various UAPIs. In one example, userspace can
change the routing entry of a particular "GSI" after configuring
irqbypass with KVM_IRQFD. KVM/arm64 is oblivious to this, and winds up
preserving the stale translation in cases where vLPIs are configured.

Honor userspace's intentions and tear down the vLPI mapping if affected
by a "GSI" routing change. Make no attempt to reconstruct vLPIs if the
new target is an MSI and just fall back to software injection.

Tested-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20250523194722.4066715-5-oliver.upton@linux.dev
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/arm.c

index 1de49b48e35eeef6075459db50efb15ada68d064..505d504b52b538e8c6ece3b97d88b07b8e724723 100644 (file)
@@ -2790,6 +2790,7 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
        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)
 {
@@ -2803,6 +2804,28 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
        kvm_vgic_v4_unset_forwarding(irqfd->kvm, prod->irq);
 }
 
+bool kvm_arch_irqfd_route_changed(struct kvm_kernel_irq_routing_entry *old,
+                                 struct kvm_kernel_irq_routing_entry *new)
+{
+       if (new->type != KVM_IRQ_ROUTING_MSI)
+               return true;
+
+       return memcmp(&old->msi, &new->msi, sizeof(new->msi));
+}
+
+int kvm_arch_update_irqfd_routing(struct kvm *kvm, unsigned int host_irq,
+                                 uint32_t guest_irq, bool set)
+{
+       /*
+        * Remapping the vLPI requires taking the its_lock mutex to resolve
+        * the new translation. We're in spinlock land at this point, so no
+        * chance of resolving the translation.
+        *
+        * Unmap the vLPI and fall back to software LPI injection.
+        */
+       return kvm_vgic_v4_unset_forwarding(kvm, host_irq);
+}
+
 void kvm_arch_irq_bypass_stop(struct irq_bypass_consumer *cons)
 {
        struct kvm_kernel_irqfd *irqfd =