* [PATCH RFC 01/17] mm: shmem: Implement guest_memfd provider operations for tmpfs
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 02/17] KVM: guest_memfd: Support provider folio allocation Ackerley Tng via B4 Relay
` (15 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Implement guest_memfd provider operations to declare that tmpfs supports
providing memory to guest_memfd.
I'm implementing it directly in tmpfs as an illustration, one alternative I
can think of is that guest_memfd (KVM the module) could provide a registry,
supporting filesystems in the kernel, and loaded filesystems could register
themselves as providers.
Would it introduce ordering issues? Like if KVM were loaded after the
provider? Should the registry be built-in to the kernel?
Perhaps another way to key the provider functions could be TMPFS_MAGIC?
I put guest_memfd_provider_operations as a pointer in
super_operations. There might be a better place, to support different kinds
of providers. What do other providers need? Perhaps it's also okay to have
guest_memfd look up in a few different places, beginning with the resource
fd it was provided.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
include/linux/fs/super_types.h | 4 ++
include/linux/guest_memfd.h | 29 +++++++++++
mm/shmem.c | 112 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+)
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ecd96aeb1cee7..29e502439648a 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -37,6 +37,7 @@ struct workqueue_struct;
struct writeback_control;
struct xattr_handler;
struct fserror_event;
+struct guest_memfd_provider_operations;
extern struct super_block *blockdev_superblock;
@@ -130,6 +131,9 @@ struct super_operations {
/* Report a filesystem error */
void (*report_error)(const struct fserror_event *event);
+#ifdef CONFIG_KVM_GUEST_MEMFD
+ const struct guest_memfd_provider_operations *gmem_provider_ops;
+#endif
};
struct super_block {
diff --git a/include/linux/guest_memfd.h b/include/linux/guest_memfd.h
new file mode 100644
index 0000000000000..60eb4f008c246
--- /dev/null
+++ b/include/linux/guest_memfd.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _LINUX_GUEST_MEMFD_H
+#define _LINUX_GUEST_MEMFD_H
+
+#include <linux/types.h>
+
+struct file;
+struct folio;
+struct mempolicy;
+
+/**
+ * struct guest_memfd_provider_operations - Operations for external memory providers
+ * @attach: Attach to a resource file, perform filesystem-specific validation,
+ * and return an opaque provider context.
+ * @release: Release provider context and unpin any resources.
+ * @alloc_folio: Allocate an uninserted folio for a given index and NUMA policy.
+ * @invalidate_folio: Notify provider that a folio has been invalidated.
+ *
+ * Used by filesystems and device drivers that provide memory for guest_memfd.
+ */
+struct guest_memfd_provider_operations {
+ void *(*attach)(struct file *resource_file);
+ void (*release)(void *provider);
+ struct folio *(*alloc_folio)(void *provider, pgoff_t index,
+ struct mempolicy *mpol);
+ void (*invalidate_folio)(void *provider, struct folio *folio);
+};
+
+#endif /* _LINUX_GUEST_MEMFD_H */
diff --git a/mm/shmem.c b/mm/shmem.c
index 897fa2b61346f..279f29a9861c1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -38,6 +38,7 @@
#include <linux/uio.h>
#include <linux/hugetlb.h>
#include <linux/fs_parser.h>
+#include <linux/guest_memfd.h>
#include <linux/swapfile.h>
#include <linux/iversion.h>
#include <linux/unicode.h>
@@ -5234,6 +5235,114 @@ static const struct inode_operations shmem_special_inode_operations = {
#endif
};
+#ifdef CONFIG_KVM_GUEST_MEMFD
+static void *shmem_gmem_attach(struct file *resource_file)
+{
+ struct inode *inode = file_inode(resource_file);
+ struct shmem_sb_info *sbinfo;
+
+ if (!S_ISDIR(inode->i_mode))
+ return ERR_PTR(-ENOTDIR);
+
+ /*
+ * Would like comments: Requiring the root directory of the mount
+ * provides a less confusing interface, although this is not strictly
+ * necessary.
+ */
+ if (resource_file->f_path.dentry != resource_file->f_path.mnt->mnt_root)
+ return ERR_PTR(-EINVAL);
+
+ if (__mnt_is_readonly(resource_file->f_path.mnt))
+ return ERR_PTR(-EROFS);
+
+ if (inode_permission(file_mnt_idmap(resource_file), inode,
+ MAY_WRITE | MAY_EXEC))
+ return ERR_PTR(-EACCES);
+
+ /* Enforce noswap because guest_memfd does not support swapping. */
+ sbinfo = SHMEM_SB(inode->i_sb);
+ if (!sbinfo->noswap)
+ return ERR_PTR(-EINVAL);
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ /*
+ * guest_memfd does not support huge pages yet; this restriction can be
+ * relaxed in the future.
+ *
+ * TODO: Block rebinding with huge page support.
+ */
+ if (sbinfo->huge != SHMEM_HUGE_NEVER)
+ return ERR_PTR(-EINVAL);
+#endif
+
+ return (void *)mntget(resource_file->f_path.mnt);
+}
+
+static void shmem_gmem_release(void *provider)
+{
+ struct vfsmount *mnt = provider;
+
+ mntput(mnt);
+}
+
+/*
+ * TODO: Consider refactoring shmem allocation helpers to share code between
+ * internal tmpfs folio allocation and guest_memfd provider allocation.
+ */
+static struct folio *shmem_gmem_alloc_folio(void *provider, pgoff_t index,
+ struct mempolicy *mpol)
+{
+ struct mempolicy *sb_mpol = NULL;
+ struct vfsmount *mnt = provider;
+ struct shmem_sb_info *sbinfo;
+ struct folio *folio;
+
+ sbinfo = SHMEM_SB(mnt->mnt_sb);
+ if (sbinfo->max_blocks &&
+ !percpu_counter_limited_add(&sbinfo->used_blocks,
+ sbinfo->max_blocks, 1))
+ return ERR_PTR(-ENOSPC);
+
+ if (!mpol) {
+ sb_mpol = shmem_get_sbmpol(sbinfo);
+ mpol = sb_mpol;
+ }
+
+ if (mpol)
+ folio = folio_alloc_mpol(GFP_HIGHUSER_MOVABLE, 0, mpol, index,
+ numa_node_id());
+ else
+ folio = folio_alloc(GFP_HIGHUSER_MOVABLE, 0);
+
+ mpol_cond_put(sb_mpol);
+
+ if (!folio) {
+ if (sbinfo->max_blocks)
+ percpu_counter_sub(&sbinfo->used_blocks, 1);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return folio;
+}
+
+static void shmem_gmem_invalidate_folio(void *provider, struct folio *folio)
+{
+ struct vfsmount *mnt = provider;
+ struct shmem_sb_info *sbinfo;
+
+ sbinfo = SHMEM_SB(mnt->mnt_sb);
+ if (sbinfo->max_blocks)
+ percpu_counter_sub(&sbinfo->used_blocks, folio_nr_pages(folio));
+}
+
+static const struct guest_memfd_provider_operations shmem_gmem_provider_ops = {
+ .attach = shmem_gmem_attach,
+ .release = shmem_gmem_release,
+ .alloc_folio = shmem_gmem_alloc_folio,
+ .invalidate_folio = shmem_gmem_invalidate_folio,
+};
+#endif /* CONFIG_KVM_GUEST_MEMFD */
+
static const struct super_operations shmem_ops = {
.alloc_inode = shmem_alloc_inode,
.free_inode = shmem_free_in_core_inode,
@@ -5252,6 +5361,9 @@ static const struct super_operations shmem_ops = {
.nr_cached_objects = shmem_unused_huge_count,
.free_cached_objects = shmem_unused_huge_scan,
#endif
+#ifdef CONFIG_KVM_GUEST_MEMFD
+ .gmem_provider_ops = &shmem_gmem_provider_ops,
+#endif
};
static const struct vm_operations_struct shmem_vm_ops = {
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 02/17] KVM: guest_memfd: Support provider folio allocation
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 01/17] mm: shmem: Implement guest_memfd provider operations for tmpfs Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 03/17] KVM: guest_memfd: Support provider folio invalidation Ackerley Tng via B4 Relay
` (14 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
If this guest_memfd was created with a provider, use the provider to
allocate a folio. For folio-based providers, using guest_memfd's filemap is
better because folio->mapping would be guest_memfd's mapping, and so
guest_memfd can handle anything that makes decisions based off
folio->mapping, like memory_failure().
Going along these lines of using gmem's filemap, passing gmem's filemap for
the provider to insert into seems awkward. At least for tmpfs, the
insertion function does a lot of stuff and assumes stuff about the filemap
being a tmpfs one (completely fair). I think it's better to use gmem's
filemap and do charging (memcg) and inode accounting according to
guest_memfd's rules though, hence insertion is done within gmem, in a gmem
filemap.
Going back to folio->mapping pointing to the gmem filemap, we could have
special memory failure handling for guest_memfd folios too, if there's some
kind of central registry of all PFNs belonging to guest_memfd? There are
other usages of folio->mapping, but gmem doesn't participate in those since
gmem doesn't do swap, etc now.
For non-folio-based providers, here's my suggestion: Don't provide
.alloc_folio(), provide some equivalent callback for pfns, track pfns in
gmem. The provider can force gmem to return the folios anytime, the
.attach() can be bidirectional.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
virt/kvm/guest_memfd.c | 44 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 40 insertions(+), 4 deletions(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index a13445c26d9d6..5ac2d558c8dd8 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -3,6 +3,7 @@
#include <linux/backing-dev.h>
#include <linux/falloc.h>
#include <linux/fs.h>
+#include <linux/guest_memfd.h>
#include <linux/kvm_host.h>
#include <linux/maple_tree.h>
#include <linux/mempolicy.h>
@@ -35,6 +36,9 @@ struct gmem_inode {
struct inode vfs_inode;
struct list_head gmem_file_list;
+ void *provider;
+ const struct guest_memfd_provider_operations *provider_ops;
+
u64 flags;
/*
* Every index in this inode, whether memory is populated or
@@ -50,6 +54,16 @@ static __always_inline struct gmem_inode *GMEM_I(struct inode *inode)
return container_of(inode, struct gmem_inode, vfs_inode);
}
+static inline struct folio *gmem_provider_alloc_folio(struct gmem_inode *gi,
+ pgoff_t index,
+ struct mempolicy *mpol)
+{
+ if (!gi->provider_ops || !gi->provider_ops->alloc_folio)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ return gi->provider_ops->alloc_folio(gi->provider, index, mpol);
+}
+
#define kvm_gmem_for_each_file(f, inode) \
list_for_each_entry(f, &GMEM_I(inode)->gmem_file_list, entry)
@@ -128,6 +142,7 @@ static bool kvm_gmem_range_has_attributes(struct inode *inode,
*/
static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
{
+ struct gmem_inode *gi = GMEM_I(inode);
/* TODO: Support huge pages. */
struct mempolicy *policy;
struct folio *folio;
@@ -140,10 +155,29 @@ static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
if (!IS_ERR(folio))
return folio;
- policy = mpol_shared_policy_lookup(&GMEM_I(inode)->policy, index);
- folio = __filemap_get_folio_mpol(inode->i_mapping, index,
- FGP_LOCK | FGP_CREAT,
- mapping_gfp_mask(inode->i_mapping), policy);
+ policy = mpol_shared_policy_lookup(&gi->policy, index);
+ /*
+ * TODO: Refactor the internal PAGE_SIZE gmem could be an internal
+ * provider with internal provider_ops.
+ */
+ if (gi->provider_ops) {
+ folio = gmem_provider_alloc_folio(gi, index, policy);
+ if (!IS_ERR(folio)) {
+ int r = filemap_add_folio(inode->i_mapping, folio,
+ index, GFP_KERNEL);
+ if (r) {
+ folio_put(folio);
+ folio = ERR_PTR(r);
+ } else {
+ folio_mark_accessed(folio);
+ }
+ }
+ } else {
+ folio = __filemap_get_folio_mpol(inode->i_mapping, index,
+ FGP_LOCK | FGP_CREAT,
+ mapping_gfp_mask(inode->i_mapping),
+ policy);
+ }
mpol_cond_put(policy);
/*
@@ -1334,6 +1368,8 @@ static struct inode *kvm_gmem_alloc_inode(struct super_block *sb)
mt_init_flags(&gi->attributes, MT_FLAGS_LOCK_EXTERN | MT_FLAGS_USE_RCU);
gi->flags = 0;
+ gi->provider = NULL;
+ gi->provider_ops = NULL;
INIT_LIST_HEAD(&gi->gmem_file_list);
return &gi->vfs_inode;
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 03/17] KVM: guest_memfd: Support provider folio invalidation
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 01/17] mm: shmem: Implement guest_memfd provider operations for tmpfs Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 02/17] KVM: guest_memfd: Support provider folio allocation Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 04/17] KVM: guest_memfd: Add helper to attach resource provider file Ackerley Tng via B4 Relay
` (13 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
tmpfs increments the total number of used pages on the mount at allocation
time, so that number has to be decremented when the folio is removed from
tmpfs' ownership.
In this model where guest_memfd allocates from a tmpfs mount, I believe we
should respect the limits set in the mount, hence I require .alloc_folio()
to increment.
.invalidate_folio() is then the counterpart to .alloc_folio(), for undoing
any provider-specific per-folio work during allocations.
.invalidate_folio() requires mapping_set_release_always(). Not sure how odd
it is to use that mapping flag.
Using .free_folio() is hard because folio->mapping is NULL by then, so I
can't reach provider information. The provider information could also be
stuffed in folio->private? What do people think of that?
Another alternative would be to use a custom truncation function in
guest_memfd, just like shmem_truncate_range() or shmem_undo_range(). The
con is that guest_memfd might at some point support more generic mm stuff
like swap/reclaim, of some form? Or maybe never? Shall we go custom now and
unify later? Or just not think too far ahead?
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
virt/kvm/guest_memfd.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 5ac2d558c8dd8..00f3bd0cf21e6 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -64,6 +64,13 @@ static inline struct folio *gmem_provider_alloc_folio(struct gmem_inode *gi,
return gi->provider_ops->alloc_folio(gi->provider, index, mpol);
}
+static inline void gmem_provider_invalidate_folio(struct gmem_inode *gi,
+ struct folio *folio)
+{
+ if (gi->provider_ops && gi->provider_ops->invalidate_folio)
+ gi->provider_ops->invalidate_folio(gi->provider, folio);
+}
+
#define kvm_gmem_for_each_file(f, inode) \
list_for_each_entry(f, &GMEM_I(inode)->gmem_file_list, entry)
@@ -166,6 +173,7 @@ static struct folio *kvm_gmem_get_folio(struct inode *inode, pgoff_t index)
int r = filemap_add_folio(inode->i_mapping, folio,
index, GFP_KERNEL);
if (r) {
+ gmem_provider_invalidate_folio(gi, folio);
folio_put(folio);
folio = ERR_PTR(r);
} else {
@@ -893,10 +901,24 @@ static void kvm_gmem_free_folio(struct folio *folio)
}
#endif
+static void kvm_gmem_invalidate_folio(struct folio *folio, size_t offset,
+ size_t len)
+{
+ struct inode *inode = folio->mapping->host;
+ struct gmem_inode *gi = GMEM_I(inode);
+
+ /* guest_memfd only allows truncating full folios. */
+ if (WARN_ON_ONCE(offset != 0 || len != folio_size(folio)))
+ return;
+
+ gmem_provider_invalidate_folio(gi, folio);
+}
+
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,
+ .invalidate_folio = kvm_gmem_invalidate_folio,
#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
.free_folio = kvm_gmem_free_folio,
#endif
@@ -935,6 +957,7 @@ static int kvm_gmem_init_inode(struct inode *inode, loff_t size, u64 flags)
*/
mapping_set_inaccessible(inode->i_mapping);
WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
+ mapping_set_release_always(inode->i_mapping);
gi->flags = flags;
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 04/17] KVM: guest_memfd: Add helper to attach resource provider file
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (2 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 03/17] KVM: guest_memfd: Support provider folio invalidation Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 05/17] KVM: selftests: Add helper to create guest_memfd with a resource file Ackerley Tng via B4 Relay
` (12 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
A refcount on the resource_fd is taken only to keep the reference stable
for guest_memfd to figure out the existence of a provider. The provider is
responsible for pinning anything required to keep the provider's ops and
the actual provided resource alive.
.release() is called when this guest_memfd inode is destroyed to allow the
provider to clean up.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
Documentation/virt/kvm/api.rst | 28 ++++++++++++++---------
include/linux/kvm_host.h | 2 +-
include/uapi/linux/kvm.h | 5 ++++-
virt/kvm/guest_memfd.c | 51 ++++++++++++++++++++++++++++++++++++++++--
4 files changed, 72 insertions(+), 14 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 67f0f290797ab..b48df2827a2c3 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6470,7 +6470,9 @@ and cannot be resized (guest_memfd files do however support PUNCH_HOLE).
struct kvm_create_guest_memfd {
__u64 size;
__u64 flags;
- __u64 reserved[6];
+ __s32 resource_fd;
+ __u32 pad;
+ __u64 reserved[5];
};
Conceptually, the inode backing a guest_memfd file represents physical memory,
@@ -6492,15 +6494,21 @@ a single guest_memfd file, but the bound ranges must not overlap).
The capability KVM_CAP_GUEST_MEMFD_FLAGS enumerates the `flags` that can be
specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags:
- ============================ ================================================
- GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file
- descriptor.
- GUEST_MEMFD_FLAG_INIT_SHARED Make all memory in the file shared during
- KVM_CREATE_GUEST_MEMFD (memory files created
- without INIT_SHARED will be marked private).
- Shared memory can be faulted into host userspace
- page tables. Private memory cannot.
- ============================ ================================================
+ ============================= ================================================
+ GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file
+ descriptor.
+ GUEST_MEMFD_FLAG_INIT_SHARED Make all memory in the file shared during
+ KVM_CREATE_GUEST_MEMFD (memory files created
+ without INIT_SHARED will be marked private).
+ Shared memory can be faulted into host userspace
+ page tables. Private memory cannot.
+ GUEST_MEMFD_FLAG_USE_RESOURCE Consume resource_fd as an external memory
+ provider or resource pool. When not set,
+ resource_fd must be '0'. When set, the provider
+ must be a supported resource (e.g. tmpfs mount
+ root directory). Currently, tmpfs resource pools
+ require noswap and huge=never mount options.
+ ============================= ================================================
When the KVM MMU performs a PFN lookup to service a guest fault, the fault will
always be consumed from guest_memfd, regardless of whether it is a shared or a
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 485f18454eb45..f4241c05650e9 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2588,7 +2588,7 @@ 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;
+ u64 flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_USE_RESOURCE;
if (!kvm || kvm_arch_supports_gmem_init_shared(kvm))
flags |= GUEST_MEMFD_FLAG_INIT_SHARED;
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 8dff2fc1972e9..f4108bf7e3192 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1674,11 +1674,14 @@ struct kvm_memory_attributes2 {
#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)
#define GUEST_MEMFD_FLAG_MMAP (1ULL << 0)
#define GUEST_MEMFD_FLAG_INIT_SHARED (1ULL << 1)
+#define GUEST_MEMFD_FLAG_USE_RESOURCE (1ULL << 2)
struct kvm_create_guest_memfd {
__u64 size;
__u64 flags;
- __u64 reserved[6];
+ __s32 resource_fd;
+ __u32 pad;
+ __u64 reserved[5];
};
#define KVM_PRE_FAULT_MEMORY _IOWR(KVMIO, 0xd5, struct kvm_pre_fault_memory)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 00f3bd0cf21e6..0e0d7e93f4148 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -71,6 +71,12 @@ static inline void gmem_provider_invalidate_folio(struct gmem_inode *gi,
gi->provider_ops->invalidate_folio(gi->provider, folio);
}
+static inline void gmem_provider_release(struct gmem_inode *gi)
+{
+ if (gi->provider_ops && gi->provider_ops->release)
+ gi->provider_ops->release(gi->provider);
+}
+
#define kvm_gmem_for_each_file(f, inode) \
list_for_each_entry(f, &GMEM_I(inode)->gmem_file_list, entry)
@@ -984,7 +990,37 @@ static int kvm_gmem_init_inode(struct inode *inode, loff_t size, u64 flags)
return r;
}
-static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
+static int kvm_gmem_attach_resource(struct inode *inode, int resource_fd)
+{
+ struct file *resource_file;
+ struct inode *res_inode;
+ const struct guest_memfd_provider_operations *ops = NULL;
+ void *provider;
+
+ resource_file = fget_raw(resource_fd);
+ if (!resource_file)
+ return -EBADF;
+
+ res_inode = file_inode(resource_file);
+ ops = res_inode->i_sb->s_op->gmem_provider_ops;
+
+ if (!ops || !ops->attach || !ops->alloc_folio) {
+ fput(resource_file);
+ return -EOPNOTSUPP;
+ }
+
+ provider = ops->attach(resource_file);
+ fput(resource_file);
+ if (IS_ERR(provider))
+ return PTR_ERR(provider);
+
+ GMEM_I(inode)->provider = provider;
+ GMEM_I(inode)->provider_ops = ops;
+
+ return 0;
+}
+
+static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags, int resource_fd)
{
static const char *name = "[kvm-gmem]";
struct gmem_file *f;
@@ -1018,6 +1054,12 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
if (err)
goto err_inode;
+ if (flags & GUEST_MEMFD_FLAG_USE_RESOURCE) {
+ err = kvm_gmem_attach_resource(inode, resource_fd);
+ if (err)
+ goto err_inode;
+ }
+
file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, &kvm_gmem_fops);
if (IS_ERR(file)) {
err = PTR_ERR(file);
@@ -1057,7 +1099,10 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
if (size <= 0 || !PAGE_ALIGNED(size))
return -EINVAL;
- return __kvm_gmem_create(kvm, size, flags);
+ if (!(flags & GUEST_MEMFD_FLAG_USE_RESOURCE) && args->resource_fd)
+ return -EINVAL;
+
+ return __kvm_gmem_create(kvm, size, flags, args->resource_fd);
}
int kvm_gmem_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *slot,
@@ -1401,6 +1446,8 @@ static void kvm_gmem_destroy_inode(struct inode *inode)
{
struct gmem_inode *gi = GMEM_I(inode);
+ gmem_provider_release(gi);
+
mpol_free_shared_policy(&gi->policy);
/*
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 05/17] KVM: selftests: Add helper to create guest_memfd with a resource file
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (3 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 04/17] KVM: guest_memfd: Add helper to attach resource provider file Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 06/17] KVM: selftests: Test negative validation of resource_fd argument Ackerley Tng via B4 Relay
` (11 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Add a helper to create guest_memfd instances with a resource file
descriptor, and update the existing creation helper to pass a zero
resource file descriptor.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/include/uapi/linux/kvm.h | 5 ++++-
tools/testing/selftests/kvm/include/kvm_util.h | 23 ++++++++++++++++++-----
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 419011097fa8e..66c1f40b50c29 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -1654,11 +1654,14 @@ struct kvm_memory_attributes {
#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)
#define GUEST_MEMFD_FLAG_MMAP (1ULL << 0)
#define GUEST_MEMFD_FLAG_INIT_SHARED (1ULL << 1)
+#define GUEST_MEMFD_FLAG_USE_RESOURCE (1ULL << 2)
struct kvm_create_guest_memfd {
__u64 size;
__u64 flags;
- __u64 reserved[6];
+ __s32 resource_fd;
+ __u32 pad;
+ __u64 reserved[5];
};
#define KVM_PRE_FAULT_MEMORY _IOWR(KVMIO, 0xd5, struct kvm_pre_fault_memory)
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index 777fa3dbf88d6..9eeaefe5a1839 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -767,26 +767,39 @@ static inline bool is_smt_on(void)
void vm_create_irqchip(struct kvm_vm *vm);
-static inline int __vm_create_guest_memfd(struct kvm_vm *vm, u64 size,
- u64 flags)
+static inline int __vm_create_guest_memfd_resource(struct kvm_vm *vm, u64 size,
+ u64 flags, int resource_fd)
{
struct kvm_create_guest_memfd guest_memfd = {
.size = size,
.flags = flags,
+ .resource_fd = resource_fd,
};
return __vm_ioctl(vm, KVM_CREATE_GUEST_MEMFD, &guest_memfd);
}
-static inline int vm_create_guest_memfd(struct kvm_vm *vm, u64 size,
- u64 flags)
+static inline int __vm_create_guest_memfd(struct kvm_vm *vm, u64 size,
+ u64 flags)
+{
+ return __vm_create_guest_memfd_resource(vm, size, flags, 0);
+}
+
+static inline int vm_create_guest_memfd_resource(struct kvm_vm *vm, u64 size,
+ u64 flags, int resource_fd)
{
- int fd = __vm_create_guest_memfd(vm, size, flags);
+ int fd = __vm_create_guest_memfd_resource(vm, size, flags, resource_fd);
TEST_ASSERT(fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_GUEST_MEMFD, fd));
return fd;
}
+static inline int vm_create_guest_memfd(struct kvm_vm *vm, u64 size,
+ u64 flags)
+{
+ return vm_create_guest_memfd_resource(vm, size, flags, 0);
+}
+
void vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags,
gpa_t gpa, u64 size, void *hva);
int __vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags,
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 06/17] KVM: selftests: Test negative validation of resource_fd argument
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (4 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 05/17] KVM: selftests: Add helper to create guest_memfd with a resource file Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 07/17] KVM: selftests: Test rejection of unsupported filesystem for resource_fd Ackerley Tng via B4 Relay
` (10 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Add negative test coverage verifying that creating a guest_memfd
instance with a non-zero resource_fd when GUEST_MEMFD_FLAG_USE_RESOURCE
is clear fails with -EINVAL, and creating with an invalid resource_fd
fails with -EBADF.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 2233d871a38f4..84b958b9ef715 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -404,6 +404,8 @@ static void test_guest_memfd_flags(struct kvm_vm *vm)
int fd;
for (flag = BIT(0); flag; flag <<= 1) {
+ if (flag == GUEST_MEMFD_FLAG_USE_RESOURCE)
+ continue;
fd = __vm_create_guest_memfd(vm, page_size, flag);
if (flag & valid_flags) {
TEST_ASSERT(fd >= 0,
@@ -418,6 +420,22 @@ static void test_guest_memfd_flags(struct kvm_vm *vm)
}
}
+static void test_resource_fd_invalid(struct kvm_vm *vm)
+{
+ int fd;
+
+ /* Non-zero resource_fd without GUEST_MEMFD_FLAG_USE_RESOURCE fails */
+ fd = __vm_create_guest_memfd_resource(vm, page_size, 0, 1);
+ TEST_ASSERT(fd < 0, "guest_memfd with resource_fd but without flag should fail");
+ TEST_ASSERT_EQ(errno, EINVAL);
+
+ /* Bad file descriptor with GUEST_MEMFD_FLAG_USE_RESOURCE fails */
+ fd = __vm_create_guest_memfd_resource(vm, page_size,
+ GUEST_MEMFD_FLAG_USE_RESOURCE, -1);
+ TEST_ASSERT(fd < 0, "guest_memfd with -1 resource_fd should fail");
+ TEST_ASSERT_EQ(errno, EBADF);
+}
+
#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
do { \
int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
@@ -476,6 +494,8 @@ static void test_guest_memfd(unsigned long vm_type)
test_guest_memfd_flags(vm);
+ test_resource_fd_invalid(vm);
+
__test_guest_memfd(vm, 0);
flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 07/17] KVM: selftests: Test rejection of unsupported filesystem for resource_fd
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (5 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 06/17] KVM: selftests: Test negative validation of resource_fd argument Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 08/17] KVM: selftests: Test rejection of tmpfs file " Ackerley Tng via B4 Relay
` (9 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Add test coverage verifying that passing a directory file descriptor
from an unsupported filesystem without provider operations as a resource
file fails with EOPNOTSUPP.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 84b958b9ef715..31bc74c4c26bd 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -436,6 +436,27 @@ static void test_resource_fd_invalid(struct kvm_vm *vm)
TEST_ASSERT_EQ(errno, EBADF);
}
+static void test_resource_fd_unsupported_fs(struct kvm_vm *vm)
+{
+ int fd, file_fd;
+
+ /*
+ * Passing an FD from an unsupported filesystem without provider ops
+ * (e.g. the /proc mount root directory) is rejected by KVM with
+ * EOPNOTSUPP.
+ */
+ file_fd = open("/proc", O_DIRECTORY | O_RDONLY);
+ TEST_ASSERT(file_fd >= 0, "open /proc failed");
+
+ fd = __vm_create_guest_memfd_resource(vm, page_size,
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ file_fd);
+ TEST_ASSERT(fd < 0, "guest_memfd with /proc fd should fail");
+ TEST_ASSERT_EQ(errno, EOPNOTSUPP);
+
+ close(file_fd);
+}
+
#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
do { \
int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
@@ -495,6 +516,7 @@ static void test_guest_memfd(unsigned long vm_type)
test_guest_memfd_flags(vm);
test_resource_fd_invalid(vm);
+ test_resource_fd_unsupported_fs(vm);
__test_guest_memfd(vm, 0);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 08/17] KVM: selftests: Test rejection of tmpfs file for resource_fd
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (6 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 07/17] KVM: selftests: Test rejection of unsupported filesystem for resource_fd Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 09/17] KVM: selftests: Test rejection of swap tmpfs mounts Ackerley Tng via B4 Relay
` (8 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Add test coverage verifying that passing a regular file from tmpfs
as a resource file fails with ENOTDIR. Only directory file descriptors
representing the mount root are supported as resource pools.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 31bc74c4c26bd..a7a1b5470cb2c 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -457,6 +457,26 @@ static void test_resource_fd_unsupported_fs(struct kvm_vm *vm)
close(file_fd);
}
+static void test_resource_fd_tmpfs_file(struct kvm_vm *vm)
+{
+ int fd, file_fd;
+
+ /*
+ * Passing a regular file from tmpfs (e.g. via memfd) is rejected by
+ * the provider attach callback with ENOTDIR because only directory
+ * FDs representing the mount root are supported resource pools.
+ */
+ file_fd = memfd_create("test_tmpfs_file", 0);
+ TEST_ASSERT(file_fd >= 0, "memfd_create failed");
+
+ fd = __vm_create_guest_memfd_resource(vm, page_size,
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ file_fd);
+ TEST_ASSERT(fd < 0, "guest_memfd with tmpfs file fd should fail");
+ TEST_ASSERT_EQ(errno, ENOTDIR);
+ close(file_fd);
+}
+
#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
do { \
int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
@@ -517,6 +537,7 @@ static void test_guest_memfd(unsigned long vm_type)
test_resource_fd_invalid(vm);
test_resource_fd_unsupported_fs(vm);
+ test_resource_fd_tmpfs_file(vm);
__test_guest_memfd(vm, 0);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 09/17] KVM: selftests: Test rejection of swap tmpfs mounts
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (7 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 08/17] KVM: selftests: Test rejection of tmpfs file " Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 10/17] KVM: selftests: Test rejection of hugepage " Ackerley Tng via B4 Relay
` (7 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Ensure that guest_memfd creation fails with -EINVAL when passed a tmpfs
resource pool mount descriptor that has swap enabled, as required by
the tmpfs guest_memfd provider implementation.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 50 ++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index a7a1b5470cb2c..5d017164d7b45 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -13,6 +13,7 @@
#include <linux/bitmap.h>
#include <linux/falloc.h>
+#include <linux/mount.h>
#include <linux/sizes.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -477,6 +478,54 @@ static void test_resource_fd_tmpfs_file(struct kvm_vm *vm)
close(file_fd);
}
+static int create_tmpfs_pool_fd(const char *huge, bool noswap, size_t size)
+{
+ char size_str[32];
+ int fs_fd, mnt_fd;
+
+ TEST_ASSERT(size && IS_ALIGNED(size, page_size),
+ "Pool size '0x%zx' must be positive and page-aligned", size);
+
+ fs_fd = syscall(__NR_fsopen, "tmpfs", FSOPEN_CLOEXEC);
+ TEST_ASSERT(fs_fd >= 0, "fsopen failed");
+
+ if (noswap)
+ TEST_ASSERT(!syscall(__NR_fsconfig, fs_fd, FSCONFIG_SET_FLAG,
+ "noswap", NULL, 0), "fsconfig noswap failed");
+
+ if (huge)
+ TEST_ASSERT(!syscall(__NR_fsconfig, fs_fd, FSCONFIG_SET_STRING,
+ "huge", huge, 0), "fsconfig huge failed");
+
+ snprintf(size_str, sizeof(size_str), "%zu", size);
+ TEST_ASSERT(!syscall(__NR_fsconfig, fs_fd, FSCONFIG_SET_STRING,
+ "size", size_str, 0), "fsconfig size failed");
+
+ TEST_ASSERT(!syscall(__NR_fsconfig, fs_fd, FSCONFIG_CMD_CREATE,
+ NULL, NULL, 0), "fsconfig create failed");
+
+ mnt_fd = syscall(__NR_fsmount, fs_fd, FSMOUNT_CLOEXEC, 0);
+ TEST_ASSERT(mnt_fd > 0, "fsmount failed");
+
+ close(fs_fd);
+ return mnt_fd;
+}
+
+static void test_resource_fd_tmpfs_swap(struct kvm_vm *vm)
+{
+ int pool_fd, fd;
+
+ pool_fd = create_tmpfs_pool_fd("never", false, page_size);
+
+ fd = __vm_create_guest_memfd_resource(vm, page_size,
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ pool_fd);
+ TEST_ASSERT(fd < 0, "guest_memfd with swap-enabled tmpfs should fail");
+ TEST_ASSERT_EQ(errno, EINVAL);
+
+ close(pool_fd);
+}
+
#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
do { \
int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
@@ -538,6 +587,7 @@ static void test_guest_memfd(unsigned long vm_type)
test_resource_fd_invalid(vm);
test_resource_fd_unsupported_fs(vm);
test_resource_fd_tmpfs_file(vm);
+ test_resource_fd_tmpfs_swap(vm);
__test_guest_memfd(vm, 0);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 10/17] KVM: selftests: Test rejection of hugepage tmpfs mounts
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (8 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 09/17] KVM: selftests: Test rejection of swap tmpfs mounts Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 11/17] KVM: selftests: Test guest_memfd with anonymous fsmount() tmpfs pool Ackerley Tng via B4 Relay
` (6 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Ensure that guest_memfd creation fails with -EINVAL when passed a tmpfs
resource pool mount descriptor that has huge pages enabled, as required
by the tmpfs guest_memfd provider implementation.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 5d017164d7b45..e8199ed17b0f8 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -526,6 +526,21 @@ static void test_resource_fd_tmpfs_swap(struct kvm_vm *vm)
close(pool_fd);
}
+static void test_resource_fd_tmpfs_huge(struct kvm_vm *vm)
+{
+ int pool_fd, fd;
+
+ pool_fd = create_tmpfs_pool_fd("always", true, page_size);
+
+ fd = __vm_create_guest_memfd_resource(vm, page_size,
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ pool_fd);
+ TEST_ASSERT(fd < 0, "guest_memfd with huge-enabled tmpfs should fail");
+ TEST_ASSERT_EQ(errno, EINVAL);
+
+ close(pool_fd);
+}
+
#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
do { \
int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
@@ -588,6 +603,7 @@ static void test_guest_memfd(unsigned long vm_type)
test_resource_fd_unsupported_fs(vm);
test_resource_fd_tmpfs_file(vm);
test_resource_fd_tmpfs_swap(vm);
+ test_resource_fd_tmpfs_huge(vm);
__test_guest_memfd(vm, 0);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 11/17] KVM: selftests: Test guest_memfd with anonymous fsmount() tmpfs pool
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (9 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 10/17] KVM: selftests: Test rejection of hugepage " Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:50 ` [PATCH RFC 12/17] KVM: selftests: Test guest_memfd with mounted tmpfs root directory Ackerley Tng via B4 Relay
` (5 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Add test coverage verifying that creating a guest_memfd backed by an
anonymous tmpfs mount descriptor returned by fsmount() succeeds and
reports the requested file size.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 35 ++++++++++++++++++++------
1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index e8199ed17b0f8..73212d5307049 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -541,12 +541,23 @@ static void test_resource_fd_tmpfs_huge(struct kvm_vm *vm)
close(pool_fd);
}
-#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
-do { \
- int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
- \
- test_##__test(args); \
- close(fd); \
+#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
+do { \
+ int pool_fd = -1; \
+ int fd; \
+ \
+ if ((__flags) & GUEST_MEMFD_FLAG_USE_RESOURCE) { \
+ pool_fd = create_tmpfs_pool_fd("never", true, __gmem_size); \
+ fd = vm_create_guest_memfd_resource(__vm, __gmem_size, \
+ __flags, pool_fd); \
+ } else { \
+ fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
+ } \
+ \
+ test_##__test(args); \
+ close(fd); \
+ if (pool_fd >= 0) \
+ close(pool_fd); \
} while (0)
#define __gmem_test(__test, __vm, __flags, __gmem_size) \
@@ -606,15 +617,23 @@ static void test_guest_memfd(unsigned long vm_type)
test_resource_fd_tmpfs_huge(vm);
__test_guest_memfd(vm, 0);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_USE_RESOURCE);
flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
- if (flags & GUEST_MEMFD_FLAG_MMAP)
+ if (flags & GUEST_MEMFD_FLAG_MMAP) {
__test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
+ GUEST_MEMFD_FLAG_USE_RESOURCE);
+ }
/* MMAP should always be supported if INIT_SHARED is supported. */
- if (flags & GUEST_MEMFD_FLAG_INIT_SHARED)
+ if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) {
__test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
GUEST_MEMFD_FLAG_INIT_SHARED);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
+ GUEST_MEMFD_FLAG_INIT_SHARED |
+ GUEST_MEMFD_FLAG_USE_RESOURCE);
+ }
kvm_vm_free(vm);
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 12/17] KVM: selftests: Test guest_memfd with mounted tmpfs root directory
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (10 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 11/17] KVM: selftests: Test guest_memfd with anonymous fsmount() tmpfs pool Ackerley Tng via B4 Relay
@ 2026-09-26 0:50 ` Ackerley Tng via B4 Relay
2026-09-26 0:51 ` [PATCH RFC 13/17] KVM: selftests: Test guest_memfd resource pool sharing across instances Ackerley Tng via B4 Relay
` (4 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:50 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Add test coverage verifying that opening the root directory of an
existing mounted tmpfs filesystem and passing that file descriptor as
the resource pool succeeds across all guest_memfd subtests and flag
combinations.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 137 +++++++++++++++++++------
1 file changed, 103 insertions(+), 34 deletions(-)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 73212d5307049..94ef64474f2a0 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -10,11 +10,13 @@
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
+#include <limits.h>
#include <linux/bitmap.h>
#include <linux/falloc.h>
#include <linux/mount.h>
#include <linux/sizes.h>
+#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -541,66 +543,122 @@ static void test_resource_fd_tmpfs_huge(struct kvm_vm *vm)
close(pool_fd);
}
-#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \
+enum gmem_pool_type {
+ GMEM_POOL_NONE,
+ GMEM_POOL_FSMOUNT,
+ GMEM_POOL_MOUNTED_DIR,
+};
+
+struct gmem_pool {
+ int fd;
+ char path[PATH_MAX];
+ bool is_mounted;
+};
+
+static struct gmem_pool create_gmem_pool(size_t size, enum gmem_pool_type type)
+{
+ struct gmem_pool pool = { .fd = -1, .is_mounted = false };
+ int mnt_fd;
+
+ if (type == GMEM_POOL_NONE)
+ return pool;
+
+ mnt_fd = create_tmpfs_pool_fd("never", true, size);
+ TEST_REQUIRE(mnt_fd >= 0);
+
+ if (type == GMEM_POOL_FSMOUNT) {
+ pool.fd = mnt_fd;
+ return pool;
+ }
+
+ strcpy(pool.path, "/tmp/gmem_test_dir_XXXXXX");
+ TEST_ASSERT(mkdtemp(pool.path), "mkdtemp failed");
+
+ if (syscall(__NR_move_mount, mnt_fd, "", AT_FDCWD, pool.path,
+ MOVE_MOUNT_F_EMPTY_PATH)) {
+ close(mnt_fd);
+ rmdir(pool.path);
+ TEST_REQUIRE(false);
+ }
+ close(mnt_fd);
+
+ pool.fd = open(pool.path, O_RDONLY | O_DIRECTORY);
+ TEST_ASSERT(pool.fd >= 0, "open mounted tmpfs root failed");
+ pool.is_mounted = true;
+
+ return pool;
+}
+
+static void destroy_gmem_pool(struct gmem_pool *pool)
+{
+ if (pool->fd >= 0)
+ close(pool->fd);
+ if (pool->is_mounted) {
+ TEST_ASSERT(!umount(pool->path), "umount failed");
+ TEST_ASSERT(!rmdir(pool->path), "rmdir failed");
+ }
+}
+
+#define ____gmem_test(__test, __vm, __flags, __pool_type, __gmem_size, args...) \
do { \
- int pool_fd = -1; \
+ struct gmem_pool pool = { .fd = -1 }; \
int fd; \
\
if ((__flags) & GUEST_MEMFD_FLAG_USE_RESOURCE) { \
- pool_fd = create_tmpfs_pool_fd("never", true, __gmem_size); \
+ pool = create_gmem_pool(__gmem_size, __pool_type); \
fd = vm_create_guest_memfd_resource(__vm, __gmem_size, \
- __flags, pool_fd); \
+ __flags, pool.fd); \
} else { \
fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \
} \
\
test_##__test(args); \
close(fd); \
- if (pool_fd >= 0) \
- close(pool_fd); \
+ destroy_gmem_pool(&pool); \
} while (0)
-#define __gmem_test(__test, __vm, __flags, __gmem_size) \
- ____gmem_test(__test, __vm, __flags, __gmem_size, fd, __gmem_size)
+#define __gmem_test(__test, __vm, __flags, __pool_type, __gmem_size) \
+ ____gmem_test(__test, __vm, __flags, __pool_type, __gmem_size, fd, __gmem_size)
-#define gmem_test(__test, __vm, __flags) \
- __gmem_test(__test, __vm, __flags, page_size * 4)
+#define gmem_test(__test, __vm, __flags, __pool_type) \
+ __gmem_test(__test, __vm, __flags, __pool_type, page_size * 4)
-#define __gmem_test_vm(__test, __vm, __flags, __gmem_size) \
- ____gmem_test(__test, __vm, __flags, __gmem_size, __vm, fd, __gmem_size)
+#define __gmem_test_vm(__test, __vm, __flags, __pool_type, __gmem_size) \
+ ____gmem_test(__test, __vm, __flags, __pool_type, __gmem_size, __vm, fd, __gmem_size)
-#define gmem_test_vm(__test, __vm, __flags) \
- __gmem_test_vm(__test, __vm, __flags, page_size * 4)
+#define gmem_test_vm(__test, __vm, __flags, __pool_type) \
+ __gmem_test_vm(__test, __vm, __flags, __pool_type, page_size * 4)
-static void __test_guest_memfd(struct kvm_vm *vm, u64 flags)
+static void __test_guest_memfd(struct kvm_vm *vm, u64 flags,
+ enum gmem_pool_type pool_type)
{
test_create_guest_memfd_multiple(vm);
test_create_guest_memfd_invalid_sizes(vm, flags);
- gmem_test(file_read_write, vm, flags);
+ gmem_test(file_read_write, vm, flags, pool_type);
if (flags & GUEST_MEMFD_FLAG_MMAP) {
if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) {
size_t pmd_size = get_trans_hugepagesz();
- gmem_test(mmap_supported, vm, flags);
- gmem_test(fault_overflow, vm, flags);
- gmem_test(numa_allocation, vm, flags);
- __gmem_test(collapse, vm, flags, pmd_size);
+ gmem_test(mmap_supported, vm, flags, pool_type);
+ gmem_test(fault_overflow, vm, flags, pool_type);
+ gmem_test(numa_allocation, vm, flags, pool_type);
+ __gmem_test(collapse, vm, flags, pool_type, pmd_size);
} else {
- gmem_test(fault_private, vm, flags);
+ gmem_test(fault_private, vm, flags, pool_type);
}
- gmem_test(mmap_cow, vm, flags);
- gmem_test(mbind, vm, flags);
+ gmem_test(mmap_cow, vm, flags, pool_type);
+ gmem_test(mbind, vm, flags, pool_type);
} else {
- gmem_test(mmap_not_supported, vm, flags);
+ gmem_test(mmap_not_supported, vm, flags, pool_type);
}
- gmem_test(file_size, vm, flags);
- gmem_test(fallocate, vm, flags);
- gmem_test(invalid_punch_hole, vm, flags);
- gmem_test_vm(invalid_binding, vm, flags);
+ gmem_test(file_size, vm, flags, pool_type);
+ gmem_test(fallocate, vm, flags, pool_type);
+ gmem_test(invalid_punch_hole, vm, flags, pool_type);
+ gmem_test_vm(invalid_binding, vm, flags, pool_type);
}
static void test_guest_memfd(unsigned long vm_type)
@@ -616,23 +674,34 @@ static void test_guest_memfd(unsigned long vm_type)
test_resource_fd_tmpfs_swap(vm);
test_resource_fd_tmpfs_huge(vm);
- __test_guest_memfd(vm, 0);
- __test_guest_memfd(vm, GUEST_MEMFD_FLAG_USE_RESOURCE);
+ __test_guest_memfd(vm, 0, GMEM_POOL_NONE);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_USE_RESOURCE, GMEM_POOL_FSMOUNT);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_USE_RESOURCE, GMEM_POOL_MOUNTED_DIR);
flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
if (flags & GUEST_MEMFD_FLAG_MMAP) {
- __test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP, GMEM_POOL_NONE);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ GMEM_POOL_FSMOUNT);
__test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
- GUEST_MEMFD_FLAG_USE_RESOURCE);
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ GMEM_POOL_MOUNTED_DIR);
}
/* MMAP should always be supported if INIT_SHARED is supported. */
if (flags & GUEST_MEMFD_FLAG_INIT_SHARED) {
__test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
- GUEST_MEMFD_FLAG_INIT_SHARED);
+ GUEST_MEMFD_FLAG_INIT_SHARED,
+ GMEM_POOL_NONE);
+ __test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
+ GUEST_MEMFD_FLAG_INIT_SHARED |
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ GMEM_POOL_FSMOUNT);
__test_guest_memfd(vm, GUEST_MEMFD_FLAG_MMAP |
GUEST_MEMFD_FLAG_INIT_SHARED |
- GUEST_MEMFD_FLAG_USE_RESOURCE);
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ GMEM_POOL_MOUNTED_DIR);
}
kvm_vm_free(vm);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 13/17] KVM: selftests: Test guest_memfd resource pool sharing across instances
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (11 preceding siblings ...)
2026-09-26 0:50 ` [PATCH RFC 12/17] KVM: selftests: Test guest_memfd with mounted tmpfs root directory Ackerley Tng via B4 Relay
@ 2026-09-26 0:51 ` Ackerley Tng via B4 Relay
2026-09-26 0:51 ` [PATCH RFC 14/17] KVM: selftests: Test memory allocation against shared tmpfs resource pool Ackerley Tng via B4 Relay
` (3 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:51 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Test that multiple guest_memfd instances can be created backed by the
same tmpfs resource pool descriptor, that each file descriptor reflects
its requested size, and that they have distinct inodes.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 36 ++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 94ef64474f2a0..81311be059923 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -543,6 +543,41 @@ static void test_resource_fd_tmpfs_huge(struct kvm_vm *vm)
close(pool_fd);
}
+static void test_resource_fd_tmpfs_shared_pool(struct kvm_vm *vm)
+{
+ int pool_fd, fd1, fd2;
+ struct stat st1, st2;
+
+ pool_fd = create_tmpfs_pool_fd("never", true, page_size * 2);
+ if (pool_fd < 0)
+ TEST_REQUIRE(false);
+
+ fd1 = vm_create_guest_memfd_resource(vm, page_size,
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ pool_fd);
+
+ fd2 = vm_create_guest_memfd_resource(vm, page_size * 2,
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ pool_fd);
+
+ TEST_ASSERT(!fstat(fd1, &st1), "fstat on fd1 should succeed");
+ TEST_ASSERT(st1.st_size == page_size,
+ "fd1 st_size (%lu) should match requested size (%lu)",
+ st1.st_size, page_size);
+
+ TEST_ASSERT(!fstat(fd2, &st2), "fstat on fd2 should succeed");
+ TEST_ASSERT(st2.st_size == page_size * 2,
+ "fd2 st_size (%lu) should match requested size (%lu)",
+ st2.st_size, page_size * 2);
+
+ TEST_ASSERT(st1.st_ino != st2.st_ino,
+ "different guest_memfd instances should have distinct inodes");
+
+ close(fd2);
+ close(fd1);
+ close(pool_fd);
+}
+
enum gmem_pool_type {
GMEM_POOL_NONE,
GMEM_POOL_FSMOUNT,
@@ -673,6 +708,7 @@ static void test_guest_memfd(unsigned long vm_type)
test_resource_fd_tmpfs_file(vm);
test_resource_fd_tmpfs_swap(vm);
test_resource_fd_tmpfs_huge(vm);
+ test_resource_fd_tmpfs_shared_pool(vm);
__test_guest_memfd(vm, 0, GMEM_POOL_NONE);
__test_guest_memfd(vm, GUEST_MEMFD_FLAG_USE_RESOURCE, GMEM_POOL_FSMOUNT);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 14/17] KVM: selftests: Test memory allocation against shared tmpfs resource pool
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (12 preceding siblings ...)
2026-09-26 0:51 ` [PATCH RFC 13/17] KVM: selftests: Test guest_memfd resource pool sharing across instances Ackerley Tng via B4 Relay
@ 2026-09-26 0:51 ` Ackerley Tng via B4 Relay
2026-09-26 0:51 ` [PATCH RFC 15/17] KVM: selftests: Test that shared tmpfs resource pool size limit is respected Ackerley Tng via B4 Relay
` (2 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:51 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Verify that allocating folios in multiple guest_memfd instances sharing
the same tmpfs resource pool consumes blocks from the underlying pool as
reported by filesystem statistics.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 81311be059923..4610eeab39be6 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -19,6 +19,7 @@
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include "kvm_syscalls.h"
#include "kvm_util.h"
@@ -545,7 +546,8 @@ static void test_resource_fd_tmpfs_huge(struct kvm_vm *vm)
static void test_resource_fd_tmpfs_shared_pool(struct kvm_vm *vm)
{
- int pool_fd, fd1, fd2;
+ int pool_fd, fd1, fd2, ret;
+ struct statvfs svfs;
struct stat st1, st2;
pool_fd = create_tmpfs_pool_fd("never", true, page_size * 2);
@@ -573,6 +575,18 @@ static void test_resource_fd_tmpfs_shared_pool(struct kvm_vm *vm)
TEST_ASSERT(st1.st_ino != st2.st_ino,
"different guest_memfd instances should have distinct inodes");
+ ret = fallocate(fd1, FALLOC_FL_KEEP_SIZE, 0, page_size);
+ TEST_ASSERT(!ret, "fallocate on fd1 should succeed");
+
+ TEST_ASSERT(!fstatvfs(pool_fd, &svfs), "fstatvfs on pool_fd should succeed");
+ TEST_ASSERT_EQ(svfs.f_blocks - svfs.f_bfree, 1);
+
+ ret = fallocate(fd2, FALLOC_FL_KEEP_SIZE, 0, page_size);
+ TEST_ASSERT(!ret, "fallocate on fd2 should succeed");
+
+ TEST_ASSERT(!fstatvfs(pool_fd, &svfs), "fstatvfs on pool_fd should succeed");
+ TEST_ASSERT_EQ(svfs.f_blocks - svfs.f_bfree, 2);
+
close(fd2);
close(fd1);
close(pool_fd);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 15/17] KVM: selftests: Test that shared tmpfs resource pool size limit is respected
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (13 preceding siblings ...)
2026-09-26 0:51 ` [PATCH RFC 14/17] KVM: selftests: Test memory allocation against shared tmpfs resource pool Ackerley Tng via B4 Relay
@ 2026-09-26 0:51 ` Ackerley Tng via B4 Relay
2026-09-26 0:51 ` [PATCH RFC 16/17] KVM: selftests: Test guest execution with tmpfs-backed guest_memfd Ackerley Tng via B4 Relay
2026-09-26 0:51 ` [PATCH RFC 17/17] KVM: selftests: Document testing TODOs Ackerley Tng via B4 Relay
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:51 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Verify that allocating memory across guest_memfd instances sharing a
tmpfs resource pool fails with ENOSPC when the total allocation exceeds
the pool capacity.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 4610eeab39be6..345b41c912a18 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -587,6 +587,10 @@ static void test_resource_fd_tmpfs_shared_pool(struct kvm_vm *vm)
TEST_ASSERT(!fstatvfs(pool_fd, &svfs), "fstatvfs on pool_fd should succeed");
TEST_ASSERT_EQ(svfs.f_blocks - svfs.f_bfree, 2);
+ ret = fallocate(fd2, FALLOC_FL_KEEP_SIZE, page_size, page_size);
+ TEST_ASSERT(ret < 0, "fallocate exceeding pool capacity should fail");
+ TEST_ASSERT_EQ(errno, ENOSPC);
+
close(fd2);
close(fd1);
close(pool_fd);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 16/17] KVM: selftests: Test guest execution with tmpfs-backed guest_memfd
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (14 preceding siblings ...)
2026-09-26 0:51 ` [PATCH RFC 15/17] KVM: selftests: Test that shared tmpfs resource pool size limit is respected Ackerley Tng via B4 Relay
@ 2026-09-26 0:51 ` Ackerley Tng via B4 Relay
2026-09-26 0:51 ` [PATCH RFC 17/17] KVM: selftests: Document testing TODOs Ackerley Tng via B4 Relay
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:51 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Test that a guest vCPU can execute instructions and read/write memory
mapped into a guest_memfd instance that is backed by a tmpfs resource
pool descriptor.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 57 ++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 345b41c912a18..bf369874f2647 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -829,6 +829,62 @@ static void test_guest_memfd_guest(void)
kvm_vm_free(vm);
}
+static void test_guest_memfd_guest_resource(void)
+{
+ const gpa_t gpa = SZ_4G;
+ const int slot = 1;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ int pool_fd, fd, i;
+ size_t size;
+ u8 *mem;
+
+ if (!kvm_check_cap(KVM_CAP_GUEST_MEMFD_FLAGS))
+ return;
+
+ pool_fd = create_tmpfs_pool_fd("never", true, page_size);
+ if (pool_fd < 0)
+ TEST_REQUIRE(false);
+
+ vm = __vm_create_shape_with_one_vcpu(VM_SHAPE_DEFAULT, &vcpu, 1, guest_code);
+
+ TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_MMAP,
+ "Default VM type should support MMAP, supported flags = 0x%x",
+ vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
+ TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_INIT_SHARED,
+ "Default VM type should support INIT_SHARED, supported flags = 0x%x",
+ vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
+
+ size = max_t(size_t, vm->page_size, page_size);
+ fd = __vm_create_guest_memfd_resource(vm, size,
+ GUEST_MEMFD_FLAG_MMAP |
+ GUEST_MEMFD_FLAG_INIT_SHARED |
+ GUEST_MEMFD_FLAG_USE_RESOURCE,
+ pool_fd);
+ TEST_ASSERT(fd >= 0, "guest_memfd with tmpfs pool should succeed");
+
+ vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0);
+
+ mem = kvm_mmap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
+ memset(mem, 0xaa, size);
+ kvm_munmap(mem, size);
+
+ virt_map(vm, gpa, gpa, size / vm->page_size);
+ vcpu_args_set(vcpu, 2, gpa, size);
+ vcpu_run(vcpu);
+
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);
+
+ mem = kvm_mmap(size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
+ for (i = 0; i < size; i++)
+ TEST_ASSERT_EQ(mem[i], 0xff);
+
+ close(fd);
+ close(pool_fd);
+ kvm_vm_free(vm);
+}
+
+
int main(int argc, char *argv[])
{
unsigned long vm_types, vm_type;
@@ -849,4 +905,5 @@ int main(int argc, char *argv[])
test_guest_memfd(vm_type);
test_guest_memfd_guest();
+ test_guest_memfd_guest_resource();
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH RFC 17/17] KVM: selftests: Document testing TODOs
2026-09-26 0:50 [PATCH RFC 00/17] Allow guest_memfd to be created using a resource (pool) fd Ackerley Tng via B4 Relay
` (15 preceding siblings ...)
2026-09-26 0:51 ` [PATCH RFC 16/17] KVM: selftests: Test guest execution with tmpfs-backed guest_memfd Ackerley Tng via B4 Relay
@ 2026-09-26 0:51 ` Ackerley Tng via B4 Relay
16 siblings, 0 replies; 18+ messages in thread
From: Ackerley Tng via B4 Relay @ 2026-09-26 0:51 UTC (permalink / raw)
To: Hugh Dickins, Baolin Wang, Andrew Morton, Sean Christopherson,
Paolo Bonzini, David Hildenbrand, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Shuah Khan, vannapurve, erdemaktas, jxgao,
rientjes, fvdl, jthoughton, tarunsahu, pratyush, fuad.tabba,
Gregory Price, David Woodhouse, yan.y.zhao, michael.roth,
suzuki.poulose, Christian Brauner, Jason Gunthorpe, Nicolin Chen,
Xu Yilun, aik, aneesh.kumar, Vlastimil Babka
Cc: kernel-team, kernel-team, linux-kernel, linux-mm, kvm, linux-doc,
linux-kselftest, Ackerley Tng
From: Ackerley Tng <ackerleytng@google.com>
Remind myself about all the tests (A)I did not write.
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
tools/testing/selftests/kvm/guest_memfd_test.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index bf369874f2647..4e5d00df899a6 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -721,6 +721,16 @@ static void test_guest_memfd(unsigned long vm_type)
test_guest_memfd_flags(vm);
+ /*
+ * TODO: Test that guest_memfd creation rejects a read-only tmpfs mount
+ * (e.g. MOUNT_ATTR_RDONLY) with EROFS.
+ *
+ * TODO: Test that guest_memfd creation rejects an ID-mapped tmpfs mount
+ * (e.g. MOUNT_ATTR_IDMAP) lacking write or execute/search permissions
+ * with EACCES.
+ *
+ * TODO: Test mpol fallback to mount config.
+ */
test_resource_fd_invalid(vm);
test_resource_fd_unsupported_fs(vm);
test_resource_fd_tmpfs_file(vm);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 18+ messages in thread