| From 2b7324f3a0c1072b9d578b8d42f199506753f26e Mon Sep 17 00:00:00 2001 |
| From: Marc Zyngier <maz@kernel.org> |
| Date: Thu, 6 Aug 2026 10:10:25 +0100 |
| Subject: KVM: arm64: Make VNCR invalidation participate in MMU invalidation retry |
| |
| From: Marc Zyngier <maz@kernel.org> |
| |
| commit 2b7324f3a0c1072b9d578b8d42f199506753f26e upstream. |
| |
| A VNCR TLB invalidation can occur on one vcpu while another vcpu is |
| faulting in this same page. Without correctly handling this, we can |
| end up with the following scenario: |
| |
| - vcpu A walks the PTs to translate VNCR |
| - before vcpu A is able to grab the MMU lock to insert the TLB, |
| vcpu B updates the S1 PTs with an invalid entry, and issues |
| a TLBI S1E2 for this VA |
| - vcpu A inserts the TLB for something that is now invalid |
| |
| This isn't a new problem, and we manage S2 by having the MMU notifier |
| to bump up mmu_invalidate_seq on invalidation so that the fault can be |
| replayed. |
| |
| We can perform something similar here, and extend invalidate_vncr_va() to |
| update the same counter, clearly indicating that the context has |
| changed under our feet. This is safe as the invalidation always happen |
| while holding the MMU lock for write, and that we sample the sequence |
| number before walking S1. |
| |
| Fixes: 4ffa72ad8f37e ("KVM: arm64: nv: Add S1 TLB invalidation primitive for VNCR_EL2") |
| Reported-by: sashiko-bot@kernel.org |
| Link: https://lore.kernel.org/r/20260801130454.5D9F11F00AC4@smtp.kernel.org |
| Signed-off-by: Marc Zyngier <maz@kernel.org> |
| Cc: stable@vger.kernel.org |
| Link: https://patch.msgid.link/20260806091026.620700-8-maz@kernel.org |
| Signed-off-by: Oliver Upton <oupton@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/arm64/kvm/nested.c | 21 ++++++++++++++++++--- |
| 1 file changed, 18 insertions(+), 3 deletions(-) |
| |
| --- a/arch/arm64/kvm/nested.c |
| +++ b/arch/arm64/kvm/nested.c |
| @@ -1060,6 +1060,12 @@ static void kvm_invalidate_vncr_ipa(stru |
| if (!kvm_has_feat(kvm, ID_AA64MMFR4_EL1, NV_frac, NV2_ONLY)) |
| return; |
| |
| + /* |
| + * Note that invalidating the VNCR on the back of an MMU notifier |
| + * doesn't require messing with the invalidation counter for a |
| + * parallel walk. The notifier itself will have bumped the counter, |
| + * making sure we rewalk. |
| + */ |
| kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) |
| if (vncr_tlb_intersects(vt, vt->wr.pa, start, end - start)) |
| invalidate_vncr(vt); |
| @@ -1087,6 +1093,15 @@ static void invalidate_vncr_va(struct kv |
| |
| lockdep_assert_held_write(&kvm->mmu_lock); |
| |
| + /* |
| + * We might be performing a parallel S1 walk, so bump up the |
| + * invalidation counter even in the absence of an actual VNCR TLB |
| + * invalidation, as this could indicate that the guest has gone |
| + * through a BBM sequence. |
| + */ |
| + kvm->mmu_invalidate_seq++; |
| + smp_wmb(); |
| + |
| kvm_for_each_vncr_tlb(i, vcpu, vt, kvm) { |
| switch (scope->type) { |
| case TLBI_ALL: |
| @@ -1421,15 +1436,15 @@ static int kvm_translate_vncr(struct kvm |
| |
| va = read_vncr_el2(vcpu); |
| |
| + mmu_seq = vcpu->kvm->mmu_invalidate_seq; |
| + smp_rmb(); |
| + |
| ret = __kvm_translate_va(vcpu, &vt->wi, &vt->wr, va); |
| if (ret) |
| return ret; |
| |
| write_fault = kvm_is_write_fault(vcpu); |
| |
| - mmu_seq = vcpu->kvm->mmu_invalidate_seq; |
| - smp_rmb(); |
| - |
| gfn = vt->wr.pa >> PAGE_SHIFT; |
| memslot = gfn_to_memslot(vcpu->kvm, gfn); |
| if (!memslot) { |