| From f1fb088d9cecde5c3066d8ff8846789667519b7d Mon Sep 17 00:00:00 2001 |
| From: Sean Christopherson <seanjc@google.com> |
| Date: Fri, 4 Apr 2025 12:38:19 -0700 |
| Subject: KVM: x86: Take irqfds.lock when adding/deleting IRQ bypass producer |
| |
| From: Sean Christopherson <seanjc@google.com> |
| |
| commit f1fb088d9cecde5c3066d8ff8846789667519b7d upstream. |
| |
| 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> |
| [sean: account for lack of kvm_x86_call()] |
| Signed-off-by: Sean Christopherson <seanjc@google.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/kvm/x86.c | 19 ++++++++++++++++--- |
| 1 file changed, 16 insertions(+), 3 deletions(-) |
| |
| --- a/arch/x86/kvm/x86.c |
| +++ b/arch/x86/kvm/x86.c |
| @@ -13387,16 +13387,22 @@ int kvm_arch_irq_bypass_add_producer(str |
| { |
| 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 = static_call(kvm_x86_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; |
| } |
| |
| @@ -13406,9 +13412,9 @@ void kvm_arch_irq_bypass_del_producer(st |
| 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 |
| @@ -13416,11 +13422,18 @@ void kvm_arch_irq_bypass_del_producer(st |
| * 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 = static_call(kvm_x86_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); |
| } |
| |