Commit f1fb088d authored by Sean Christopherson's avatar Sean Christopherson Committed by Paolo Bonzini
Browse files

KVM: x86: Take irqfds.lock when adding/deleting IRQ bypass producer



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: 87276880 ("KVM: x86: select IRQ_BYPASS_MANAGER")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Message-ID: <20250404193923.1413163-5-seanjc@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent bcda70c5
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -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);
}