From: Paolo Bonzini <pbonzini@redhat.com>
To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org
Cc: Sean Christopherson <seanjc@google.com>,
Jim Mattson <jmattson@google.com>,
James Houghton <jthoughton@google.com>,
Yosry Ahmed <yosry@kernel.org>,
David Woodhouse <dwmw2@infradead.org>,
stable@vger.kernel.org, Stefan Teodorescu <fane@google.com>
Subject: [PATCH 5/7] KVM: x86/mmu: Cap number of parent PTEs per SP at 1000 (the arbitrary rmap limit)
Date: Mon, 2 Nov 2026 18:14:26 -0500 [thread overview]
Message-ID: <20261102231428.13523-6-pbonzini@redhat.com> (raw)
In-Reply-To: <20261102231428.13523-1-pbonzini@redhat.com>
From: Sean Christopherson <seanjc@google.com>
When potentially reusing a shadow page, zap the shadow page if its number
of parent PTEs exceeds KVM's (mostly) arbitrary rmap recycling threshold.
Letting the guest create a (mostly) unbounded number of parent PTEs allows
the guest to coerce KVM into creating lists containing millions of PTEs,
effectively bounded only by the maximum number of shadow pages.
Because PTE lists are simple singly linked lists, removal is O(n). Even
worse, if a list is constructed just so, teardown can be O(n^2), e.g. if
SPTEs are built such that zapping child SPTEs always zaps the last entry
in the list. To cap things off, all paths that walk PTE lists do so
while holding mmu_lock, without conditionally rescheduling.
Using a doubly linked list would not address the issue, as removal would
remain O(n) since KVM still wouldn't know where in the list to remove from.
Removal could be made O(1) by storing a per-SPTE pointer, but that would
largely defeat the purpose of reusing SPs, and would penalize well-behaved
guests, especially for nested TDP where aliasing page tables is uncommon.
So, cap the maximum number of parent PTEs at the rmap limit. In practice,
well-behaved guests will likely never run afoul of the limit. E.g. when
running Linux as L1 with TDP, the number of parent PTEs for a single SP
tops out at under 200 (though undoubtedly obscure workloads can push it
higher). Deliberately use a "greater than or equal" as opposed to rmap's
strictly "greater than", as the rmap code checks the threshold immediately
*after* inserting into the list, i.e. the limit is 1000 for rmaps, KVM just
very briefly blows past it.
Apply the check to both indirect and direct SPs, because while a fully
direct MMU naturally limits the number of parent PTEs to '1', direct SPs in
indirect MMUs have no such constraint. E.g. if the guest maps a huge PTE
into many locations in its page tables, and KVM is backing the hugepage
with a direct SP, i.e. with a series of smaller pages, then KVM could end
up with a direct SP with many parents.
Note, shadow_mmu_try_split_huge_pages() always restarts the rmap walk or
bails entirely if it gets to the point where it tries to grab a SP, i.e.
there is *no* pre-existing bug that would be tripped by zapping a SP and
thus rmaps when getting a SP.
Cc: Jim Mattson <jmattson@google.com>
Cc: James Houghton <jthoughton@google.com>
Cc: Yosry Ahmed <yosry@kernel.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: stable@vger.kernel.org
Reported-by: Stefan Teodorescu <fane@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Message-ID: <20261027171518.1432557-1-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
arch/x86/kvm/mmu/mmu.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 5a2cc6a17a9b..f4b12d6aef55 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -2355,6 +2355,16 @@ static struct kvm_mmu_page *kvm_mmu_find_shadow_page(struct kvm *kvm,
continue;
}
+ /*
+ * Note! Unlike fully direct MMUs, direct SPs in indirect MMUs
+ * can have an arbitrary number of parents, e.g if the guest is
+ * mapping a huge PTE at many points in its page tables.
+ */
+ if (pte_list_count(&sp->parent_ptes) >= RMAP_RECYCLE_THRESHOLD) {
+ kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list);
+ break;
+ }
+
/* unsync and write-flooding only apply to indirect SPs. */
if (sp->role.direct)
goto out;
--
2.52.0
next prev parent reply other threads:[~2026-11-02 23:14 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 23:14 [PATCH 0/7] KVM: a bunch of fixes Paolo Bonzini
2026-11-02 23:14 ` [PATCH 1/7] KVM: nSVM: Check validity of vmcs01 save area copied from SMRAM on RSM Paolo Bonzini
2026-11-02 23:14 ` [PATCH 2/7] KVM: x86/mmu: Conditionally resched when retrying gPTE walk on A/D update failure Paolo Bonzini
2026-11-02 23:14 ` [PATCH 3/7] KVM: x86: Conditionally resched when restarting REP string emulation Paolo Bonzini
2026-11-02 23:14 ` [PATCH 4/7] KVM: nVMX: Process each host MSR entry at most once when unwinding failed VM-Entry Paolo Bonzini
2026-11-02 23:39 ` Jim Mattson
2026-11-03 0:10 ` Sean Christopherson
2026-11-03 0:34 ` Jim Mattson
2026-11-03 19:40 ` Jim Mattson
2026-11-03 20:59 ` Sean Christopherson
2026-11-03 22:52 ` Jim Mattson
2026-11-04 0:12 ` Sean Christopherson
2026-11-02 23:14 ` Paolo Bonzini [this message]
2026-11-02 23:14 ` [PATCH 6/7] KVM: x86: Unconditionally reset MMU context when emulating INIT Paolo Bonzini
2026-11-02 23:14 ` [PATCH 7/7] KVM: x86: Flush guest TLB when emulating a RESET to strictly honor the spec Paolo Bonzini
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=20261102231428.13523-6-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=dwmw2@infradead.org \
--cc=fane@google.com \
--cc=jmattson@google.com \
--cc=jthoughton@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=seanjc@google.com \
--cc=stable@vger.kernel.org \
--cc=yosry@kernel.org \
/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
all inboxes | Powered by JetHome®