From: Yan Zhao <yan.y.zhao@intel.com>
To: pbonzini@redhat.com, seanjc@google.com
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
x86@kernel.org, rick.p.edgecombe@intel.com,
dave.hansen@intel.com, kas@kernel.org, tabba@google.com,
ackerleytng@google.com, michael.roth@amd.com, david@kernel.org,
vannapurve@google.com, sagis@google.com, vbabka@suse.cz,
thomas.lendacky@amd.com, nik.borisov@suse.com, pgonda@google.com,
fan.du@intel.com, jun.miao@intel.com,
francescolavra.fl@gmail.com, jgross@suse.com,
ira.weiny@intel.com, isaku.yamahata@intel.com,
xiaoyao.li@intel.com, kai.huang@intel.com,
binbin.wu@linux.intel.com, chao.p.peng@intel.com,
chao.gao@intel.com, yan.y.zhao@intel.com
Subject: [PATCH v3 19/24] KVM: x86: Introduce per-VM external cache for splitting
Date: Tue, 6 Jan 2026 18:23:31 +0800 [thread overview]
Message-ID: <20260106102331.25244-1-yan.y.zhao@intel.com> (raw)
In-Reply-To: <20260106101646.24809-1-yan.y.zhao@intel.com>
Introduce per-VM external cache for splitting the external page table by
adding KVM x86 ops for cache "topup", "free", "need topup" operations.
Invoke the KVM x86 ops for "topup", "need topup" for the per-VM external
split cache when splitting the mirror root in
tdp_mmu_split_huge_pages_root() where there's no per-vCPU context.
Invoke the KVM x86 op for "free" to destroy the per-VM external split cache
when KVM frees memory caches.
This per-VM external split cache is only used when per-vCPU context is not
available. Use the per-vCPU external fault cache in the fault path
when per-vCPU context is available.
The per-VM external split cache is protected under both kvm->mmu_lock and a
cache lock inside vendor implementations to ensure that there're enough
pages in cache for one split:
- Dequeuing of the per-VM external split cache is in
kvm_x86_ops.split_external_spte() under mmu_lock.
- Yield the traversal in tdp_mmu_split_huge_pages_root() after topup of
the per-VM cache, so that need_topup() is checked again after
re-acquiring the mmu_lock.
- Vendor implementations of the per-VM external split cache provide a
cache lock to protect the enqueue/dequeue of pages into/from the cache.
Here's the sequence to show how enough pages in cache is guaranteed.
a. with write mmu_lock:
1. write_lock(&kvm->mmu_lock)
kvm_x86_ops.need_topup()
2. write_unlock(&kvm->mmu_lock)
kvm_x86_ops.topup() --> in vendor:
{
allocate pages
get cache lock
enqueue pages in cache
put cache lock
}
3. write_lock(&kvm->mmu_lock)
kvm_x86_ops.need_topup() (goto 2 if topup is necessary) (*)
kvm_x86_ops.split_external_spte() --> in vendor:
{
get cache lock
dequeue pages in cache
put cache lock
}
write_unlock(&kvm->mmu_lock)
b. with read mmu_lock,
1. read_lock(&kvm->mmu_lock)
kvm_x86_ops.need_topup()
2. read_unlock(&kvm->mmu_lock)
kvm_x86_ops.topup() --> in vendor:
{
allocate pages
get cache lock
enqueue pages in cache
put cache lock
}
3. read_lock(&kvm->mmu_lock)
kvm_x86_ops.need_topup() (goto 2 if topup is necessary)
kvm_x86_ops.split_external_spte() --> in vendor:
{
get cache lock
kvm_x86_ops.need_topup() (return retry if topup is necessary) (**)
dequeue pages in cache
put cache lock
}
read_unlock(&kvm->mmu_lock)
Due to (*) and (**) in step 3, enough pages for split is guaranteed.
Co-developed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
---
v3:
- Introduce x86 ops to manages the cache.
---
arch/x86/include/asm/kvm-x86-ops.h | 3 ++
arch/x86/include/asm/kvm_host.h | 17 +++++++
arch/x86/kvm/mmu/mmu.c | 2 +
arch/x86/kvm/mmu/tdp_mmu.c | 71 +++++++++++++++++++++++++++++-
4 files changed, 91 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 84fa8689b45c..307edc51ad8d 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -102,6 +102,9 @@ KVM_X86_OP_OPTIONAL(split_external_spte)
KVM_X86_OP_OPTIONAL(alloc_external_fault_cache)
KVM_X86_OP_OPTIONAL(topup_external_fault_cache)
KVM_X86_OP_OPTIONAL(free_external_fault_cache)
+KVM_X86_OP_OPTIONAL(topup_external_per_vm_split_cache)
+KVM_X86_OP_OPTIONAL(free_external_per_vm_split_cache)
+KVM_X86_OP_OPTIONAL(need_topup_external_per_vm_split_cache)
KVM_X86_OP(has_wbinvd_exit)
KVM_X86_OP(get_l2_tsc_offset)
KVM_X86_OP(get_l2_tsc_multiplier)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 315ffb23e9d8..6122801f334b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1862,6 +1862,23 @@ struct kvm_x86_ops {
/* Free in external page fault cache. */
void (*free_external_fault_cache)(struct kvm_vcpu *vcpu);
+ /*
+ * Top up extra pages needed in the per-VM cache for splitting external
+ * page table.
+ */
+ int (*topup_external_per_vm_split_cache)(struct kvm *kvm,
+ enum pg_level level);
+
+ /* Free the per-VM cache for splitting external page table. */
+ void (*free_external_per_vm_split_cache)(struct kvm *kvm);
+
+ /*
+ * Check if it's necessary to top up the per-VM cache for splitting
+ * external page table.
+ */
+ bool (*need_topup_external_per_vm_split_cache)(struct kvm *kvm,
+ enum pg_level level);
+
bool (*has_wbinvd_exit)(void);
u64 (*get_l2_tsc_offset)(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 35a6e37bfc68..3d568512201d 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -6924,6 +6924,8 @@ static void mmu_free_vm_memory_caches(struct kvm *kvm)
kvm_mmu_free_memory_cache(&kvm->arch.split_desc_cache);
kvm_mmu_free_memory_cache(&kvm->arch.split_page_header_cache);
kvm_mmu_free_memory_cache(&kvm->arch.split_shadow_page_cache);
+ if (kvm_has_mirrored_tdp(kvm))
+ kvm_x86_call(free_external_per_vm_split_cache)(kvm);
}
void kvm_mmu_uninit_vm(struct kvm *kvm)
diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c
index b984027343b7..b45d3da683f2 100644
--- a/arch/x86/kvm/mmu/tdp_mmu.c
+++ b/arch/x86/kvm/mmu/tdp_mmu.c
@@ -1606,6 +1606,55 @@ static bool iter_cross_boundary(struct tdp_iter *iter, gfn_t start, gfn_t end)
(iter->gfn + KVM_PAGES_PER_HPAGE(iter->level)) <= end);
}
+/*
+ * Check the per-VM external split cache under write mmu_lock or read mmu_lock
+ * in tdp_mmu_split_huge_pages_root().
+ *
+ * When need_topup_external_split_cache() returns false, the mmu_lock is held
+ * throughout the exectution from
+ * (a) need_topup_external_split_cache(), and
+ * (b) the cache dequeuing (in tdx_sept_split_private_spte() called by
+ * tdp_mmu_split_huge_page()).
+ *
+ * - When mmu_lock is held for write, the per-VM external split cache is
+ * exclusively accessed by a single user. Therefore, the result returned from
+ * need_topup_external_split_cache() is accurate.
+ *
+ * - When mmu_lock is held for read, the per-VM external split cache can be
+ * shared among multiple users. Cache dequeuing in
+ * tdx_sept_split_private_spte() thus needs to check again of the cache page
+ * count after acquiring its internal split cache lock and return an error for
+ * retry if the cache page count is not sufficient.
+ */
+static bool need_topup_external_split_cache(struct kvm *kvm, int level)
+{
+ return kvm_x86_call(need_topup_external_per_vm_split_cache)(kvm, level);
+}
+
+static int topup_external_split_cache(struct kvm *kvm, int level, bool shared)
+{
+ int r;
+
+ rcu_read_unlock();
+
+ if (shared)
+ read_unlock(&kvm->mmu_lock);
+ else
+ write_unlock(&kvm->mmu_lock);
+
+ r = kvm_x86_call(topup_external_per_vm_split_cache)(kvm, level);
+
+ if (shared)
+ read_lock(&kvm->mmu_lock);
+ else
+ write_lock(&kvm->mmu_lock);
+
+ if (!r)
+ rcu_read_lock();
+
+ return r;
+}
+
static int tdp_mmu_split_huge_pages_root(struct kvm *kvm,
struct kvm_mmu_page *root,
gfn_t start, gfn_t end,
@@ -1614,6 +1663,7 @@ static int tdp_mmu_split_huge_pages_root(struct kvm *kvm,
{
struct kvm_mmu_page *sp = NULL;
struct tdp_iter iter;
+ int r = 0;
rcu_read_lock();
@@ -1672,6 +1722,21 @@ static int tdp_mmu_split_huge_pages_root(struct kvm *kvm,
continue;
}
+ if (is_mirror_sp(root) &&
+ need_topup_external_split_cache(kvm, iter.level)) {
+ r = topup_external_split_cache(kvm, iter.level, shared);
+
+ if (r) {
+ trace_kvm_mmu_split_huge_page(iter.gfn,
+ iter.old_spte,
+ iter.level, r);
+ goto out;
+ }
+
+ iter.yielded = true;
+ continue;
+ }
+
tdp_mmu_init_child_sp(sp, &iter);
if (tdp_mmu_split_huge_page(kvm, &iter, sp, shared))
@@ -1682,15 +1747,17 @@ static int tdp_mmu_split_huge_pages_root(struct kvm *kvm,
rcu_read_unlock();
+out:
/*
* It's possible to exit the loop having never used the last sp if, for
* example, a vCPU doing HugePage NX splitting wins the race and
- * installs its own sp in place of the last sp we tried to split.
+ * installs its own sp in place of the last sp we tried to split or
+ * topup_external_split_cache() failure.
*/
if (sp)
tdp_mmu_free_sp(sp);
- return 0;
+ return r;
}
--
2.43.2
next prev parent reply other threads:[~2026-01-06 10:25 UTC|newest]
Thread overview: 127+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-06 10:16 [PATCH v3 00/24] KVM: TDX huge page support for private memory Yan Zhao
2026-01-06 10:18 ` [PATCH v3 01/24] x86/tdx: Enhance tdh_mem_page_aug() to support huge pages Yan Zhao
2026-01-06 21:08 ` Dave Hansen
2026-01-07 9:12 ` Yan Zhao
2026-01-07 16:39 ` Dave Hansen
2026-01-08 19:05 ` Ackerley Tng
2026-01-08 19:24 ` Dave Hansen
2026-01-09 16:21 ` Vishal Annapurve
2026-01-09 3:08 ` Yan Zhao
2026-01-09 18:29 ` Ackerley Tng
2026-01-12 2:41 ` Yan Zhao
2026-01-13 16:50 ` Vishal Annapurve
2026-01-14 1:48 ` Yan Zhao
2026-01-06 10:18 ` [PATCH v3 02/24] x86/virt/tdx: Add SEAMCALL wrapper tdh_mem_page_demote() Yan Zhao
2026-01-16 1:00 ` Huang, Kai
2026-01-16 8:35 ` Yan Zhao
2026-01-16 11:10 ` Huang, Kai
2026-01-16 11:22 ` Huang, Kai
2026-01-19 6:18 ` Yan Zhao
2026-01-19 6:15 ` Yan Zhao
2026-01-16 11:22 ` Huang, Kai
2026-01-19 5:55 ` Yan Zhao
2026-01-28 22:49 ` Sean Christopherson
2026-01-06 10:19 ` [PATCH v3 03/24] x86/tdx: Enhance tdh_phymem_page_wbinvd_hkid() to invalidate huge pages Yan Zhao
2026-01-06 10:19 ` [PATCH v3 04/24] x86/tdx: Introduce tdx_quirk_reset_folio() to reset private " Yan Zhao
2026-01-06 10:20 ` [PATCH v3 05/24] x86/virt/tdx: Enhance tdh_phymem_page_reclaim() to support " Yan Zhao
2026-01-06 10:20 ` [PATCH v3 06/24] KVM: x86/mmu: Disallow page merging (huge page adjustment) for mirror root Yan Zhao
2026-01-15 22:49 ` Sean Christopherson
2026-01-16 7:54 ` Yan Zhao
2026-01-26 16:08 ` Sean Christopherson
2026-01-27 3:40 ` Yan Zhao
2026-01-28 19:51 ` Sean Christopherson
2026-01-06 10:20 ` [PATCH v3 07/24] KVM: x86/tdp_mmu: Introduce split_external_spte() under write mmu_lock Yan Zhao
2026-01-28 22:38 ` Sean Christopherson
2026-01-06 10:20 ` [PATCH v3 08/24] KVM: TDX: Enable huge page splitting " Yan Zhao
2026-01-06 10:21 ` [PATCH v3 09/24] KVM: x86: Reject splitting huge pages under shared mmu_lock in TDX Yan Zhao
2026-01-06 10:21 ` [PATCH v3 10/24] KVM: x86/tdp_mmu: Alloc external_spt page for mirror page table splitting Yan Zhao
2026-01-06 10:21 ` [PATCH v3 11/24] KVM: x86/mmu: Introduce kvm_split_cross_boundary_leafs() Yan Zhao
2026-01-15 12:25 ` Huang, Kai
2026-01-16 23:39 ` Sean Christopherson
2026-01-19 1:28 ` Yan Zhao
2026-01-19 8:35 ` Huang, Kai
2026-01-19 8:49 ` Huang, Kai
2026-01-19 10:11 ` Yan Zhao
2026-01-19 10:40 ` Huang, Kai
2026-01-19 11:06 ` Yan Zhao
2026-01-19 12:32 ` Yan Zhao
2026-01-29 14:36 ` Sean Christopherson
2026-01-20 17:51 ` Sean Christopherson
2026-01-22 6:27 ` Yan Zhao
2026-01-20 17:57 ` Vishal Annapurve
2026-01-20 18:02 ` Sean Christopherson
2026-01-22 6:33 ` Yan Zhao
2026-01-29 14:51 ` Sean Christopherson
2026-01-06 10:21 ` [PATCH v3 12/24] KVM: x86: Introduce hugepage_set_guest_inhibit() Yan Zhao
2026-01-06 10:22 ` [PATCH v3 13/24] KVM: TDX: Honor the guest's accept level contained in an EPT violation Yan Zhao
2026-01-06 10:22 ` [PATCH v3 14/24] KVM: Change the return type of gfn_handler_t() from bool to int Yan Zhao
2026-01-16 0:21 ` Sean Christopherson
2026-01-16 6:42 ` Yan Zhao
2026-01-06 10:22 ` [PATCH v3 15/24] KVM: x86: Split cross-boundary mirror leafs for KVM_SET_MEMORY_ATTRIBUTES Yan Zhao
2026-01-06 10:22 ` [PATCH v3 16/24] KVM: guest_memfd: Split for punch hole and private-to-shared conversion Yan Zhao
2026-01-28 22:39 ` Sean Christopherson
2026-01-06 10:23 ` [PATCH v3 17/24] KVM: TDX: Get/Put DPAMT page pair only when mapping size is 4KB Yan Zhao
2026-01-06 10:23 ` [PATCH v3 18/24] x86/virt/tdx: Add loud warning when tdx_pamt_put() fails Yan Zhao
2026-01-06 10:23 ` Yan Zhao [this message]
2026-01-21 1:54 ` [PATCH v3 19/24] KVM: x86: Introduce per-VM external cache for splitting Huang, Kai
2026-01-21 17:30 ` Sean Christopherson
2026-01-21 19:39 ` Edgecombe, Rick P
2026-01-21 23:01 ` Huang, Kai
2026-01-22 7:03 ` Yan Zhao
2026-01-22 7:30 ` Huang, Kai
2026-01-22 7:49 ` Yan Zhao
2026-01-22 10:33 ` Huang, Kai
2026-01-06 10:23 ` [PATCH v3 20/24] KVM: TDX: Implement per-VM external cache for splitting in TDX Yan Zhao
2026-01-06 10:23 ` [PATCH v3 21/24] KVM: TDX: Add/Remove DPAMT pages for the new S-EPT page for splitting Yan Zhao
2026-01-06 10:24 ` [PATCH v3 22/24] x86/tdx: Add/Remove DPAMT pages for guest private memory to demote Yan Zhao
2026-01-19 10:52 ` Huang, Kai
2026-01-19 11:11 ` Yan Zhao
2026-01-06 10:24 ` [PATCH v3 23/24] x86/tdx: Pass guest memory's PFN info to demote for updating pamt_refcount Yan Zhao
2026-01-06 10:24 ` [PATCH v3 24/24] KVM: TDX: Turn on PG_LEVEL_2M Yan Zhao
2026-01-06 17:47 ` [PATCH v3 00/24] KVM: TDX huge page support for private memory Vishal Annapurve
2026-01-06 21:26 ` Ackerley Tng
2026-01-06 21:38 ` Sean Christopherson
2026-01-06 22:04 ` Ackerley Tng
2026-01-06 23:43 ` Sean Christopherson
2026-01-07 9:03 ` Yan Zhao
2026-01-08 20:11 ` Ackerley Tng
2026-01-09 9:18 ` Yan Zhao
2026-01-09 16:12 ` Vishal Annapurve
2026-01-09 17:16 ` Vishal Annapurve
2026-01-09 18:07 ` Ackerley Tng
2026-01-12 1:39 ` Yan Zhao
2026-01-12 2:12 ` Yan Zhao
2026-01-12 19:56 ` Ackerley Tng
2026-01-13 6:10 ` Yan Zhao
2026-01-13 16:40 ` Vishal Annapurve
2026-01-14 9:32 ` Yan Zhao
2026-01-07 19:22 ` Edgecombe, Rick P
2026-01-07 20:27 ` Sean Christopherson
2026-01-12 20:15 ` Ackerley Tng
2026-01-14 0:33 ` Yan Zhao
2026-01-14 1:24 ` Sean Christopherson
2026-01-14 9:23 ` Yan Zhao
2026-01-14 15:26 ` Sean Christopherson
2026-01-14 18:45 ` Ackerley Tng
2026-01-15 3:08 ` Yan Zhao
2026-01-15 18:13 ` Ackerley Tng
2026-01-14 18:56 ` Dave Hansen
2026-01-15 0:19 ` Sean Christopherson
2026-01-16 15:45 ` Edgecombe, Rick P
2026-01-16 16:31 ` Sean Christopherson
2026-01-16 16:58 ` Edgecombe, Rick P
2026-01-19 5:53 ` Yan Zhao
2026-01-30 15:32 ` Sean Christopherson
2026-02-03 9:18 ` Yan Zhao
2026-02-09 17:01 ` Sean Christopherson
2026-01-16 16:57 ` Dave Hansen
2026-01-16 17:14 ` Sean Christopherson
2026-01-16 17:45 ` Dave Hansen
2026-01-16 19:59 ` Sean Christopherson
2026-01-16 22:25 ` Dave Hansen
2026-01-15 1:41 ` Yan Zhao
2026-01-15 16:26 ` Sean Christopherson
2026-01-16 0:28 ` Sean Christopherson
2026-01-16 11:25 ` Yan Zhao
2026-01-16 14:46 ` Sean Christopherson
2026-01-19 1:25 ` Yan Zhao
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=20260106102331.25244-1-yan.y.zhao@intel.com \
--to=yan.y.zhao@intel.com \
--cc=ackerleytng@google.com \
--cc=binbin.wu@linux.intel.com \
--cc=chao.gao@intel.com \
--cc=chao.p.peng@intel.com \
--cc=dave.hansen@intel.com \
--cc=david@kernel.org \
--cc=fan.du@intel.com \
--cc=francescolavra.fl@gmail.com \
--cc=ira.weiny@intel.com \
--cc=isaku.yamahata@intel.com \
--cc=jgross@suse.com \
--cc=jun.miao@intel.com \
--cc=kai.huang@intel.com \
--cc=kas@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=michael.roth@amd.com \
--cc=nik.borisov@suse.com \
--cc=pbonzini@redhat.com \
--cc=pgonda@google.com \
--cc=rick.p.edgecombe@intel.com \
--cc=sagis@google.com \
--cc=seanjc@google.com \
--cc=tabba@google.com \
--cc=thomas.lendacky@amd.com \
--cc=vannapurve@google.com \
--cc=vbabka@suse.cz \
--cc=x86@kernel.org \
--cc=xiaoyao.li@intel.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
all inboxes | Powered by JetHome®