[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250807094450.4673-1-yan.y.zhao@intel.com>
Date: Thu, 7 Aug 2025 17:44:50 +0800
From: Yan Zhao <yan.y.zhao@...el.com>
To: pbonzini@...hat.com,
seanjc@...gle.com
Cc: linux-kernel@...r.kernel.org,
kvm@...r.kernel.org,
x86@...nel.org,
rick.p.edgecombe@...el.com,
dave.hansen@...el.com,
kas@...nel.org,
tabba@...gle.com,
ackerleytng@...gle.com,
quic_eberman@...cinc.com,
michael.roth@....com,
david@...hat.com,
vannapurve@...gle.com,
vbabka@...e.cz,
thomas.lendacky@....com,
pgonda@...gle.com,
zhiquan1.li@...el.com,
fan.du@...el.com,
jun.miao@...el.com,
ira.weiny@...el.com,
isaku.yamahata@...el.com,
xiaoyao.li@...el.com,
binbin.wu@...ux.intel.com,
chao.p.peng@...el.com,
yan.y.zhao@...el.com
Subject: [RFC PATCH v2 16/23] KVM: x86: Split cross-boundary mirror leafs for KVM_SET_MEMORY_ATTRIBUTES
In TDX, private page tables require precise zapping because faulting back
the zapped mappings necessitates the guest's re-acceptance. Therefore,
before performing a zap for the private-to-shared conversion, rather than
zapping a huge leaf entry that crosses the boundary of the GFN range to be
zapped, split the leaf entry to ensure GFNs outside the conversion range
are not affected.
Invoke kvm_split_cross_boundary_leafs() in
kvm_arch_pre_set_memory_attributes() to split the huge leafs that cross
GFN range boundary before calling kvm_unmap_gfn_range() to zap the GFN
range that will be converted to shared.
When kvm_split_cross_boundary_leafs() fails, it is expected to internally
invoke kvm_flush_remote_tlbs() to flush any changes that have been
successfully completed.
Unlike kvm_unmap_gfn_range(), which cannot fail,
kvm_split_cross_boundary_leafs() may fail due to memory allocation for
splitting. Update kvm_handle_gfn_range() to propagate the error back to
kvm_vm_set_mem_attributes(), which can then fail the ioctl
KVM_SET_MEMORY_ATTRIBUTES.
The downside of current implementation is that though
kvm_split_cross_boundary_leafs() is invoked before kvm_unmap_gfn_range()
for each GFN range, the entire conversion range may consist of several GFN
ranges. If an out-of-memory error occurs during the splitting of a GFN
range, some previous GFN ranges may have been successfully split and
zapped, even though their page attributes remain unchanged due to the
splitting failure.
If it's necessary, a patch can be arranged to divide a single invocation of
"kvm_handle_gfn_range(kvm, &pre_set_range)" into two, e.g.,
kvm_handle_gfn_range(kvm, &pre_set_range_prepare_and_split)
kvm_handle_gfn_range(kvm, &pre_set_range_unmap),
Signed-off-by: Yan Zhao <yan.y.zhao@...el.com>
---
RFC v2:
- update kvm_split_boundary_leafs() to kvm_split_cross_boundary_leafs() and
invoke it only for priate-to-shared conversion.
RFC v1:
- new patch.
---
arch/x86/kvm/mmu/mmu.c | 13 ++++++++++---
virt/kvm/kvm_main.c | 13 +++++++++----
2 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index c71f8bb0b903..f23d8fc59323 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7845,7 +7845,9 @@ int kvm_arch_pre_set_memory_attributes(struct kvm *kvm,
struct kvm_gfn_range *range)
{
struct kvm_memory_slot *slot = range->slot;
+ bool flush = false;
int level;
+ int ret;
/*
* Zap SPTEs even if the slot can't be mapped PRIVATE. KVM x86 only
@@ -7894,12 +7896,17 @@ int kvm_arch_pre_set_memory_attributes(struct kvm *kvm,
}
/* Unmap the old attribute page. */
- if (range->arg.attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE)
+ if (range->arg.attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE) {
range->attr_filter = KVM_FILTER_SHARED;
- else
+ } else {
range->attr_filter = KVM_FILTER_PRIVATE;
+ ret = kvm_split_cross_boundary_leafs(kvm, range, false);
+ if (ret < 0)
+ return ret;
+ flush |= ret;
+ }
- return kvm_unmap_gfn_range(kvm, range);
+ return kvm_unmap_gfn_range(kvm, range) | flush;
}
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 8f87d6c6be3f..9dceecf34822 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2464,8 +2464,8 @@ bool kvm_range_has_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
return true;
}
-static __always_inline void kvm_handle_gfn_range(struct kvm *kvm,
- struct kvm_mmu_notifier_range *range)
+static __always_inline int kvm_handle_gfn_range(struct kvm *kvm,
+ struct kvm_mmu_notifier_range *range)
{
struct kvm_gfn_range gfn_range;
struct kvm_memory_slot *slot;
@@ -2519,6 +2519,8 @@ static __always_inline void kvm_handle_gfn_range(struct kvm *kvm,
if (found_memslot)
KVM_MMU_UNLOCK(kvm);
+
+ return ret < 0 ? ret : 0;
}
static int kvm_pre_set_memory_attributes(struct kvm *kvm,
@@ -2587,7 +2589,9 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
cond_resched();
}
- kvm_handle_gfn_range(kvm, &pre_set_range);
+ r = kvm_handle_gfn_range(kvm, &pre_set_range);
+ if (r)
+ goto out_unlock;
for (i = start; i < end; i++) {
r = xa_err(xa_store(&kvm->mem_attr_array, i, entry,
@@ -2596,7 +2600,8 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end,
cond_resched();
}
- kvm_handle_gfn_range(kvm, &post_set_range);
+ r = kvm_handle_gfn_range(kvm, &post_set_range);
+ KVM_BUG_ON(r, kvm);
out_unlock:
mutex_unlock(&kvm->slots_lock);
--
2.43.2
Powered by blists - more mailing lists