Skip to content

Commit a3f574c

Browse files
author
Marc Zyngier
committed
KVM: arm64: vgic-v4: Plug race between non-residency and v4.1 doorbell
When making a vPE non-resident because it has hit a blocking WFI, the doorbell can fire at any time after the write to the RD. Crucially, it can fire right between the write to GICR_VPENDBASER and the write to the pending_last field in the its_vpe structure. This means that we would overwrite pending_last with stale data, and potentially not wakeup until some unrelated event (such as a timer interrupt) puts the vPE back on the CPU. GICv4 isn't affected by this as we actively mask the doorbell on entering the guest, while GICv4.1 automatically manages doorbell delivery without any hypervisor-driven masking. Use the vpe_lock to synchronize such update, which solves the problem altogether. Fixes: ae699ad ("irqchip/gic-v4.1: Move doorbell management to the GICv4 abstraction layer") Reported-by: Zenghui Yu <[email protected]> Signed-off-by: Marc Zyngier <[email protected]>
1 parent a25e910 commit a3f574c

File tree

2 files changed

+16
-0
lines changed

2 files changed

+16
-0
lines changed

arch/arm64/kvm/vgic/vgic-v4.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,15 @@ static irqreturn_t vgic_v4_doorbell_handler(int irq, void *info)
9090
!irqd_irq_disabled(&irq_to_desc(irq)->irq_data))
9191
disable_irq_nosync(irq);
9292

93+
/*
94+
* The v4.1 doorbell can fire concurrently with the vPE being
95+
* made non-resident. Ensure we only update pending_last
96+
* *after* the non-residency sequence has completed.
97+
*/
98+
raw_spin_lock(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vpe_lock);
9399
vcpu->arch.vgic_cpu.vgic_v3.its_vpe.pending_last = true;
100+
raw_spin_unlock(&vcpu->arch.vgic_cpu.vgic_v3.its_vpe.vpe_lock);
101+
94102
kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu);
95103
kvm_vcpu_kick(vcpu);
96104

drivers/irqchip/irq-gic-v3-its.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4054,16 +4054,24 @@ static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
40544054
u64 val;
40554055

40564056
if (info->req_db) {
4057+
unsigned long flags;
4058+
40574059
/*
40584060
* vPE is going to block: make the vPE non-resident with
40594061
* PendingLast clear and DB set. The GIC guarantees that if
40604062
* we read-back PendingLast clear, then a doorbell will be
40614063
* delivered when an interrupt comes.
4064+
*
4065+
* Note the locking to deal with the concurrent update of
4066+
* pending_last from the doorbell interrupt handler that can
4067+
* run concurrently.
40624068
*/
4069+
raw_spin_lock_irqsave(&vpe->vpe_lock, flags);
40634070
val = its_clear_vpend_valid(vlpi_base,
40644071
GICR_VPENDBASER_PendingLast,
40654072
GICR_VPENDBASER_4_1_DB);
40664073
vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast);
4074+
raw_spin_unlock_irqrestore(&vpe->vpe_lock, flags);
40674075
} else {
40684076
/*
40694077
* We're not blocking, so just make the vPE non-resident

0 commit comments

Comments
 (0)