* [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD
@ 2026-10-13 8:16 Fuad Tabba
2026-10-13 8:16 ` [PATCH v2 1/2] KVM: arm64: vgic-its: Don't map an LPI from more than one event Fuad Tabba
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Fuad Tabba @ 2026-10-13 8:16 UTC (permalink / raw)
To: Marc Zyngier, Oliver Upton
Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara,
linux-arm-kernel, kvmarm, linux-kernel, Fuad Tabba
Hi folks,
Changes since v1 [1]:
- Dropped the second mapping of an LPI at MAPTI, instead of keeping a
doubly mapped LPI out of GICv4 forwarding. (Marc)
- Keyed that check on whether an ITE maps the LPI, not on whether its
vgic_irq exists.
- Added a patch that makes DISCARD remove the pending state. (Marc)
Two fixes to the ITS emulation. The first stops a guest from mapping
one LPI from two events, which can oops a GICv4 host when the VMM then
routes an assigned device's MSI to the second event. The second makes
DISCARD remove the LPI's pending state, as the spec requires.
Tested on QEMU, which emulates a GICv4 ITS. A local reproducer with two
vCPUs DISCARDs an LPI that's pending in the other vCPU's list register.
On rc3, and with patch 2 but without its halt, the guest still takes
the LPI. With the series applied, it doesn't.
Based on Linux 7.3-rc3 (fd73f4a665989).
Cheers,
/fuad
[1] https://lore.kernel.org/all/20261012084743.2228612-1-fuad.tabba@linux.dev/
Fuad Tabba (2):
KVM: arm64: vgic-its: Don't map an LPI from more than one event
KVM: arm64: vgic-its: Remove the pending state of a discarded LPI
arch/arm64/kvm/vgic/vgic-its.c | 50 +++++++++++++++++++++++++++++-----
include/kvm/arm_vgic.h | 1 +
2 files changed, 44 insertions(+), 7 deletions(-)
base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v2 1/2] KVM: arm64: vgic-its: Don't map an LPI from more than one event 2026-10-13 8:16 [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Fuad Tabba @ 2026-10-13 8:16 ` Fuad Tabba 2026-10-13 8:16 ` [PATCH v2 2/2] KVM: arm64: vgic-its: Remove the pending state of a discarded LPI Fuad Tabba 2026-10-13 9:51 ` [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Oliver Upton 2 siblings, 0 replies; 10+ messages in thread From: Fuad Tabba @ 2026-10-13 8:16 UTC (permalink / raw) To: Marc Zyngier, Oliver Upton Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel, Fuad Tabba KVM keeps one vgic_irq per LPI. A MAPTI of an LPI whose vgic_irq already exists reuses it for the new ITE and leaves its target vCPU as it was. Mapping one LPI from two events is UNPREDICTABLE (IHI 0069H.b, 5.2.10), but nothing stops a guest from doing it. If the first MAPTI names a collection the guest never maps and the second a mapped one, the LPI translates through the second event with a NULL target vCPU. When the VMM routes an assigned device's MSI to that event on a host with GICv4 enabled, kvm_vgic_v4_set_forwarding() takes the vPE from the NULL target vCPU and the host oopses: Unable to handle kernel paging request at virtual address 0000000000001980 [...] Call trace: its_build_vmapti_cmd (drivers/irqchip/irq-gic-v3-its.c:943) (P) its_irq_set_vcpu_affinity (drivers/irqchip/irq-gic-v3-its.c:1380) irq_set_vcpu_affinity (kernel/irq/manage.c:670) its_map_vlpi (drivers/irqchip/irq-gic-v4.c:326) kvm_vgic_v4_set_forwarding (arch/arm64/kvm/vgic/vgic-v4.c:485) kvm_arch_irq_bypass_add_producer (arch/arm64/kvm/arm.c:2977) irq_bypass_register_consumer (virt/lib/irqbypass.c:44) kvm_irqfd (arch/arm64/kvm/../../../virt/kvm/eventfd.c:496) kvm_vm_ioctl (arch/arm64/kvm/../../../virt/kvm/kvm_main.c:5270) Track in each vgic_irq whether an ITE maps it. Drop a MAPTI of an LPI that is already mapped, as a failed MAPTI already is. Restoring the ITS tables keeps the first ITE it reads for each LPI and drops any later one, so a guest saved with a double mapping still restores. Key the check on the mapping, not on the vgic_irq. A vgic_irq outlives its ITE while anything holds a reference to it, such as the vCPU it was last queued on. A later MAPTI of such an LPI succeeds as before, and now retargets it to its collection. Fixes: 196b136498b35 ("KVM: arm/arm64: GICv4: Wire mapping/unmapping of VLPIs in VFIO irq bypass") Suggested-by: Marc Zyngier <maz@kernel.org> Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> --- arch/arm64/kvm/vgic/vgic-its.c | 26 ++++++++++++++++++++++++-- include/kvm/arm_vgic.h | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 9e782a4fea7e5..44b92c01bc727 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -772,6 +772,7 @@ static void its_free_ite(struct kvm *kvm, struct its_ite *ite) its_unmap_vlpi(ite->irq->host_irq); irq->hw = false; + irq->mapped = false; } vgic_put_irq(kvm, ite->irq); @@ -780,6 +781,23 @@ static void its_free_ite(struct kvm *kvm, struct its_ite *ite) kfree(ite); } +/* Mapping one LPI from two events is UNPREDICTABLE: the first ITE keeps it. */ +static bool vgic_its_map_lpi(struct kvm *kvm, struct its_ite *ite, + struct vgic_irq *irq, struct kvm_vcpu *vcpu) +{ + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) { + if (!irq->mapped) { + irq->mapped = true; + irq->target_vcpu = vcpu; + ite->irq = irq; + return true; + } + } + + vgic_put_irq(kvm, irq); + return false; +} + static u64 its_cmd_mask_field(u64 *its_cmd, int word, int shift, int size) { return (le64_to_cpu(its_cmd[word]) >> shift) & (BIT_ULL(size) - 1); @@ -1089,13 +1107,14 @@ static int vgic_its_cmd_handle_mapi(struct kvm *kvm, struct vgic_its *its, vcpu = collection_to_vcpu(kvm, collection); irq = vgic_add_lpi(kvm, lpi_nr, vcpu); + if (!IS_ERR(irq) && !vgic_its_map_lpi(kvm, ite, irq, vcpu)) + irq = ERR_PTR(-EEXIST); if (IS_ERR(irq)) { if (new_coll) vgic_its_free_collection(its, coll_id); its_free_ite(kvm, ite); return PTR_ERR(irq); } - ite->irq = irq; return 0; } @@ -2195,7 +2214,10 @@ static int vgic_its_restore_ite(struct vgic_its *its, u32 event_id, its_free_ite(kvm, ite); return PTR_ERR(irq); } - ite->irq = irq; + + /* Drop an ITE whose LPI another ITE maps, as MAPTI does. */ + if (!vgic_its_map_lpi(kvm, ite, irq, vcpu)) + its_free_ite(kvm, ite); return offset; } diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 086b7578e5f33..5c3a0154becc4 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -256,6 +256,7 @@ struct vgic_irq { bool active:1; bool hw:1; /* Tied to HW IRQ */ bool on_lr:1; /* Present in a CPU LR */ + bool mapped:1; /* LPIs: mapped by an ITE */ refcount_t refcount; /* Used for LPIs */ u32 hwintid; /* HW INTID number */ unsigned int host_irq; /* linux irq corresponding to hwintid */ -- 2.39.5 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 2/2] KVM: arm64: vgic-its: Remove the pending state of a discarded LPI 2026-10-13 8:16 [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Fuad Tabba 2026-10-13 8:16 ` [PATCH v2 1/2] KVM: arm64: vgic-its: Don't map an LPI from more than one event Fuad Tabba @ 2026-10-13 8:16 ` Fuad Tabba 2026-10-13 9:51 ` [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Oliver Upton 2 siblings, 0 replies; 10+ messages in thread From: Fuad Tabba @ 2026-10-13 8:16 UTC (permalink / raw) To: Marc Zyngier, Oliver Upton Cc: Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel, Fuad Tabba DISCARD removes the mapping of an event and instructs the Redistributor to remove the pending state of its LPI (IHI 0069H.b, 5.3.4). KVM only frees the ITE, so an LPI that is pending when the guest discards it stays queued on its target vCPU, and the guest still takes it. Clear the pending state before freeing the ITE. A queued LPI can be pending in a running vCPU's list register, so halt the guest first when the LPI is queued, to fold that state back. A forwarded LPI already loses its pending state in its_free_ite(), whose unmap from the vPE sends a DISCARD to the host ITS. Fixes: df9f58fbea9bc ("KVM: arm64: vgic-its: Implement ITS command queue command handlers") Suggested-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> --- arch/arm64/kvm/vgic/vgic-its.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 44b92c01bc727..3b8e0e5b097e0 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -829,13 +829,27 @@ static int vgic_its_cmd_handle_discard(struct kvm *kvm, struct vgic_its *its, struct its_device *device = find_its_device(its, device_id); int ite_esz = vgic_its_get_abi(its)->ite_esz; gpa_t gpa = device->itt_addr + ite->event_id * ite_esz; - /* - * Though the spec talks about removing the pending state, we - * don't bother here since we clear the ITTE anyway and the - * pending state is a property of the ITTE struct. - */ + struct vgic_irq *irq = ite->irq; + bool queued; + vgic_its_invalidate_cache(its); + /* + * A queued LPI can be pending in a running vCPU's list register, + * which the exit path folds back: halt the guest before clearing. + */ + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + queued = irq->vcpu; + + if (queued) + kvm_arm_halt_guest(kvm); + + scoped_guard(raw_spinlock_irqsave, &irq->irq_lock) + irq->pending_latch = false; + + if (queued) + kvm_arm_resume_guest(kvm); + its_free_ite(kvm, ite); return vgic_its_write_entry_lock(its, gpa, 0ULL, ite); -- 2.39.5 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 8:16 [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Fuad Tabba 2026-10-13 8:16 ` [PATCH v2 1/2] KVM: arm64: vgic-its: Don't map an LPI from more than one event Fuad Tabba 2026-10-13 8:16 ` [PATCH v2 2/2] KVM: arm64: vgic-its: Remove the pending state of a discarded LPI Fuad Tabba @ 2026-10-13 9:51 ` Oliver Upton 2026-10-13 10:01 ` Marc Zyngier 2 siblings, 1 reply; 10+ messages in thread From: Oliver Upton @ 2026-10-13 9:51 UTC (permalink / raw) To: Fuad Tabba Cc: Marc Zyngier, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel, Fuad Tabba On Tue, Oct 13, 2026 at 09:16:44AM +0100, Fuad Tabba wrote: > Hi folks, > > Changes since v1 [1]: > - Dropped the second mapping of an LPI at MAPTI, instead of keeping a > doubly mapped LPI out of GICv4 forwarding. (Marc) > - Keyed that check on whether an ITE maps the LPI, not on whether its > vgic_irq exists. > - Added a patch that makes DISCARD remove the pending state. (Marc) > > Two fixes to the ITS emulation. The first stops a guest from mapping > one LPI from two events, which can oops a GICv4 host when the VMM then > routes an assigned device's MSI to the second event. The second makes > DISCARD remove the LPI's pending state, as the spec requires. > > Tested on QEMU, which emulates a GICv4 ITS. A local reproducer with two > vCPUs DISCARDs an LPI that's pending in the other vCPU's list register. > On rc3, and with patch 2 but without its halt, the guest still takes > the LPI. With the series applied, it doesn't. > > Based on Linux 7.3-rc3 (fd73f4a665989). > > Cheers, > /fuad > > [1] https://lore.kernel.org/all/20261012084743.2228612-1-fuad.tabba@linux.dev/ > > Fuad Tabba (2): > KVM: arm64: vgic-its: Don't map an LPI from more than one event > KVM: arm64: vgic-its: Remove the pending state of a discarded LPI Looks good. As all of these VGIC fixes depend on reliably synchronizing hwstate I'll assume Marc will apply this patchset after his own series. Reviewed-by: Oliver Upton <oupton@kernel.org> Thanks, Oliver ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 9:51 ` [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Oliver Upton @ 2026-10-13 10:01 ` Marc Zyngier 2026-10-13 10:50 ` Fuad Tabba 0 siblings, 1 reply; 10+ messages in thread From: Marc Zyngier @ 2026-10-13 10:01 UTC (permalink / raw) To: Oliver Upton Cc: Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel, Fuad Tabba On Tue, 13 Oct 2026 10:51:48 +0100, Oliver Upton <oupton@kernel.org> wrote: > > On Tue, Oct 13, 2026 at 09:16:44AM +0100, Fuad Tabba wrote: > > Hi folks, > > > > Changes since v1 [1]: > > - Dropped the second mapping of an LPI at MAPTI, instead of keeping a > > doubly mapped LPI out of GICv4 forwarding. (Marc) > > - Keyed that check on whether an ITE maps the LPI, not on whether its > > vgic_irq exists. > > - Added a patch that makes DISCARD remove the pending state. (Marc) > > > > Two fixes to the ITS emulation. The first stops a guest from mapping > > one LPI from two events, which can oops a GICv4 host when the VMM then > > routes an assigned device's MSI to the second event. The second makes > > DISCARD remove the LPI's pending state, as the spec requires. > > > > Tested on QEMU, which emulates a GICv4 ITS. A local reproducer with two > > vCPUs DISCARDs an LPI that's pending in the other vCPU's list register. > > On rc3, and with patch 2 but without its halt, the guest still takes > > the LPI. With the series applied, it doesn't. > > > > Based on Linux 7.3-rc3 (fd73f4a665989). > > > > Cheers, > > /fuad > > > > [1] https://lore.kernel.org/all/20261012084743.2228612-1-fuad.tabba@linux.dev/ > > > > Fuad Tabba (2): > > KVM: arm64: vgic-its: Don't map an LPI from more than one event > > KVM: arm64: vgic-its: Remove the pending state of a discarded LPI > > Looks good. As all of these VGIC fixes depend on reliably synchronizing > hwstate I'll assume Marc will apply this patchset after his own series. Yeah, I now have a series that has the counting pause, your single vcpu pause (fixed to DTRT), and the LPI disabling fix. Patch #2 of this series can probably make use of the single-vcpu stunning. As for the OUTSIDE_GUEST discussion, I've had a long discussion with Will where we reversed engineered the synchronisation requirements, and concluded that there was nothing to fix on that front (the logic is incredibly subtle and counter-intuitive, but overall sound). This patch is therefore dropped. I'll post that today, and we can assemble a series. Thanks, M. -- Without deviation from the norm, progress is not possible. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 10:01 ` Marc Zyngier @ 2026-10-13 10:50 ` Fuad Tabba 2026-10-13 11:16 ` Marc Zyngier 0 siblings, 1 reply; 10+ messages in thread From: Fuad Tabba @ 2026-10-13 10:50 UTC (permalink / raw) To: Marc Zyngier Cc: Oliver Upton, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel Hi Marc, On Tue, 13 Oct 2026 11:01:44 +0100, Marc Zyngier <maz@kernel.org> wrote: [...] > Yeah, I now have a series that has the counting pause, your single > vcpu pause (fixed to DTRT), and the LPI disabling fix. Patch #2 of > this series can probably make use of the single-vcpu stunning. I'll rebase the series on that once it's out, with patch 2 using the single-vcpu stunning. > As for the OUTSIDE_GUEST discussion, I've had a long discussion with > Will where we reversed engineered the synchronisation requirements, > and concluded that there was nothing to fix on that front (the logic > is incredibly subtle and counter-intuitive, but overall sound). This > patch is therefore dropped. The first [High] issue Sashiko found on patch 2 [1] holds for DISCARD, because the fold writes back the pending_latch that DISCARD clears. With a 2ms delay after the exit to widen the window, and the DISCARD sent at a varying time, the guest in my DISCARD reproducer takes the discarded LPI in 13 of 20 runs on v2. A vCPU on its way into the guest does the same: it loads its LRs in kvm_vgic_flush_hwstate() while it's still OUTSIDE_GUEST_MODE, so kvm_arm_halt_guest() doesn't wait for it. With the delay after the flush instead, on v2 plus your 1/7 and 2/7 [2] and both changes from your reply to 1/7 [3], the guest takes it in 9 of 20 runs. Once DISCARD waits for irq->on_lr to clear before clearing pending_latch, the guest takes it in none of 20 runs in either case. I'll add that to v3, unless the single-vcpu stunning already waits for it. Cheers, /fuad [1] https://lore.kernel.org/r/sashiko-outbox-169278@kernel.org [2] https://lore.kernel.org/r/20260929093548.3598547-1-maz@kernel.org [3] https://lore.kernel.org/r/86a4p0403b.wl-maz@kernel.org ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 10:50 ` Fuad Tabba @ 2026-10-13 11:16 ` Marc Zyngier 2026-10-13 12:59 ` Fuad Tabba 0 siblings, 1 reply; 10+ messages in thread From: Marc Zyngier @ 2026-10-13 11:16 UTC (permalink / raw) To: Fuad Tabba Cc: Oliver Upton, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel On Tue, 13 Oct 2026 11:50:09 +0100, Fuad Tabba <fuad.tabba@linux.dev> wrote: > > Hi Marc, > > On Tue, 13 Oct 2026 11:01:44 +0100, Marc Zyngier <maz@kernel.org> wrote: > [...] > > Yeah, I now have a series that has the counting pause, your single > > vcpu pause (fixed to DTRT), and the LPI disabling fix. Patch #2 of > > this series can probably make use of the single-vcpu stunning. > > I'll rebase the series on that once it's out, with patch 2 using the > single-vcpu stunning. > > > As for the OUTSIDE_GUEST discussion, I've had a long discussion with > > Will where we reversed engineered the synchronisation requirements, > > and concluded that there was nothing to fix on that front (the logic > > is incredibly subtle and counter-intuitive, but overall sound). This > > patch is therefore dropped. > > The first [High] issue Sashiko found on patch 2 [1] holds for DISCARD, > because the fold writes back the pending_latch that DISCARD clears. > With a 2ms delay after the exit to widen the window, and the DISCARD > sent at a varying time, the guest in my DISCARD reproducer takes the > discarded LPI in 13 of 20 runs on v2. > > A vCPU on its way into the guest does the same: it loads its LRs in > kvm_vgic_flush_hwstate() while it's still OUTSIDE_GUEST_MODE, so > kvm_arm_halt_guest() doesn't wait for it. With the delay after the > flush instead, on v2 plus your 1/7 and 2/7 [2] and both changes from > your reply to 1/7 [3], the guest takes it in 9 of 20 runs. Does this mean we still need OUTSIDE_GUEST_MODE to be a store-release past the point where we resync the state? > Once DISCARD waits for irq->on_lr to clear before clearing > pending_latch, the guest takes it in none of 20 runs in either case. > I'll add that to v3, unless the single-vcpu stunning already waits for > it. Polling for on_lr feels very dodgy, and I'd like to avoid this. If the OUTSIDE_GUEST release addresses it, I'd rather do that. Thanks, M. -- Without deviation from the norm, progress is not possible. ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 11:16 ` Marc Zyngier @ 2026-10-13 12:59 ` Fuad Tabba 2026-10-13 13:25 ` Marc Zyngier 0 siblings, 1 reply; 10+ messages in thread From: Fuad Tabba @ 2026-10-13 12:59 UTC (permalink / raw) To: Marc Zyngier Cc: Oliver Upton, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel Hi Marc, On Tue, 13 Oct 2026 12:16:09 +0100, Marc Zyngier <maz@kernel.org> wrote: [...] > > A vCPU on its way into the guest does the same: it loads its LRs in > > kvm_vgic_flush_hwstate() while it's still OUTSIDE_GUEST_MODE, so > > kvm_arm_halt_guest() doesn't wait for it. With the delay after the > > flush instead, on v2 plus your 1/7 and 2/7 [2] and both changes from > > your reply to 1/7 [3], the guest takes it in 9 of 20 runs. > > Does this mean we still need OUTSIDE_GUEST_MODE to be a store-release > past the point where we resync the state? Not for the 9 of 20: that run already had the release. The halt reads the OUTSIDE_GUEST_MODE the vCPU published at its previous exit, so it doesn't wait for the early exit that folds the LPI back. We do still need it for the window after the exit: with the delay there and the same patches applied, the guest takes it in none of 20 runs, against 13 of 20 on v2. > > Once DISCARD waits for irq->on_lr to clear before clearing > > pending_latch, the guest takes it in none of 20 runs in either case. > > I'll add that to v3, unless the single-vcpu stunning already waits for > > it. > > Polling for on_lr feels very dodgy, and I'd like to avoid this. If the > OUTSIDE_GUEST release addresses it, I'd rather do that. I'd rather avoid the polling too, but the release alone doesn't close this one. The vCPU would also need to store IN_GUEST_MODE and check its requests before kvm_vgic_flush_hwstate(), so that it never loads its LRs while it's OUTSIDE_GUEST_MODE. The existing check after the flush would still be needed, for the KVM_REQ_GUEST_HYP_IRQ_PENDING request kvm_vgic_flush_hwstate() makes in a nested state. With that and the release, the guest takes it in none of 20 runs with the delay after the flush, and herd7 finds no LKMM execution that leaves it pending. The IN_GUEST_MODE change would also cover vgic_mmio_change_active(), which depends on the same halt (the comment above vgic_access_active_prepare()). Would you be OK folding it into your series, on top of your OUTSIDE_GUEST_MODE changes? Patch 2 then wouldn't need the on_lr wait. Cheers, /fuad ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 12:59 ` Fuad Tabba @ 2026-10-13 13:25 ` Marc Zyngier 2026-10-13 14:33 ` Fuad Tabba 0 siblings, 1 reply; 10+ messages in thread From: Marc Zyngier @ 2026-10-13 13:25 UTC (permalink / raw) To: Fuad Tabba Cc: Oliver Upton, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel On Tue, 13 Oct 2026 13:59:22 +0100, Fuad Tabba <fuad.tabba@linux.dev> wrote: > > Hi Marc, > > On Tue, 13 Oct 2026 12:16:09 +0100, Marc Zyngier <maz@kernel.org> wrote: > [...] > > > A vCPU on its way into the guest does the same: it loads its LRs in > > > kvm_vgic_flush_hwstate() while it's still OUTSIDE_GUEST_MODE, so > > > kvm_arm_halt_guest() doesn't wait for it. With the delay after the > > > flush instead, on v2 plus your 1/7 and 2/7 [2] and both changes from > > > your reply to 1/7 [3], the guest takes it in 9 of 20 runs. > > > > Does this mean we still need OUTSIDE_GUEST_MODE to be a store-release > > past the point where we resync the state? > > Not for the 9 of 20: that run already had the release. The halt reads > the OUTSIDE_GUEST_MODE the vCPU published at its previous exit, so it > doesn't wait for the early exit that folds the LPI back. > > We do still need it for the window after the exit: with the delay > there and the same patches applied, the guest takes it in none of 20 > runs, against 13 of 20 on v2. I don't understand what these probabilities represent. Either it works or it doesn't. And given that I have no idea what you are testing with, I can't really make head or tail of the above. > > > > Once DISCARD waits for irq->on_lr to clear before clearing > > > pending_latch, the guest takes it in none of 20 runs in either case. > > > I'll add that to v3, unless the single-vcpu stunning already waits for > > > it. > > > > Polling for on_lr feels very dodgy, and I'd like to avoid this. If the > > OUTSIDE_GUEST release addresses it, I'd rather do that. > > I'd rather avoid the polling too, but the release alone doesn't close > this one. The vCPU would also need to store IN_GUEST_MODE and check > its requests before kvm_vgic_flush_hwstate(), so that it never loads > its LRs while it's OUTSIDE_GUEST_MODE. The existing check after the > flush would still be needed, for the KVM_REQ_GUEST_HYP_IRQ_PENDING > request kvm_vgic_flush_hwstate() makes in a nested state. With that > and the release, the guest takes it in none of 20 runs with the delay > after the flush, and herd7 finds no LKMM execution that leaves it > pending. If you have a herd litmus test, how about posting it? It would really avoid a lot of that back and forth where the S/N ratio is pretty poor. > The IN_GUEST_MODE change would also cover vgic_mmio_change_active(), > which depends on the same halt (the comment above > vgic_access_active_prepare()). Would you be OK folding it into your > series, on top of your OUTSIDE_GUEST_MODE changes? Patch 2 then > wouldn't need the on_lr wait. Is the hack below what you are asking for? I can't tell. If it isn't, please post your own version. Thanks, M. diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 0576c2022ef57..f1449b08531eb 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1321,10 +1321,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) local_irq_disable(); - kvm_vgic_flush_hwstate(vcpu); - - kvm_pmu_update_vcpu_events(vcpu); - /* * Ensure we set mode to IN_GUEST_MODE after we disable * interrupts and before the final VCPU requests check. @@ -1333,14 +1329,18 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) */ smp_store_mb(vcpu->mode, IN_GUEST_MODE); + kvm_vgic_flush_hwstate(vcpu); + + kvm_pmu_update_vcpu_events(vcpu); + if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) { - vcpu->mode = OUTSIDE_GUEST_MODE; isb(); /* Ensure work in x_flush_hwstate is committed */ if (kvm_vcpu_has_pmu(vcpu)) kvm_pmu_sync_hwstate(vcpu); if (unlikely(!irqchip_in_kernel(vcpu->kvm))) kvm_timer_sync_user(vcpu); kvm_vgic_sync_hwstate(vcpu); + smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE); local_irq_enable(); preempt_enable(); continue; @@ -1356,7 +1356,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) ret = kvm_arm_vcpu_enter_exit(vcpu); - vcpu->mode = OUTSIDE_GUEST_MODE; vcpu->stat.exits++; /* * Back from guest @@ -1390,6 +1389,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_arch_vcpu_ctxsync_fp(vcpu); + /* + * All the state has been synchronised, let advertise + * we're outside of the guest. + */ + smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE); + /* * We must ensure that any pending interrupts are taken before * we exit guest timing so that timer ticks are accounted as -- Without deviation from the norm, progress is not possible. ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD 2026-10-13 13:25 ` Marc Zyngier @ 2026-10-13 14:33 ` Fuad Tabba 0 siblings, 0 replies; 10+ messages in thread From: Fuad Tabba @ 2026-10-13 14:33 UTC (permalink / raw) To: Marc Zyngier Cc: Oliver Upton, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Mark Rutland, Andre Przywara, linux-arm-kernel, kvmarm, linux-kernel, Fuad Tabba Hi Marc, On Tue, 13 Oct 2026 14:25:09 +0100, Marc Zyngier <maz@kernel.org> wrote: [...] > > We do still need it for the window after the exit: with the delay > > there and the same patches applied, the guest takes it in none of 20 > > runs, against 13 of 20 on v2. > > I don't understand what these probabilities represent. Either it works > or it doesn't. And given that I have no idea what you are testing > with, I can't really make head or tail of the above. They're the number of runs, out of 20, in which the guest took the discarded LPI. My reproducer has vCPU 0 issue DISCARD for an LPI pending in vCPU 1's LR, at a varying time, with a debug mdelay() widening the window, so only some runs hit it. I've since replaced the delay with debug hooks that force the race, so now it either works or it doesn't. [...] > If you have a herd litmus test, how about posting it? It would really > avoid a lot of that back and forth where the S/N ratio is pretty poor. Sure, here are two, run with herd7 7.58 and the LKMM from v7.3-rc3. The first models your diff and comes out Sometimes. The second adds the request check before the flush and comes out Never. C discard+halt (* * P0 is the vCPU in kvm_arch_vcpu_ioctl_run() with your diff, with a * pending LPI queued on it: check_vcpu_requests(), the IN_GUEST_MODE * store, the flush moving pending_latch into an LR, the fold (on the * early exit, or after a guest run that leaves the LPI pending), and * the release of OUTSIDE_GUEST_MODE. * * P1 is DISCARD: kvm_arm_halt_guest() making KVM_REQ_SLEEP and reading * the mode in kvm_vcpu_exiting_guest_mode(), then clearing * pending_latch. A halt that reads IN or EXITING (1, 2) waits for the * IPI, which the vCPU takes after its fold, so only one that reads * OUTSIDE (0) clears here. *) { pending_latch = 1; } P0(int *mode, int *requests, int *pending_latch, spinlock_t *irq_lock) { int r0; int r1 = 0; r0 = READ_ONCE(*requests); if (r0 == 0) { smp_store_mb(*mode, 1); spin_lock(irq_lock); r1 = READ_ONCE(*pending_latch); if (r1 == 1) WRITE_ONCE(*pending_latch, 0); spin_unlock(irq_lock); spin_lock(irq_lock); if (r1 == 1) WRITE_ONCE(*pending_latch, 1); spin_unlock(irq_lock); smp_store_release(mode, 0); } } P1(int *mode, int *requests, int *pending_latch, spinlock_t *irq_lock) { int r3; smp_wmb(); WRITE_ONCE(*requests, 1); smp_mb__before_atomic(); r3 = cmpxchg(mode, 1, 2); if (r3 == 0) { spin_lock(irq_lock); WRITE_ONCE(*pending_latch, 0); spin_unlock(irq_lock); } } exists (0:r1=1 /\ 1:r3=0 /\ pending_latch=1) C discard+halt+check (* * As discard+halt, with the request check between the IN_GUEST_MODE * store and the flush. *) { pending_latch = 1; } P0(int *mode, int *requests, int *pending_latch, spinlock_t *irq_lock) { int r0; int r1 = 0; int r2; r0 = READ_ONCE(*requests); if (r0 == 0) { smp_store_mb(*mode, 1); r2 = READ_ONCE(*requests); if (r2 == 0) { spin_lock(irq_lock); r1 = READ_ONCE(*pending_latch); if (r1 == 1) WRITE_ONCE(*pending_latch, 0); spin_unlock(irq_lock); spin_lock(irq_lock); if (r1 == 1) WRITE_ONCE(*pending_latch, 1); spin_unlock(irq_lock); } smp_store_release(mode, 0); } } P1(int *mode, int *requests, int *pending_latch, spinlock_t *irq_lock) { int r3; smp_wmb(); WRITE_ONCE(*requests, 1); smp_mb__before_atomic(); r3 = cmpxchg(mode, 1, 2); if (r3 == 0) { spin_lock(irq_lock); WRITE_ONCE(*pending_latch, 0); spin_unlock(irq_lock); } } exists (0:r1=1 /\ 1:r3=0 /\ pending_latch=1) [...] > Is the hack below what you are asking for? I can't tell. If it isn't, > please post your own version. Almost: it's missing a request check between the IN_GUEST_MODE store and the flush. Without it, a halt that reads OUTSIDE_GUEST_MODE just before the store doesn't wait for the vCPU, which then loads the LPI into an LR, takes the early exit on KVM_REQ_SLEEP and folds the LPI back after DISCARD has cleared it. That's the outcome in the first litmus test's exists clause. With the debug hooks forcing it on QEMU, the guest takes the discarded LPI in every run with your diff, and in none with this on top: diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index f1449b0..896bc76 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1329,11 +1329,29 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) */ smp_store_mb(vcpu->mode, IN_GUEST_MODE); + /* + * Don't load the LRs with a request pending: + * kvm_arm_halt_guest() doesn't wait for a vCPU it reads + * as OUTSIDE_GUEST_MODE. + */ + if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) { + isb(); /* Ensure work in x_flush_hwstate is committed */ + if (kvm_vcpu_has_pmu(vcpu)) + kvm_pmu_sync_hwstate(vcpu); + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) + kvm_timer_sync_user(vcpu); + smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE); + local_irq_enable(); + preempt_enable(); + continue; + } + kvm_vgic_flush_hwstate(vcpu); kvm_pmu_update_vcpu_events(vcpu); - if (ret <= 0 || kvm_vcpu_exit_request(vcpu, &ret)) { + /* kvm_vgic_flush_hwstate() can make a request when nested */ + if (kvm_vcpu_exit_request(vcpu, &ret)) { isb(); /* Ensure work in x_flush_hwstate is committed */ if (kvm_vcpu_has_pmu(vcpu)) kvm_pmu_sync_hwstate(vcpu); Cheers, /fuad ^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-13 14:33 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-13 8:16 [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Fuad Tabba 2026-10-13 8:16 ` [PATCH v2 1/2] KVM: arm64: vgic-its: Don't map an LPI from more than one event Fuad Tabba 2026-10-13 8:16 ` [PATCH v2 2/2] KVM: arm64: vgic-its: Remove the pending state of a discarded LPI Fuad Tabba 2026-10-13 9:51 ` [PATCH v2 0/2] KVM: arm64: vgic-its: Fix LPI double mapping and DISCARD Oliver Upton 2026-10-13 10:01 ` Marc Zyngier 2026-10-13 10:50 ` Fuad Tabba 2026-10-13 11:16 ` Marc Zyngier 2026-10-13 12:59 ` Fuad Tabba 2026-10-13 13:25 ` Marc Zyngier 2026-10-13 14:33 ` Fuad Tabba
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox