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 00:05:29 -0700 [thread overview]
Message-ID: <aliCuX3ZIvxWlFE-@kernel.org> (raw)
In-Reply-To: <20260714115926.2044757-3-weilin.chang@arm.com>
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?
> +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.
> + mmu->nested_revmap_broken = true;
> +unlock:
> + mtree_unlock(revmap_mt);
> +}
> +
> void kvm_init_nested_s2_mmu(struct kvm_s2_mmu *mmu)
> {
> /* CnP being set denotes an invalid entry */
> mmu->tlb_vttbr = VTTBR_CNP_BIT;
> mmu->nested_stage2_enabled = false;
> atomic_set(&mmu->refcnt, 0);
> + mt_init(&mmu->nested_revmap_mt);
> + mmu->nested_revmap_broken = false;
> }
>
> void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu)
> @@ -1225,6 +1305,89 @@ void kvm_nested_s2_wp(struct kvm *kvm)
> kvm_invalidate_vncr_ipa(kvm, 0, BIT(kvm->arch.mmu.pgt->ia_bits));
> }
>
> +static void reset_revmap_and_unmap(struct kvm_s2_mmu *mmu, bool may_block)
> +{
> + mtree_destroy(&mmu->nested_revmap_mt);
> + mmu->nested_revmap_broken = false;
> + kvm_stage2_unmap_range(mmu, 0, kvm_phys_size(mmu), may_block);
> +}
> +
> +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.
[*] https://lore.kernel.org/all/iv7sxqezzz42xieytbf3lskutricz7ltkr3wii3w4jbnbzto7f@2tz5hwa43l37/
Thanks,
Oliver
next prev parent reply other threads:[~2026-07-16 7:05 UTC|newest]
Thread overview: 10+ 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 [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-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=aliCuX3ZIvxWlFE-@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox