From: Oliver Upton <oupton@kernel.org>
To: Wei-Lin Chang <weilin.chang@arm.com>
Cc: linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
linux-kernel@vger.kernel.org, Marc Zyngier <maz@kernel.org>,
Fuad Tabba <tabba@google.com>, Joey Gouly <joey.gouly@arm.com>,
Steffen Eiden <seiden@linux.ibm.com>,
Suzuki K Poulose <suzuki.poulose@arm.com>,
Zenghui Yu <yuzenghui@huawei.com>,
Catalin Marinas <catalin.marinas@arm.com>,
Will Deacon <will@kernel.org>,
Itaru Kitayama <itaru.kitayama@fujitsu.com>,
Sebastian Ene <sebastianene@google.com>
Subject: Re: [PATCH v4 2/6] KVM: arm64: nv: Avoid full shadow s2 unmap
Date: Thu, 16 Jul 2026 10:16:51 -0700 [thread overview]
Message-ID: <alkSAyfhI4n_VAok@kernel.org> (raw)
In-Reply-To: <upkq27f2mo3fy27ubkxxgfwkgn2oamnbxom2o6gce5ocfyauo5@6tfu3ppaw7o6>
On Thu, Jul 16, 2026 at 05:14:46PM +0100, Wei-Lin Chang wrote:
> On Thu, Jul 16, 2026 at 12:05:29AM -0700, Oliver Upton wrote:
> > Hey,
> >
> > On Tue, Jul 14, 2026 at 12:59:21PM +0100, Wei-Lin Chang wrote:
> > > +static bool valid_entry(unsigned long entry)
> > > +{
> > > + WARN_ON(entry & VALID_ENTRY && entry & UNKNOWN_IPA);
> > > + return entry & VALID_ENTRY;
> > > +}
> > > +
> > > +static bool unknown_ipa_entry(unsigned long entry)
> > > +{
> > > + WARN_ON(entry & VALID_ENTRY && entry & UNKNOWN_IPA);
> > > + return entry & UNKNOWN_IPA;
> > > +}
> >
> > These should be WARN_ON_ONCE(), but I find the condition you're
> > asserting to be a bit confusing. An aliased reverse map entry is still a
> > valid entry, would you not set the valid bit?
>
> Sorry, VALID_ENTRY might not be the best name for this flag. I used it
> to mean entries in the maple tree, that are not UNKNOWN_IPA, meaning the
> canonical IPA <-> nested IPA relation is one-to-one.
>
> So by definition UNKNOWN_IPA and "VALID_ENTRY" should not be set at the
> same time. Unfortunately they can't be a single flag, as the stored
> address can be 0, this can make the whole value 0 when being stored if
> the single flag is also 0, effectively not doing the store.
>
> Maybe it's better with just "KNOWN_IPA"?
Does anything break with:
state {VALID, UNKNOWN}
invalid entry (NULL) {0, 0}
valid IPA {1, 0}
aliased IPA {1, 1}
It's just the assertion that the bits are mutually exclusive that I find
confusing, agree that we need to have two bits of state.
> >
> > > +void kvm_record_nested_revmap(gpa_t canonical_ipa, struct kvm_s2_mmu *mmu,
> > > + gpa_t nested_ipa, size_t map_size)
> > > +{
> > > + struct maple_tree *revmap_mt = &mmu->nested_revmap_mt;
> > > + gpa_t canonical_ipa_end;
> > > + u64 entry, new_entry = 0;
> > > +
> > > + lockdep_assert_held_read(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock);
> > > +
> > > + if (mmu->nested_revmap_broken)
> > > + return;
> > > +
> > > + if (WARN_ON(!IS_ALIGNED(canonical_ipa, map_size)))
> > > + canonical_ipa = ALIGN_DOWN(canonical_ipa, map_size);
> > > +
> > > + canonical_ipa_end = canonical_ipa + map_size - 1;
> > > + MA_STATE(mas_rmap, revmap_mt, canonical_ipa, canonical_ipa_end);
> > > +
> > > + mtree_lock(revmap_mt);
> > > + entry = xa_to_value(mas_find(&mas_rmap, canonical_ipa_end));
> > > +
> > > + if (entry) {
> > > + /* parallel faults can be adding the same mapping */
> > > + if (valid_entry(entry) &&
> > > + mas_rmap.index == canonical_ipa &&
> > > + mas_rmap.last == canonical_ipa_end &&
> > > + nested_ipa == (entry & ADDR_MASK))
> > > + goto unlock;
> > > + /*
> > > + * Create a "UNKNOWN_IPA" range that spans all the overlapping
> > > + * ranges and store it.
> > > + */
> > > + while (entry && mas_rmap.index <= canonical_ipa_end) {
> > > + canonical_ipa = min(mas_rmap.index, canonical_ipa);
> > > + canonical_ipa_end = max(mas_rmap.last, canonical_ipa_end);
> > > + entry = xa_to_value(mas_find(&mas_rmap, canonical_ipa_end));
> > > + }
> > > + new_entry |= UNKNOWN_IPA;
> > > + } else {
> > > + new_entry |= nested_ipa;
> > > + new_entry |= VALID_ENTRY;
> > > + }
> > > +
> > > + mas_set_range(&mas_rmap, canonical_ipa, canonical_ipa_end);
> > > + if (mas_store_gfp(&mas_rmap, xa_mk_value(new_entry),
> > > + GFP_NOWAIT | __GFP_ACCOUNT))
> >
> > The general pattern for handling this situation would be to preallocate
> > nodes prior to acquiring the lock. If we can preallocate then we can
> > just outright refuse to create a shadow stage-2 mapping if the update to
> > the reverse map fails.
>
> Preallocation was thought about, but it was found to be impractical in
> this case [*], mainly because we must hold the lock in order to know
> what range to store.
Could we perform an RCU-protected walk to predict the range we intend to
store and preallocate based off of that? And if we're not able to
preallocate from a sleepable context then bailing to userspace seems
appropriate.
> However, we can make this function return an error when storing to the
> maple tree fails, and just not do the shadow mapping. This ties the
> wellness of the maple tree to guest progress. For repeated failure I
> think of these possibilities:
>
> - The maple tree code is somehow broken that stores never succeed.
> - We have so little available memory that maple tree stores don't
> succeed.
>
> These cause the guest fault repeatedly, but do we still care about the
> guest when these happen? Probably not for the OOM case, and it solves
> itself when the kernel gets back memory. For the first case I'm not so
> sure, and I might have missed some other important possibilities.
So there's two failure modes that we need to handle.
The most common case would be a race to update the maple tree, where the
preallocation we did outside of the MMU lock is insufficient for the actual
operation we perform. Typically in the event of a race we restart the faulting
instruction and re-attempt the fault. So long as you pass gfp=0 to the
store, you can reinterpret -ENOMEM as -EAGAIN and refault.
At that point, any non ENOMEM failure to store in the maple tree can be
treated as fatal for the VM, KVM_BUG_ON() and out to userspace.
> > > +static void unmap_mmu_cipa_range(struct kvm_s2_mmu *mmu, gpa_t canonical_ipa,
> > > + size_t unmap_size, bool may_block)
> > > +{
> > > + struct maple_tree *revmap_mt = &mmu->nested_revmap_mt;
> > > + gpa_t canonical_ipa_end = canonical_ipa + unmap_size - 1;
> > > + size_t entry_size;
> > > + gpa_t next_addr;
> > > + u64 entry;
> > > + MA_STATE(mas_rmap, revmap_mt, canonical_ipa, canonical_ipa_end);
> > > +
> > > + lockdep_assert_held_write(&kvm_s2_mmu_to_kvm(mmu)->mmu_lock);
> > > +
> > > + if (mmu->nested_revmap_broken) {
> > > + reset_revmap_and_unmap(mmu, may_block);
> > > + return;
> > > + }
> > > +
> > > + if (!mmu->nested_stage2_enabled) {
> > > + kvm_stage2_unmap_range(mmu, canonical_ipa, unmap_size, may_block);
> > > + return;
> > > + }
> > > +
> > > + mtree_lock(revmap_mt);
> > > + entry = xa_to_value(mas_find(&mas_rmap, canonical_ipa_end));
> > > +
> > > + while (entry && mas_rmap.index <= canonical_ipa_end) {
> > > + entry_size = mas_rmap.last - mas_rmap.index + 1;
> > > + next_addr = mas_rmap.index + entry_size;
> > > + /*
> > > + * Give up and invalidate this s2 mmu if the unmap range
> > > + * touches any UNKNOWN_IPA range.
> > > + */
> > > + if (unknown_ipa_entry(entry)) {
> > > + mtree_unlock(revmap_mt);
> > > + reset_revmap_and_unmap(mmu, may_block);
> > > + return;
> > > + }
> > > +
> > > + /*
> > > + * Ignore result, it is okay if a reverse mapping erase
> > > + * fails.
> > > + */
> > > + mas_store_gfp(&mas_rmap, NULL, GFP_NOWAIT | __GFP_ACCOUNT);
> >
> > I wonder if we can handle this a bit more gracefully.
> >
> > Ideally we'd be able to place a search mark on the entry flagging it as
> > stale which could be used to avoid attempting to overinvalidate the
> > shadow stage-2.
> >
> > Interestingly enough, Liam recently gave the suggestion [*] of storing
> > XA_ZERO_ENTRY for a similar use case where erasure happens in an atomic
> > context. We should be able to do the exact same thing here and push the
> > cleanup to the next insertion.
>
> I see. Not saying I have a better idea, but this is relying on the fact
> that replacing an existing range with another value does not require
> memory allocation. I would say this is an obvious assumption and I don't
> see it becoming false in the future, but still it's an implementation
> detail and relying on it as a user feels strange...
>
> What do you think?
I believe there's value in such an approach because it reduces the
likelihood of a false positive. Since we could be updating the maple
tree as part of reclaim I think it's safe to assume the system is under
some amount of memory pressure (and GFP_NOWAIT could fail). Although I
agree, it'd be good to document this behavior explicitly.
Thanks,
Oliver
next prev parent reply other threads:[~2026-07-16 17:16 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-14 11:59 [PATCH v4 0/6] KVM: arm64: nv: Implement nested stage-2 reverse map Wei-Lin Chang
2026-07-14 11:59 ` [PATCH v4 1/6] KVM: arm64: Use a variable for the canonical GPA in kvm_s2_fault_map() Wei-Lin Chang
2026-07-14 11:59 ` [PATCH v4 2/6] KVM: arm64: nv: Avoid full shadow s2 unmap Wei-Lin Chang
2026-07-16 7:05 ` Oliver Upton
2026-07-16 16:14 ` Wei-Lin Chang
2026-07-16 17:16 ` Oliver Upton [this message]
2026-07-14 11:59 ` [PATCH v4 3/6] KVM: arm64: nv: Add nested revmap broken tracepoint Wei-Lin Chang
2026-07-16 6:45 ` Oliver Upton
2026-07-16 8:56 ` Marc Zyngier
2026-07-16 16:25 ` Wei-Lin Chang
2026-07-14 11:59 ` [PATCH v4 4/6] KVM: arm64: Refactor kvm_unmap_gfn_range() with common variables Wei-Lin Chang
2026-07-14 11:59 ` [PATCH v4 5/6] KVM: arm64: nv: Remove reverse map entries during TLBI handling Wei-Lin Chang
2026-07-14 11:59 ` [PATCH v4 6/6] KVM: arm64: nv: Create nested IPA direct map to speed up reverse map removal Wei-Lin Chang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=alkSAyfhI4n_VAok@kernel.org \
--to=oupton@kernel.org \
--cc=catalin.marinas@arm.com \
--cc=itaru.kitayama@fujitsu.com \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maz@kernel.org \
--cc=sebastianene@google.com \
--cc=seiden@linux.ibm.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=weilin.chang@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.