Commit ef369168 authored by Marc Zyngier's avatar Marc Zyngier

KVM: arm64: GICv4.1: Fix race with doorbell on VPE activation/deactivation

To save the vgic LPI pending state with GICv4.1, the VPEs must all be
unmapped from the ITSs so that the sGIC caches can be flushed.
The opposite is done once the state is saved.

This is all done by using the activate/deactivate irqdomain callbacks
directly from the vgic code. Crutially, this is done without holding
the irqdesc lock for the interrupts that represent the VPE. And these
callbacks are changing the state of the irqdesc. What could possibly
go wrong?

If a doorbell fires while we are messing with the irqdesc state,
it will acquire the lock and change the interrupt state concurrently.
Since we don't hole the lock, curruption occurs in on the interrupt
state. Oh well.

While acquiring the lock would fix this (and this was Shanker's
initial approach), this is still a layering violation we could do
without. A better approach is actually to free the VPE interrupt,
do what we have to do, and re-request it.

It is more work, but this usually happens only once in the lifetime
of the VM and we don't really care about this sort of overhead.

Fixes: f66b7b15 ("KVM: arm64: GICv4.1: Try to save VLPI state in save_pending_tables")
Reported-by: default avatarShanker Donthineni <sdonthineni@nvidia.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20230118022348.4137094-1-sdonthineni@nvidia.com
parent c3b37c2d
...@@ -350,26 +350,23 @@ int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq) ...@@ -350,26 +350,23 @@ int vgic_v3_lpi_sync_pending_status(struct kvm *kvm, struct vgic_irq *irq)
* The deactivation of the doorbell interrupt will trigger the * The deactivation of the doorbell interrupt will trigger the
* unmapping of the associated vPE. * unmapping of the associated vPE.
*/ */
static void unmap_all_vpes(struct vgic_dist *dist) static void unmap_all_vpes(struct kvm *kvm)
{ {
struct irq_desc *desc; struct vgic_dist *dist = &kvm->arch.vgic;
int i; int i;
for (i = 0; i < dist->its_vm.nr_vpes; i++) { for (i = 0; i < dist->its_vm.nr_vpes; i++)
desc = irq_to_desc(dist->its_vm.vpes[i]->irq); free_irq(dist->its_vm.vpes[i]->irq, kvm_get_vcpu(kvm, i));
irq_domain_deactivate_irq(irq_desc_get_irq_data(desc));
}
} }
static void map_all_vpes(struct vgic_dist *dist) static void map_all_vpes(struct kvm *kvm)
{ {
struct irq_desc *desc; struct vgic_dist *dist = &kvm->arch.vgic;
int i; int i;
for (i = 0; i < dist->its_vm.nr_vpes; i++) { for (i = 0; i < dist->its_vm.nr_vpes; i++)
desc = irq_to_desc(dist->its_vm.vpes[i]->irq); WARN_ON(vgic_v4_request_vpe_irq(kvm_get_vcpu(kvm, i),
irq_domain_activate_irq(irq_desc_get_irq_data(desc), false); dist->its_vm.vpes[i]->irq));
}
} }
/** /**
...@@ -394,7 +391,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) ...@@ -394,7 +391,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
* and enabling of the doorbells have already been done. * and enabling of the doorbells have already been done.
*/ */
if (kvm_vgic_global_state.has_gicv4_1) { if (kvm_vgic_global_state.has_gicv4_1) {
unmap_all_vpes(dist); unmap_all_vpes(kvm);
vlpi_avail = true; vlpi_avail = true;
} }
...@@ -444,7 +441,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) ...@@ -444,7 +441,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
out: out:
if (vlpi_avail) if (vlpi_avail)
map_all_vpes(dist); map_all_vpes(kvm);
return ret; return ret;
} }
......
...@@ -222,6 +222,11 @@ void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val) ...@@ -222,6 +222,11 @@ void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val)
*val = !!(*ptr & mask); *val = !!(*ptr & mask);
} }
int vgic_v4_request_vpe_irq(struct kvm_vcpu *vcpu, int irq)
{
return request_irq(irq, vgic_v4_doorbell_handler, 0, "vcpu", vcpu);
}
/** /**
* vgic_v4_init - Initialize the GICv4 data structures * vgic_v4_init - Initialize the GICv4 data structures
* @kvm: Pointer to the VM being initialized * @kvm: Pointer to the VM being initialized
...@@ -283,8 +288,7 @@ int vgic_v4_init(struct kvm *kvm) ...@@ -283,8 +288,7 @@ int vgic_v4_init(struct kvm *kvm)
irq_flags &= ~IRQ_NOAUTOEN; irq_flags &= ~IRQ_NOAUTOEN;
irq_set_status_flags(irq, irq_flags); irq_set_status_flags(irq, irq_flags);
ret = request_irq(irq, vgic_v4_doorbell_handler, ret = vgic_v4_request_vpe_irq(vcpu, irq);
0, "vcpu", vcpu);
if (ret) { if (ret) {
kvm_err("failed to allocate vcpu IRQ%d\n", irq); kvm_err("failed to allocate vcpu IRQ%d\n", irq);
/* /*
......
...@@ -331,5 +331,6 @@ int vgic_v4_init(struct kvm *kvm); ...@@ -331,5 +331,6 @@ int vgic_v4_init(struct kvm *kvm);
void vgic_v4_teardown(struct kvm *kvm); void vgic_v4_teardown(struct kvm *kvm);
void vgic_v4_configure_vsgis(struct kvm *kvm); void vgic_v4_configure_vsgis(struct kvm *kvm);
void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val); void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val);
int vgic_v4_request_vpe_irq(struct kvm_vcpu *vcpu, int irq);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment