[PATCH] KVM: arm64: Don't livelock when AT can't resolve a fault IPA
Marc Zyngier
maz at kernel.org
Mon Nov 2 10:41:15 PST 2026
On Mon, 02 Nov 2026 18:23:09 +0000,
"Ard Biesheuvel" <ardb at kernel.org> wrote:
>
>
>
> On Mon, 2 Nov 2026, at 17:52, Marc Zyngier wrote:
> > On Mon, 02 Nov 2026 15:57:51 +0000,
> > Fuad Tabba <fuad.tabba at linux.dev> wrote:
> >>
> >> Hi Will,
> >>
> >> On Mon, 02 Nov 2026 14:17:10 +0000, Will Deacon <will at kernel.org> wrote:
> >> > On Mon, Nov 02, 2026 at 02:09:25PM +0000, Marc Zyngier wrote:
> >> > > On Mon, 02 Nov 2026 13:59:12 +0000,
> >> > > Will Deacon <will at kernel.org> wrote:
> >> [...]
> >> > > > > diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
> >> [...]
> >> > > > > static inline bool kvm_hyp_handle_memory_fault(struct kvm_vcpu *vcpu,
> >> > > > > u64 *exit_code)
> >> > > > > {
> >> > > > > - if (!__populate_fault_info(vcpu))
> >> > > > > + if (!__populate_fault_info(vcpu)) {
> >> > > >
> >> > > > Presumably we need a barrier (DSB?) here, to ensure that the TLBI happens
> >> > > > after the AT instruction?
> >>
> >> I don't think the TLBI requires a barrier before it, because it
> >> doesn't matter if the AT's walk allocates entries after the TLBI.
> >> Anything it allocates matches memory, and the stage-2 descriptor the
> >> AT faulted on has AF clear, so that descriptor can't be cached:
> >>
> >> R_XCLRD: Translation table entries that generate a Translation
> >> fault, an Address size fault, or an Access flag fault are never cached
> >> in a TLB.
> >>
> >> The stale entries the guest keeps hitting were already there before
> >> the TLBI, and the dsb(nsh) and isb after it are what complete it
> >> against them.
> >>
> >> [... your other reply ... ]
> >> > > Alternatively, VMALLE1 or even VAE1 should be enough to get rid of the
> >> > > S1 TLB.
> >> >
> >> > Ah, sorry, VMALLE1 is what I meant to ask about. I basically haven't
> >> > understood the need to invalidate the stage-2.
> >>
> >> The guest's walk can also go through a stage-2 entry for its
> >> page-table page, cached before the aging cleared that page's AF, and
> >> nothing requires the AT to use that entry:
> >>
> >> R_NPZWX: When an address translation instruction is executed, it is
> >> IMPLEMENTATION DEFINED whether the result can be returned from a TLB,
> >> or a translation table walk occurs.
> >>
> >> So if my reading is correct, on a CPU whose AT walks memory instead,
> >> the AT keeps failing while the guest's walk goes through the stale
> >> entry, and the guest takes the same permission fault again. VMALLE1
> >> and VAE1 only cover stage 1, so they'd leave that loop in place.
> >
> > If we assume that the TLB covers VA->PA (which is a very likely case),
> > TLBI VAE1 should nuke the permission fault that brought the vcpu to
> > EL2, and instead walk the page tables, getting the access fault.
> >
> > The only case I can imagine where invalidating S2 would matter is if
> > you are running under NV, because there is an independent S2 TLB (the
> > shadow S2), and then VMALLS12E1 makes total sense.
> >
> > Ard, was this solely observed under NV? Or did you see it on a non NV
> > guest as well?
> >
>
> No it happens with guests running at EL1 too.
Damn!
> My experiments seem to indicate that on this uarch, the AT instruction
> does not hit in the TLB, and so it can fail on AF==0 for an IPA that
> is covered by a S2-only TLB entry (with AF==1 and XN==1 due to lazy exec).
>
> So after the VAAE1, the S1 walk happily produces the same translation
> we just invalidated, without taking a S1PTW fault, and the guest faults
> again on the I-fetch that took us to hyp the first time around.
Ah, I think I see what can happen, even without split TLBs.
If you have a S2 walk cache that skips the descriptor where AF=0, then
the TLB refill just succeeds, and the cycle repeats. OK, I'm now
convinced we need S2 invalidation as well even in the VA->PA case. We
needed it for NV anyway, but hey...
Hopefully this doesn't happen too often.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
More information about the linux-arm-kernel
mailing list