[PATCH RFC v2 42/50] KVM: guest_memfd: Allow attaching PFN-backed providers

From: Ackerley Tng via B4 Relay

Date: Thu Oct 22 2026 - 19:42:17 EST


From: Ackerley Tng <ackerleytng@xxxxxxxxxx>

Expose GUEST_MEMFD_FLAG_PFN_MODE in supported flags to allow userspace
to explicitly request PFN mode when creating a guest_memfd instance with
GUEST_MEMFD_FLAG_USE_RESOURCE.

PFN mode is only valid when backed by an external resource provider, as
native guest_memfd relies on the page cache and manages memory as folios.
Reject creation if PFN mode is requested without an external resource.

While guest_memfd could try to infer PFN mode from whether PFN lookup
callbacks exist, an explicit creation flag ensures userspace selects the
operating mode directly, allowing providers to support both modes.

Validate the requested operating mode against the provider operations
when attaching a resource:
- In PFN mode, the provider must implement both .get_pfn and
.lookup_pfn_range.
- In folio mode, the provider must implement .alloc_folio.

Document the new flag and its requirements in the KVM API documentation.

Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
Documentation/virt/kvm/api.rst | 6 ++++++
include/linux/kvm_host.h | 3 ++-
virt/kvm/guest_memfd.c | 18 +++++++++++++++++-
3 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 9f5313d405d9c..bb550e4f11cd8 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6524,6 +6524,12 @@ specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags:
must be a supported resource (e.g. tmpfs mount
root directory). Currently, tmpfs resource pools
require noswap and huge=never mount options.
+ GUEST_MEMFD_FLAG_PFN_MODE Request PFN mode for guest_memfd backed
+ by an external resource provider. When set,
+ GUEST_MEMFD_FLAG_USE_RESOURCE must also be set,
+ and the provider must support PFN lookup
+ (.get_pfn and .lookup_pfn_range) rather than
+ folio allocation (.alloc_folio).
============================= ================================================

When the KVM MMU performs a PFN lookup to service a guest fault, the fault will
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index d820afac49252..345f97b110fa6 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2618,7 +2618,8 @@ bool kvm_arch_supports_gmem_init_shared(struct kvm *kvm);

static inline u64 kvm_gmem_get_supported_flags(struct kvm *kvm)
{
- u64 flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_USE_RESOURCE;
+ u64 flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_USE_RESOURCE |
+ GUEST_MEMFD_FLAG_PFN_MODE;

if (!kvm || kvm_arch_supports_gmem_init_shared(kvm))
flags |= GUEST_MEMFD_FLAG_INIT_SHARED;
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index c89df77df7146..a4519f3b9eadb 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -85,6 +85,18 @@ static const struct guest_memfd_provider_operations gmem_native_provider_ops = {
.alloc_folio = gmem_native_alloc_folio,
};

+static bool gmem_provider_ops_valid(struct gmem_inode *gi,
+ const struct guest_memfd_provider_operations *ops)
+{
+ if (!ops || !ops->attach)
+ return false;
+
+ if (kvm_gmem_is_pfn_mode(gi))
+ return ops->get_pfn && ops->lookup_pfn_range;
+
+ return ops->alloc_folio != NULL;
+}
+
#define GMEM_RESOURCE_FD_NATIVE (-1)

static int kvm_gmem_attach_resource(struct inode *inode, int resource_fd)
@@ -104,7 +116,7 @@ static int kvm_gmem_attach_resource(struct inode *inode, int resource_fd)
ops = resource_file->f_op->gmem_provider_ops;
}

- if (ops && ops->attach && ops->alloc_folio)
+ if (gmem_provider_ops_valid(GMEM_I(inode), ops))
provider = ops->attach(inode, resource_file);

if (resource_file)
@@ -1291,6 +1303,10 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
if (size <= 0 || !PAGE_ALIGNED(size))
return -EINVAL;

+ if (flags & GUEST_MEMFD_FLAG_PFN_MODE &&
+ !(flags & GUEST_MEMFD_FLAG_USE_RESOURCE))
+ return -EINVAL;
+
if (flags & GUEST_MEMFD_FLAG_USE_RESOURCE) {
if (args->resource_fd < 0)
return -EBADF;

--
2.56.0.257.gf7497c7d27-goog