* [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups
@ 2026-07-23 21:08 Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 1/9] KVM: guest_memfd: Pass the number of pages instead of the end pfn into .invalidate() Sean Christopherson
` (8 more replies)
0 siblings, 9 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
These are the cleanups from the SNP VMSA series. Well, that's how they started
out, now it's full into prep work for in-place conversion. Oh well.
Rework prepare()+invalidate() into make_private()+reclaim(), with direct line
of sight to adding make_shared() for in-place conversion. Have the x86 backend
use make_{private,shared} (no reclaim()) for SNP, as reclaim in SNP-land just
means "make shared".
The goals are to eliminate the misleading use of the term "invalidate", drop
the "prepare" terminology (which gets especially confusing with in-place
conversion), and in general to prepare for the future. E.g. so that KVM ends
up with something like:
if (!to_private)
kvm_gmem_make_shared(inode, start, end);
instead of a funky:
if (!to_private)
kvm_gmem_invalidate(inode, start, end);
and
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
if (kvm_gmem_is_private_mem(file_inode(file), index))
r = kvm_arch_gmem_make_private(kvm, gfn, *pfn,
(kvm_pfn_t)1 << *max_order);
#endif
instead of the current bare:
r = kvm_gmem_prepare_folio(kvm, slot, gfn, folio);
We'll still need changes for things like TDX hugepage support, and obviously to
add kvm_arch_gmem_make_shared() for in-place conversion, but I think/hope this
sets the stage for SNP, TDX, and pKVM to build on the arch hooks without
needing to make non-trivial changes to the core semantics.
v6:
- Provide separate make_{private,shared}() hooks instead of a single convert(),
because forcing a single API just meant multiplexing on the front-end, and
de-multiplexing on the back-end, along with weird sanity checks to enforce
different requires for private vs. shared. [Ackerley]
- Kill off per-folio preparation entirely. [Sashiko, because Sashiko kept
finding bugs in my attemps to refactor the code, so I went scorched earth].
- Pull in "Only "prepare" folios for private pages" from the in-place series
to make it easier to kill of per-folio preparation.
- Collect some reviews (dropped many since things got reworked, at lot).
v5
- https://lore.kernel.org/all/20260714231015.3337831-1-seanjc@google.com
- Drop the VMSA fixes (already applied).
- Keep a dedicated reclaim() flow. [Fuad, Ackerley]
- Pass a gfn, not a gpa, to convert() / reclaim(). [Ackerley]
- Collect reviews. [Ackerley]
- Align the gfn when folding __kvm_gmem_prepare_folio() into its caller. [Sashiko]
v4:
- https://lore.kernel.org/all/20260709204948.1988414-1-seanjc@google.com
- Collect reviews. [Mike, Ackerley]
- Rework prepare()+invalidate() into a common convert() instead of simply
renaming invalidate() to reclaim_memory(). [Ackerley]
- Fix several (mostly benign) issues in the "prepare" flow.
v3:
- https://lore.kernel.org/all/20260630222607.497895-1-seanjc@google.com
- Ensure disabling quirks while the VM is live won't result in KVM skipping
the back-half of "zap all fast". [Sashiko]
- s/gmem_free_folio/gmem_reclaim_memory. [Ackerley]
- Use {READ,WRITE}_ONCE() when accessing the guest's VMSA GPA outside of the
per-vCPU mutex, and comment. [Sashiko]
v2:
- https://lore.kernel.org/all/20260626231416.3943216-1-seanjc@google.com
- Invalidate VMSAs if the memslot is DELETED or MOVED. [Sashiko]
- Limit stable@ patches without a Fixes to 6.12+.
v1: https://lore.kernel.org/all/20260625222229.3367197-2-seanjc@google.com
Ackerley Tng (1):
KVM: guest_memfd: Only "prepare" folios for private pages
Sean Christopherson (8):
KVM: guest_memfd: Pass the number of pages instead of the end pfn into
.invalidate()
KVM: guest_memfd: Rename invalidate() arch hook to reclaim() and
isolate it
KVM: x86: Rename kvm_x86_ops' gmem_invalidate() to gmem_make_shared()
KVM: guest_memfd: Drop the redundant printk on arch gmem_prepare()
failure
KVM: guest_memfd: Add helpers to query SHARED vs. PRIVATE for a given
page
KVM: guest_memfd: Rename prepare() hook and Kconfig to make_private()
/ CONVERT
KVM: guest_memfd: Explicitly pass number of pages to make_private()
hook
KVM: guest_memfd: Make private exactly what can be mapped on page
fault
arch/x86/include/asm/kvm-x86-ops.h | 8 ++--
arch/x86/include/asm/kvm_host.h | 9 ++--
arch/x86/kvm/Kconfig | 3 +-
arch/x86/kvm/svm/sev.c | 35 +++++++--------
arch/x86/kvm/svm/svm.c | 4 +-
arch/x86/kvm/svm/svm.h | 4 +-
arch/x86/kvm/x86.c | 18 +++++---
include/linux/kvm_host.h | 10 +++--
virt/kvm/Kconfig | 6 ++-
virt/kvm/guest_memfd.c | 68 +++++++-----------------------
10 files changed, 71 insertions(+), 94 deletions(-)
base-commit: 6bc96b971766fbbbbdd9fb2642cedacaf02da957
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v6 1/9] KVM: guest_memfd: Pass the number of pages instead of the end pfn into .invalidate()
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 2/9] KVM: guest_memfd: Rename invalidate() arch hook to reclaim() and isolate it Sean Christopherson
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Pass the number of pages to "invalidate", i.e. reclaim, instead of the end
pfn, as a first step towards aligning the function prototypes between the
de facto "to private" and "to shared" arch hooks. Eventually, the goal
is to end up with kvm_gmem_arch_make_{private,shared}(), and in both cases,
providing the number of pages makes the call sites slightly nicer, and also
avoids any confusion over whether the end pfn is inclusive or exclusive.
Opportunistically rename "start" to "pfn", again to align with the expected
signature of make_private() (which needs to pass a starting gfn as well, at
which point the "start" becomes noise).
No functional change intended.
Cc: Fuad Tabba <fuad.tabba@linux.dev>
Cc: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/include/asm/kvm_host.h | 2 +-
arch/x86/kvm/svm/sev.c | 8 ++++----
arch/x86/kvm/svm/svm.h | 2 +-
arch/x86/kvm/x86.c | 4 ++--
include/linux/kvm_host.h | 2 +-
virt/kvm/guest_memfd.c | 6 +-----
6 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 58f156ae31e7..8c96999f49a0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1727,7 +1727,7 @@ struct kvm_x86_ops {
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
#endif
- void (*gmem_invalidate)(kvm_pfn_t start, kvm_pfn_t end);
+ void (*gmem_invalidate)(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
void (*gmem_invalidate_range)(struct kvm *kvm, struct kvm_gfn_range *range);
#endif
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 82b983484af2..e6987351dc04 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5161,16 +5161,16 @@ int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
return 0;
}
-void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
+void sev_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
- kvm_pfn_t pfn;
+ kvm_pfn_t end = pfn + nr_pages;
if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
return;
- pr_debug("%s: PFN start 0x%llx PFN end 0x%llx\n", __func__, start, end);
+ pr_debug("%s: PFN start 0x%llx PFN end 0x%llx\n", __func__, pfn, end);
- for (pfn = start; pfn < end;) {
+ while (pfn < end) {
bool use_2m_update = false;
int rc, rmp_level;
bool assigned;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index af25e4b56212..cf8fb3d6a100 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -1010,7 +1010,7 @@ int sev_dev_get_attr(u32 group, u64 attr, u64 *val);
extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
-void sev_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+void sev_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 1f5dc685f049..65bcad3d0264 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10627,9 +10627,9 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
-void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end)
+void kvm_arch_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
- kvm_x86_call(gmem_invalidate)(start, end);
+ kvm_x86_call(gmem_invalidate)(pfn, nr_pages);
}
void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range)
{
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 9db6eb4023c4..911c3f08b3a2 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2614,7 +2614,7 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src,
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
-void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);
+void kvm_arch_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
#endif
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 171a9da1b685..ca1d93fb2495 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -530,11 +530,7 @@ static int kvm_gmem_error_folio(struct address_space *mapping, struct folio *fol
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
static void kvm_gmem_free_folio(struct folio *folio)
{
- struct page *page = folio_page(folio, 0);
- kvm_pfn_t pfn = page_to_pfn(page);
- int order = folio_order(folio);
-
- kvm_arch_gmem_invalidate(pfn, pfn + (1ul << order));
+ kvm_arch_gmem_invalidate(folio_file_pfn(folio, 0), folio_nr_pages(folio));
}
#endif
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 2/9] KVM: guest_memfd: Rename invalidate() arch hook to reclaim() and isolate it
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 1/9] KVM: guest_memfd: Pass the number of pages instead of the end pfn into .invalidate() Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 3/9] KVM: x86: Rename kvm_x86_ops' gmem_invalidate() to gmem_make_shared() Sean Christopherson
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Rename guest_memfd's invalidate() hook to reclaim() and isolate it via its
own RECLAIM Kconfig, as the hook is called when a folio is freed, which is
far too late and lacks sufficient information for KVM to actually
invalidate its usage of the memory. E.g. SNP uses the hook to convert
memory back to SHARED so that it can be safely accessed by the host, there
is no invalidation of guest mappings anywhere. Isolating the hook will
also allow pKVM on arm64 to opt-in to reclaim() without also having to
differentiate between reclaim and conversions to shared for active VMs.
Keep guest_memfd's trampoline, even though it would be trivial to wire up
.free_folio() directly to an arch callback, to avoid bleeding guest_memfd
internals into arch code (specifically, avoid referencing folios in arch
code).
Leave the kvm_x86_ops hook as-is for the moment, as "reclaim" on SNP is
the same as convert-to-shared, i.e. using a different name for the x86 hook
will allow reusing it for in-place conversion.
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/Kconfig | 1 +
arch/x86/kvm/x86.c | 7 +++++--
include/linux/kvm_host.h | 5 ++++-
virt/kvm/Kconfig | 4 ++++
virt/kvm/guest_memfd.c | 6 +++---
5 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index 801bf9e520db..e0e7ad015839 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -161,6 +161,7 @@ config KVM_AMD_SEV
select ARCH_HAS_CC_PLATFORM
select KVM_GENERIC_MEMORY_ATTRIBUTES
select HAVE_KVM_ARCH_GMEM_PREPARE
+ select HAVE_KVM_ARCH_GMEM_RECLAIM
select HAVE_KVM_ARCH_GMEM_INVALIDATE
select HAVE_KVM_ARCH_GMEM_POPULATE
help
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 65bcad3d0264..71c1f3e9044e 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10626,11 +10626,14 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord
}
#endif
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
-void kvm_arch_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
+void kvm_arch_gmem_reclaim(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
kvm_x86_call(gmem_invalidate)(pfn, nr_pages);
}
+#endif
+
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range)
{
kvm_x86_call(gmem_invalidate_range)(kvm, range);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 911c3f08b3a2..0b5b9cb022ba 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2613,8 +2613,11 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src,
kvm_gmem_populate_cb post_populate, void *opaque);
#endif
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
+void kvm_arch_gmem_reclaim(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
+#endif
+
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
-void kvm_arch_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void kvm_arch_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
#endif
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 794976b88c6f..617876993225 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -111,6 +111,10 @@ config HAVE_KVM_ARCH_GMEM_PREPARE
bool
depends on KVM_GUEST_MEMFD
+config HAVE_KVM_ARCH_GMEM_RECLAIM
+ bool
+ depends on KVM_GUEST_MEMFD
+
config HAVE_KVM_ARCH_GMEM_INVALIDATE
bool
depends on KVM_GUEST_MEMFD
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index ca1d93fb2495..b5272645a98d 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -527,10 +527,10 @@ static int kvm_gmem_error_folio(struct address_space *mapping, struct folio *fol
return MF_DELAYED;
}
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
static void kvm_gmem_free_folio(struct folio *folio)
{
- kvm_arch_gmem_invalidate(folio_file_pfn(folio, 0), folio_nr_pages(folio));
+ kvm_arch_gmem_reclaim(folio_file_pfn(folio, 0), folio_nr_pages(folio));
}
#endif
@@ -538,7 +538,7 @@ static const struct address_space_operations kvm_gmem_aops = {
.dirty_folio = noop_dirty_folio,
.migrate_folio = kvm_gmem_migrate_folio,
.error_remove_folio = kvm_gmem_error_folio,
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
.free_folio = kvm_gmem_free_folio,
#endif
};
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 3/9] KVM: x86: Rename kvm_x86_ops' gmem_invalidate() to gmem_make_shared()
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 1/9] KVM: guest_memfd: Pass the number of pages instead of the end pfn into .invalidate() Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 2/9] KVM: guest_memfd: Rename invalidate() arch hook to reclaim() and isolate it Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 4/9] KVM: guest_memfd: Drop the redundant printk on arch gmem_prepare() failure Sean Christopherson
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Rename kvm_x86_ops's gmem_invalidate() hook to gmem_make_shared(), as the
hook doesn't invalidate anything, and so that KVM doesn't need to add yet
another vendor callback to support "convert to shared" once in-place
conversion comes along.
Opportunistically wrap the ops declarations with a GMEM_RECLAIM guard so
that attempting to wire up a .gmem_make_shared() hook without selecting
CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM will result in a build failure.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/include/asm/kvm-x86-ops.h | 4 +++-
arch/x86/include/asm/kvm_host.h | 4 +++-
arch/x86/kvm/svm/sev.c | 2 +-
arch/x86/kvm/svm/svm.c | 2 +-
arch/x86/kvm/svm/svm.h | 2 +-
arch/x86/kvm/x86.c | 2 +-
6 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 736129db272a..210cb95d0a0b 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -149,7 +149,9 @@ KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
KVM_X86_OP_OPTIONAL_RET0(gmem_prepare)
#endif
-KVM_X86_OP_OPTIONAL(gmem_invalidate)
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
+KVM_X86_OP_OPTIONAL(gmem_make_shared)
+#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
KVM_X86_OP_OPTIONAL(gmem_invalidate_range)
#endif
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 8c96999f49a0..4dc8a03c829a 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1727,7 +1727,9 @@ struct kvm_x86_ops {
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
#endif
- void (*gmem_invalidate)(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
+ void (*gmem_make_shared)(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
+#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_INVALIDATE
void (*gmem_invalidate_range)(struct kvm *kvm, struct kvm_gfn_range *range);
#endif
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index e6987351dc04..4aa8330d5b2d 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5161,7 +5161,7 @@ int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
return 0;
}
-void sev_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
+void sev_gmem_make_shared(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
kvm_pfn_t end = pfn + nr_pages;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 91286d46d13a..b8c9967dd0de 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5439,7 +5439,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.vm_move_enc_context_from = sev_vm_move_enc_context_from,
.gmem_prepare = sev_gmem_prepare,
- .gmem_invalidate = sev_gmem_invalidate,
+ .gmem_make_shared = sev_gmem_make_shared,
.gmem_invalidate_range = sev_gmem_invalidate_range,
.gmem_max_mapping_level = sev_gmem_max_mapping_level,
#endif
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index cf8fb3d6a100..b2acb5ab7c26 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -1010,7 +1010,7 @@ int sev_dev_get_attr(u32 group, u64 attr, u64 *val);
extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
-void sev_gmem_invalidate(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
+void sev_gmem_make_shared(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
struct vmcb_save_area *sev_decrypt_vmsa(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 71c1f3e9044e..249267ed2a9d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10629,7 +10629,7 @@ int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_ord
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
void kvm_arch_gmem_reclaim(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
- kvm_x86_call(gmem_invalidate)(pfn, nr_pages);
+ kvm_x86_call(gmem_make_shared)(pfn, nr_pages);
}
#endif
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 4/9] KVM: guest_memfd: Drop the redundant printk on arch gmem_prepare() failure
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
` (2 preceding siblings ...)
2026-07-23 21:08 ` [PATCH v6 3/9] KVM: x86: Rename kvm_x86_ops' gmem_invalidate() to gmem_make_shared() Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 5/9] KVM: guest_memfd: Add helpers to query SHARED vs. PRIVATE for a given page Sean Christopherson
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Drop guest_memfd's ratelimited printk to log "preparation" failures, as KVM
SNP already logs more precise messages in all error paths, and whether or
not failure to convert the pfn to private is "unexpected", i.e. warrants
logging, is firmly an architecture specific detail.
Reviewed-by: Ackerley Tng <ackerleytng@google.com>
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
virt/kvm/guest_memfd.c | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index b5272645a98d..2effff582f50 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -66,15 +66,11 @@ static int __kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slo
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
kvm_pfn_t pfn = folio_file_pfn(folio, index);
gfn_t gfn = slot->base_gfn + index - slot->gmem.pgoff;
- int rc = kvm_arch_gmem_prepare(kvm, gfn, pfn, folio_order(folio));
- if (rc) {
- pr_warn_ratelimited("gmem: Failed to prepare folio for index %lx GFN %llx PFN %llx error %d.\n",
- index, gfn, pfn, rc);
- return rc;
- }
-#endif
+ return kvm_arch_gmem_prepare(kvm, gfn, pfn, folio_order(folio));
+#else
return 0;
+#endif
}
/*
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 5/9] KVM: guest_memfd: Add helpers to query SHARED vs. PRIVATE for a given page
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
` (3 preceding siblings ...)
2026-07-23 21:08 ` [PATCH v6 4/9] KVM: guest_memfd: Drop the redundant printk on arch gmem_prepare() failure Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 6/9] KVM: guest_memfd: Only "prepare" folios for private pages Sean Christopherson
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Add helpers to check if a given page in a guest_memfd instance is PRIVATE
versus SHARED, and use the "is shared" helper instead of an open-coded
equivalent in the user pagefault handler. In addition to the immediate
usage, providing an "is private" helper will allow cleaning up the so
called prepare() code, and eventually will be heavily used once in-place
conversion support comes along.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
virt/kvm/guest_memfd.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 2effff582f50..e9d61410e5fb 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -60,6 +60,16 @@ static pgoff_t kvm_gmem_get_index(struct kvm_memory_slot *slot, gfn_t gfn)
return gfn - slot->base_gfn + slot->gmem.pgoff;
}
+static bool kvm_gmem_is_private_mem(struct inode *inode, pgoff_t index)
+{
+ return !(GMEM_I(inode)->flags & GUEST_MEMFD_FLAG_INIT_SHARED);
+}
+
+static bool kvm_gmem_is_shared_mem(struct inode *inode, pgoff_t index)
+{
+ return !kvm_gmem_is_private_mem(inode, index);
+}
+
static int __kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slot,
pgoff_t index, struct folio *folio)
{
@@ -397,7 +407,7 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf)
if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode))
return VM_FAULT_SIGBUS;
- if (!(GMEM_I(inode)->flags & GUEST_MEMFD_FLAG_INIT_SHARED))
+ if (!kvm_gmem_is_shared_mem(inode, vmf->pgoff))
return VM_FAULT_SIGBUS;
folio = kvm_gmem_get_folio(inode, vmf->pgoff);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 6/9] KVM: guest_memfd: Only "prepare" folios for private pages
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
` (4 preceding siblings ...)
2026-07-23 21:08 ` [PATCH v6 5/9] KVM: guest_memfd: Add helpers to query SHARED vs. PRIVATE for a given page Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 7/9] KVM: guest_memfd: Rename prepare() hook and Kconfig to make_private() / CONVERT Sean Christopherson
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
From: Ackerley Tng <ackerleytng@google.com>
When getting a guest_memfd pfn, prepare the folio, i.e. convert its pages
to private, if and only if the page is actually private. The misnamed
prepare() hook exists specifically to allow x86's SNP to assign pages to
the owning VM in the RMP when mapping private memory into a guest.
Guarding the call will allow renaming the prepare() hook to better reflect
its role, without creating a semantic mess, and will become a hard
requirement once in-place conversion is supported, i.e. when CoCo VMs
support SHARED guest_memfd pages.
For all intents, no functional change intended (the sole arch hook is a nop
for SHARED memory).
Suggested-by: Michael Roth <michael.roth@amd.com>
Reviewed-by: Fuad Tabba <tabba@google.com>
[sean: rewrite changelog to fit the context]
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
virt/kvm/guest_memfd.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index e9d61410e5fb..f64cd87ec8b0 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -814,7 +814,8 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
folio_mark_uptodate(folio);
}
- r = kvm_gmem_prepare_folio(kvm, slot, gfn, folio);
+ if (kvm_gmem_is_private_mem(file_inode(file), index))
+ r = kvm_gmem_prepare_folio(kvm, slot, gfn, folio);
folio_unlock(folio);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 7/9] KVM: guest_memfd: Rename prepare() hook and Kconfig to make_private() / CONVERT
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
` (5 preceding siblings ...)
2026-07-23 21:08 ` [PATCH v6 6/9] KVM: guest_memfd: Only "prepare" folios for private pages Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 8/9] KVM: guest_memfd: Explicitly pass number of pages to make_private() hook Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 9/9] KVM: guest_memfd: Make private exactly what can be mapped on page fault Sean Christopherson
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Rework guest_memfd's prepare() hook into a more accurate make_private(),
and rework its Kconfig from PREPARE to a more generic CONVERT. This will
allow x86 to share (pun intended) a kvm_x86_ops.gmem_make_shared() hook
between the "convert to shared" and "reclaim" flows, which are one and the
same for SNP.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/include/asm/kvm-x86-ops.h | 4 ++--
arch/x86/include/asm/kvm_host.h | 4 ++--
arch/x86/kvm/Kconfig | 2 +-
arch/x86/kvm/svm/sev.c | 2 +-
arch/x86/kvm/svm/svm.c | 2 +-
arch/x86/kvm/svm/svm.h | 2 +-
arch/x86/kvm/x86.c | 6 +++---
include/linux/kvm_host.h | 5 +++--
virt/kvm/Kconfig | 2 +-
virt/kvm/guest_memfd.c | 4 ++--
10 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index 210cb95d0a0b..a4d872ddef9d 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -146,8 +146,8 @@ KVM_X86_OP(vcpu_deliver_sipi_vector)
KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);
KVM_X86_OP_OPTIONAL(get_untagged_addr)
KVM_X86_OP_OPTIONAL(alloc_apic_backing_page)
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
-KVM_X86_OP_OPTIONAL_RET0(gmem_prepare)
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
+KVM_X86_OP_OPTIONAL_RET0(gmem_make_private)
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
KVM_X86_OP_OPTIONAL(gmem_make_shared)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 4dc8a03c829a..4be57157136b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1724,8 +1724,8 @@ struct kvm_x86_ops {
gva_t (*get_untagged_addr)(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
- int (*gmem_prepare)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
+ int (*gmem_make_private)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
void (*gmem_make_shared)(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index e0e7ad015839..538ed1e80332 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -160,7 +160,7 @@ config KVM_AMD_SEV
depends on CRYPTO_DEV_SP_PSP && !(KVM_AMD=y && CRYPTO_DEV_CCP_DD=m)
select ARCH_HAS_CC_PLATFORM
select KVM_GENERIC_MEMORY_ATTRIBUTES
- select HAVE_KVM_ARCH_GMEM_PREPARE
+ select HAVE_KVM_ARCH_GMEM_CONVERT
select HAVE_KVM_ARCH_GMEM_RECLAIM
select HAVE_KVM_ARCH_GMEM_INVALIDATE
select HAVE_KVM_ARCH_GMEM_POPULATE
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 4aa8330d5b2d..0ecba768c153 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5114,7 +5114,7 @@ static bool is_large_rmp_possible(struct kvm *kvm, kvm_pfn_t pfn, int order)
return false;
}
-int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
+int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
{
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
kvm_pfn_t pfn_aligned;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index b8c9967dd0de..d5a08f1db84b 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5438,7 +5438,7 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.vm_copy_enc_context_from = sev_vm_copy_enc_context_from,
.vm_move_enc_context_from = sev_vm_move_enc_context_from,
- .gmem_prepare = sev_gmem_prepare,
+ .gmem_make_private = sev_gmem_make_private,
.gmem_make_shared = sev_gmem_make_shared,
.gmem_invalidate_range = sev_gmem_invalidate_range,
.gmem_max_mapping_level = sev_gmem_max_mapping_level,
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index b2acb5ab7c26..ad849edcacc7 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -1009,7 +1009,7 @@ int sev_cpu_init(struct svm_cpu_data *sd);
int sev_dev_get_attr(u32 group, u64 attr, u64 *val);
extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
-int sev_gmem_prepare(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
+int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
void sev_gmem_make_shared(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 249267ed2a9d..e7865b28d37b 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10619,10 +10619,10 @@ bool kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
return !kvm_arch_has_private_mem(kvm);
}
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
-int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_order)
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
+int kvm_arch_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_order)
{
- return kvm_x86_call(gmem_prepare)(kvm, pfn, gfn, max_order);
+ return kvm_x86_call(gmem_make_private)(kvm, pfn, gfn, max_order);
}
#endif
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 0b5b9cb022ba..9cf01429281b 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2579,8 +2579,9 @@ static inline int kvm_gmem_get_pfn(struct kvm *kvm,
}
#endif /* CONFIG_KVM_GUEST_MEMFD */
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
-int kvm_arch_gmem_prepare(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_order);
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
+int kvm_arch_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
+ int max_order);
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_POPULATE
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
index 617876993225..c3c0ee253fc7 100644
--- a/virt/kvm/Kconfig
+++ b/virt/kvm/Kconfig
@@ -107,7 +107,7 @@ config KVM_GUEST_MEMFD
select XARRAY_MULTI
bool
-config HAVE_KVM_ARCH_GMEM_PREPARE
+config HAVE_KVM_ARCH_GMEM_CONVERT
bool
depends on KVM_GUEST_MEMFD
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index f64cd87ec8b0..466f611ad4b7 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -73,11 +73,11 @@ static bool kvm_gmem_is_shared_mem(struct inode *inode, pgoff_t index)
static int __kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slot,
pgoff_t index, struct folio *folio)
{
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_PREPARE
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
kvm_pfn_t pfn = folio_file_pfn(folio, index);
gfn_t gfn = slot->base_gfn + index - slot->gmem.pgoff;
- return kvm_arch_gmem_prepare(kvm, gfn, pfn, folio_order(folio));
+ return kvm_arch_gmem_make_private(kvm, gfn, pfn, folio_order(folio));
#else
return 0;
#endif
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 8/9] KVM: guest_memfd: Explicitly pass number of pages to make_private() hook
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
` (6 preceding siblings ...)
2026-07-23 21:08 ` [PATCH v6 7/9] KVM: guest_memfd: Rename prepare() hook and Kconfig to make_private() / CONVERT Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 9/9] KVM: guest_memfd: Make private exactly what can be mapped on page fault Sean Christopherson
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
Tweak the guest_memfd make_private() hook to explicitly pass the number of
pages to align with the signature of the make_shared() hook, and because
the existing code is outright broken if a guest_memfd folio is comprised of
more than one page (which can't happen, yet). The SNP code *tries* to
create a corresponding huge entry, but if the RMP must use 4KiB entries for
whatever reason, KVM will only convert the first pfn, and not the entire
range of pfns that will be mapped into the guest.
Alternatively, @max_order could simply be repurposed as _the_ @order, but
that will fall apart when in-place conversion comes along, at which point
KVM will need to deal with conversions that aren't bound 1:1 to a folio.
I.e. the number of pages to convert may not be exactly be a power-of-2 (and
folios don't strictly guarantee power-of-2 pages anyways).
WARN in the SNP code if the number of pages to prepare is anything other
than '1', i.e. if guest_memfd is trying to prepare/convert more than a
single 4KiB page, as sev_gmem_prepare() doesn't actually handle conversion
greater than order-0 folios.
Opportunistically swap the ordering of @pfn and @gfn params for
kvm_x86_ops.gmem_make_private() to match kvm_arch_gmem_make_private().
Fixes: b85524314a3d ("KVM: guest_memfd: delay kvm_gmem_prepare_folio() until the memory is passed to the guest")
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/include/asm/kvm_host.h | 3 ++-
arch/x86/kvm/svm/sev.c | 27 +++++++++++----------------
arch/x86/kvm/svm/svm.h | 2 +-
arch/x86/kvm/x86.c | 5 +++--
include/linux/kvm_host.h | 2 +-
virt/kvm/guest_memfd.c | 2 +-
6 files changed, 19 insertions(+), 22 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 4be57157136b..230267b2203b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1725,7 +1725,8 @@ struct kvm_x86_ops {
gva_t (*get_untagged_addr)(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
void *(*alloc_apic_backing_page)(struct kvm_vcpu *vcpu);
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
- int (*gmem_make_private)(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
+ int (*gmem_make_private)(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
+ kvm_pfn_t nr_pages);
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
void (*gmem_make_shared)(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 0ecba768c153..bf7f94d1d7f9 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5090,15 +5090,7 @@ static bool is_pfn_range_shared(kvm_pfn_t start, kvm_pfn_t end)
return true;
}
-static u8 max_level_for_order(int order)
-{
- if (order >= KVM_HPAGE_GFN_SHIFT(PG_LEVEL_2M))
- return PG_LEVEL_2M;
-
- return PG_LEVEL_4K;
-}
-
-static bool is_large_rmp_possible(struct kvm *kvm, kvm_pfn_t pfn, int order)
+static bool is_large_rmp_possible(kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
kvm_pfn_t pfn_aligned = ALIGN_DOWN(pfn, PTRS_PER_PMD);
@@ -5107,14 +5099,14 @@ static bool is_large_rmp_possible(struct kvm *kvm, kvm_pfn_t pfn, int order)
* PFN is currently shared, then the entire 2M-aligned range can be
* set to private via a single 2M RMP entry.
*/
- if (max_level_for_order(order) > PG_LEVEL_4K &&
+ if (nr_pages >= KVM_PAGES_PER_HPAGE(PG_LEVEL_2M) &&
is_pfn_range_shared(pfn_aligned, pfn_aligned + PTRS_PER_PMD))
return true;
return false;
}
-int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order)
+int sev_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, kvm_pfn_t nr_pages)
{
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
kvm_pfn_t pfn_aligned;
@@ -5125,6 +5117,9 @@ int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_ord
if (!sev_snp_guest(kvm))
return 0;
+ if (WARN_ON_ONCE(nr_pages != 1))
+ return -EIO;
+
rc = snp_lookup_rmpentry(pfn, &assigned, &level);
if (rc) {
pr_err_ratelimited("SEV: Failed to look up RMP entry: GFN %llx PFN %llx error %d\n",
@@ -5133,12 +5128,12 @@ int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_ord
}
if (assigned) {
- pr_debug("%s: already assigned: gfn %llx pfn %llx max_order %d level %d\n",
- __func__, gfn, pfn, max_order, level);
+ pr_debug("%s: already assigned: gfn %llx pfn %llx nr_pages %llx level %d\n",
+ __func__, gfn, pfn, nr_pages, level);
return 0;
}
- if (is_large_rmp_possible(kvm, pfn, max_order)) {
+ if (is_large_rmp_possible(pfn, nr_pages)) {
level = PG_LEVEL_2M;
pfn_aligned = ALIGN_DOWN(pfn, PTRS_PER_PMD);
gfn_aligned = ALIGN_DOWN(gfn, PTRS_PER_PMD);
@@ -5155,8 +5150,8 @@ int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_ord
return -EINVAL;
}
- pr_debug("%s: updated: gfn %llx pfn %llx pfn_aligned %llx max_order %d level %d\n",
- __func__, gfn, pfn, pfn_aligned, max_order, level);
+ pr_debug("%s: updated: gfn %llx pfn %llx pfn_aligned %llx nr_pages %llx level %d\n",
+ __func__, gfn, pfn, pfn_aligned, nr_pages, level);
return 0;
}
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index ad849edcacc7..a327bf751ecd 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -1009,7 +1009,7 @@ int sev_cpu_init(struct svm_cpu_data *sd);
int sev_dev_get_attr(u32 group, u64 attr, u64 *val);
extern unsigned int max_sev_asid;
void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code);
-int sev_gmem_make_private(struct kvm *kvm, kvm_pfn_t pfn, gfn_t gfn, int max_order);
+int sev_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void sev_gmem_make_shared(kvm_pfn_t pfn, kvm_pfn_t nr_pages);
void sev_gmem_invalidate_range(struct kvm *kvm, struct kvm_gfn_range *range);
int sev_gmem_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, bool is_private);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index e7865b28d37b..511830d1b6f3 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10620,9 +10620,10 @@ bool kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
}
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
-int kvm_arch_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, int max_order)
+int kvm_arch_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
+ kvm_pfn_t nr_pages)
{
- return kvm_x86_call(gmem_make_private)(kvm, pfn, gfn, max_order);
+ return kvm_x86_call(gmem_make_private)(kvm, gfn, pfn, nr_pages);
}
#endif
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 9cf01429281b..97cedf8b89c5 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2581,7 +2581,7 @@ static inline int kvm_gmem_get_pfn(struct kvm *kvm,
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
int kvm_arch_gmem_make_private(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
- int max_order);
+ kvm_pfn_t nr_pages);
#endif
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_POPULATE
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 466f611ad4b7..255860c472ee 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -77,7 +77,7 @@ static int __kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slo
kvm_pfn_t pfn = folio_file_pfn(folio, index);
gfn_t gfn = slot->base_gfn + index - slot->gmem.pgoff;
- return kvm_arch_gmem_make_private(kvm, gfn, pfn, folio_order(folio));
+ return kvm_arch_gmem_make_private(kvm, gfn, pfn, folio_nr_pages(folio));
#else
return 0;
#endif
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v6 9/9] KVM: guest_memfd: Make private exactly what can be mapped on page fault
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
` (7 preceding siblings ...)
2026-07-23 21:08 ` [PATCH v6 8/9] KVM: guest_memfd: Explicitly pass number of pages to make_private() hook Sean Christopherson
@ 2026-07-23 21:08 ` Sean Christopherson
8 siblings, 0 replies; 10+ messages in thread
From: Sean Christopherson @ 2026-07-23 21:08 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini
Cc: kvm, linux-kernel, Fuad Tabba, Ackerley Tng, Xiaoyao Li,
Michael Roth, Fuad Tabba
When calling into arch code to make the underlying memory private, i.e. to
assign memory to the VM in SNP's RMP table, assign/convert *exactly* the
range of memory that can be mapped into the guest for the current page
fault, instead of aggressively converting/assigning the entire folio. For
SNP, the mapping size in the stage-2 page tables (Nested Page Tables, NPT)
must be at least the size of the corresponding RMP entry, e.g. assigning a
2MiB mapping in the RMP when it can only be mapped at 4KiB granualarity
will ultimate result in another page fault (#NPF for SNP) to "smash" the
RMP down to the correct mapping size.
Assigning the entire folio was necessary back when guest_memfd tracked
preparedness, which was done on a per-folio basis. At the time, it made
sense to do per-folio tracking/preparation, because tracking per-folio
meant guest_memfd didn't need to add a separate data structure to track
that information, and doing per-folio tracking only works if the entire
folio is prepared (or not).
Now that guest_memfd no longer does preparation tracking (see commit
8622ef05709f ("KVM: guest_memfd: Remove preparation tracking")), in favor
having SNP query the RMP, per-folio preparation, i.e. per-folio conversions
to private, doesn't make any sense.
*If* SNP allowed the RMP size to be greater than the NPT size, then
per-folio conversion could theoretically provide marginal value, as it
would allow KVM to assign a hugepage in the RMP even if it can only be
mapped into the NPT with a smaller page, e.g. because of memslot alignment.
The documentation of that reasoning would be something like this:
/*
* If the memory is private from KVM's perspective, and hardware tracks
* VM-assigned private memory in a dedicated data structure, i.e. not
* in the stage-2 page tables, then call into arch code to assign the
* entire folio to the guest. Assigning the entire folio, e.g. instead
* of only the memory being mapped into the guest, allows KVM to assign
* an entire hugepage of memory in the out-of-band structure even if
* KVM can only map a smaller page size into the MMU, e.g. because the
* gmem hugepage is spread across multiple memslots.
*/
But even *if* a future SNP implementation supported that behavior, the
value added would be dubious, as having a huge folio that is fully private,
but can only be mapped at a smaller granularity, would be rare. E.g. maybe
for memory at the top of lower DRAM that has holes for non-RAM assets?
So, convert/assign exactly what guest_memfd allows the caller to map to
simplify the guest_memfd code and provide a (super) minor performance
optimization for SNP. E.g. once hugepage support comes along, guest_memfd
will only need a single flow to compute "how much memory can be assigned
and at what size".
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
virt/kvm/guest_memfd.c | 53 ++++++------------------------------------
1 file changed, 7 insertions(+), 46 deletions(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 255860c472ee..0bca42a0b346 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -70,50 +70,6 @@ static bool kvm_gmem_is_shared_mem(struct inode *inode, pgoff_t index)
return !kvm_gmem_is_private_mem(inode, index);
}
-static int __kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slot,
- pgoff_t index, struct folio *folio)
-{
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
- kvm_pfn_t pfn = folio_file_pfn(folio, index);
- gfn_t gfn = slot->base_gfn + index - slot->gmem.pgoff;
-
- return kvm_arch_gmem_make_private(kvm, gfn, pfn, folio_nr_pages(folio));
-#else
- return 0;
-#endif
-}
-
-/*
- * Process @folio, which contains @gfn, so that the guest can use it.
- * The folio must be locked and the gfn must be contained in @slot.
- * On successful return the guest sees a zero page so as to avoid
- * leaking host data and the up-to-date flag is set.
- */
-static int kvm_gmem_prepare_folio(struct kvm *kvm, struct kvm_memory_slot *slot,
- gfn_t gfn, struct folio *folio)
-{
- pgoff_t index;
-
- /*
- * Preparing huge folios should always be safe, since it should
- * be possible to split them later if needed.
- *
- * Right now the folio order is always going to be zero, but the
- * code is ready for huge folios. The only assumption is that
- * the base pgoff of memslots is naturally aligned with the
- * requested page order, ensuring that huge folios can also use
- * huge page table entries for GPA->HPA mapping.
- *
- * The order will be passed when creating the guest_memfd, and
- * checked when creating memslots.
- */
- WARN_ON(!IS_ALIGNED(slot->gmem.pgoff, folio_nr_pages(folio)));
- index = kvm_gmem_get_index(slot, gfn);
- index = ALIGN_DOWN(index, folio_nr_pages(folio));
-
- return __kvm_gmem_prepare_folio(kvm, slot, index, folio);
-}
-
/*
* Returns a locked folio on success. The caller is responsible for
* setting the up-to-date flag before the memory is mapped into the guest.
@@ -799,7 +755,9 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
{
pgoff_t index = kvm_gmem_get_index(slot, gfn);
struct folio *folio;
- int r = 0;
+ int r = 0, __order;
+
+ max_order = max_order ?: &__order;
CLASS(gmem_get_file, file)(slot);
if (!file)
@@ -814,8 +772,11 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
folio_mark_uptodate(folio);
}
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
if (kvm_gmem_is_private_mem(file_inode(file), index))
- r = kvm_gmem_prepare_folio(kvm, slot, gfn, folio);
+ r = kvm_arch_gmem_make_private(kvm, gfn, *pfn,
+ (kvm_pfn_t)1 << *max_order);
+#endif
folio_unlock(folio);
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-23 21:08 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 21:08 [PATCH v6 0/9] KVM: guest_memfd: RECLAIM+CONVERT cleanups Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 1/9] KVM: guest_memfd: Pass the number of pages instead of the end pfn into .invalidate() Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 2/9] KVM: guest_memfd: Rename invalidate() arch hook to reclaim() and isolate it Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 3/9] KVM: x86: Rename kvm_x86_ops' gmem_invalidate() to gmem_make_shared() Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 4/9] KVM: guest_memfd: Drop the redundant printk on arch gmem_prepare() failure Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 5/9] KVM: guest_memfd: Add helpers to query SHARED vs. PRIVATE for a given page Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 6/9] KVM: guest_memfd: Only "prepare" folios for private pages Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 7/9] KVM: guest_memfd: Rename prepare() hook and Kconfig to make_private() / CONVERT Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 8/9] KVM: guest_memfd: Explicitly pass number of pages to make_private() hook Sean Christopherson
2026-07-23 21:08 ` [PATCH v6 9/9] KVM: guest_memfd: Make private exactly what can be mapped on page fault Sean Christopherson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox