KVM: x86: Take irqfds.lock when adding/deleting IRQ bypass producer
authorSean Christopherson <seanjc@google.com>
Fri, 4 Apr 2025 19:38:19 +0000 (12:38 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 24 Apr 2025 13:52:31 +0000 (09:52 -0400)
Take irqfds.lock when adding/deleting an IRQ bypass producer to ensure
irqfd->producer isn't modified while kvm_irq_routing_update() is running.
The only lock held when a producer is added/removed is irqbypass's mutex.

Fixes: 872768800652 ("KVM: x86: select IRQ_BYPASS_MANAGER")
Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-ID: <20250404193923.1413163-5-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/x86.c

index 9c98b77b7dc1bbf76c7d3d364f884836f7f205e2..a6829a370e6a979b64ff2236691a0c90bdf5309b 100644 (file)
@@ -13561,15 +13561,22 @@ 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);
+       struct kvm *kvm = irqfd->kvm;
        int ret;
 
-       irqfd->producer = prod;
        kvm_arch_start_assignment(irqfd->kvm);
+
+       spin_lock_irq(&kvm->irqfds.lock);
+       irqfd->producer = prod;
+
        ret = kvm_x86_call(pi_update_irte)(irqfd->kvm,
                                           prod->irq, irqfd->gsi, 1);
        if (ret)
                kvm_arch_end_assignment(irqfd->kvm);
 
+       spin_unlock_irq(&kvm->irqfds.lock);
+
+
        return ret;
 }
 
@@ -13579,9 +13586,9 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
        int ret;
        struct kvm_kernel_irqfd *irqfd =
                container_of(cons, struct kvm_kernel_irqfd, consumer);
+       struct kvm *kvm = irqfd->kvm;
 
        WARN_ON(irqfd->producer != prod);
-       irqfd->producer = NULL;
 
        /*
         * When producer of consumer is unregistered, we change back to
@@ -13589,12 +13596,18 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
         * when the irq is masked/disabled or the consumer side (KVM
         * int this case doesn't want to receive the interrupts.
        */
+       spin_lock_irq(&kvm->irqfds.lock);
+       irqfd->producer = NULL;
+
        ret = kvm_x86_call(pi_update_irte)(irqfd->kvm,
                                           prod->irq, irqfd->gsi, 0);
        if (ret)
                printk(KERN_INFO "irq bypass consumer (token %p) unregistration"
                       " fails: %d\n", irqfd->consumer.token, ret);
 
+       spin_unlock_irq(&kvm->irqfds.lock);
+
+
        kvm_arch_end_assignment(irqfd->kvm);
 }