* [PATCH 1/5] mm/mempolicy: add mempolicy_create()
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
@ 2026-09-02 19:46 ` Gregory Price
2026-09-02 19:46 ` [PATCH 2/5] mm/mempolicy: add mpol_set_shared_policy_range() Gregory Price
` (5 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-02 19:46 UTC (permalink / raw)
To: linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, david, ziy, matthew.brost, joshua.hahnjy, rakie.kim,
byungchul, gourry, ying.huang, apopple, shuah
do_set_mempolicy() builds a validated policy and installs it into
the current running task.
An in-kernel user that wants to construct a mempolicy wants the same
validation without the install step.
Add mempolicy_create(mode, flags, nodes): it allocates the policy and
contextualises it against the calling task's cpuset, and returns the
mempolicy to the caller (or ERR_PTR on error).
do_set_mempolicy() is deliberately left alone rather than reimplemented
on top of it. For syscall users, mpol_set_nodemask() and the current
task policy swap must run under the task_lock(current) acquisition.
Export the symbol to kvm for use in guest_memfd integration.
Signed-off-by: Gregory Price <gourry@gourry.net>
---
include/linux/mempolicy.h | 3 +++
mm/mempolicy.c | 40 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 65c732d440d2f..aef018ad92317 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -128,6 +128,9 @@ void mpol_free_shared_policy(struct shared_policy *sp);
struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp,
pgoff_t idx);
+struct mempolicy *mempolicy_create(unsigned short mode, unsigned short flags,
+ nodemask_t *nodes);
+
struct mempolicy *get_task_policy(struct task_struct *p);
struct mempolicy *__get_vma_policy(struct vm_area_struct *vma,
unsigned long addr, pgoff_t *ilx);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 2ad0a5f18280a..da133ffe0b1c8 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1085,7 +1085,45 @@ static int mbind_range(struct vma_iterator *vmi, struct vm_area_struct *vma,
return vma_replace_policy(vma, new_pol);
}
-/* Set the process memory policy */
+/**
+ * mempolicy_create - build a validated, cpuset-contextualised mempolicy
+ * @mode: MPOL_* mode
+ * @flags: MPOL_F_* flags
+ * @nodes: target nodemask, or NULL (interpreted per @mode; see mpol_new())
+ *
+ * Creates a new policy and constrains it to the task's cpuset.
+ *
+ * The caller owns the returned reference and frees it with mpol_put().
+ *
+ * Return: the policy (NULL for a default policy), or an ERR_PTR on failure.
+ */
+struct mempolicy *mempolicy_create(unsigned short mode, unsigned short flags,
+ nodemask_t *nodes)
+{
+ struct mempolicy *pol;
+ NODEMASK_SCRATCH(scratch);
+ int err;
+
+ if (!scratch)
+ return ERR_PTR(-ENOMEM);
+
+ pol = mpol_new(mode, flags, nodes);
+ if (IS_ERR(pol))
+ goto out;
+
+ task_lock(current);
+ err = mpol_set_nodemask(pol, nodes, scratch);
+ task_unlock(current);
+ if (err) {
+ mpol_put(pol);
+ pol = ERR_PTR(err);
+ }
+out:
+ NODEMASK_SCRATCH_FREE(scratch);
+ return pol;
+}
+EXPORT_SYMBOL_FOR_MODULES(mempolicy_create, "kvm");
+
static long do_set_mempolicy(unsigned short mode, unsigned short flags,
nodemask_t *nodes)
{
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 2/5] mm/mempolicy: add mpol_set_shared_policy_range()
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
2026-09-02 19:46 ` [PATCH 1/5] mm/mempolicy: add mempolicy_create() Gregory Price
@ 2026-09-02 19:46 ` Gregory Price
2026-09-02 19:46 ` [PATCH 3/5] KVM: guest_memfd: bind backing memory to a NUMA node at creation Gregory Price
` (4 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-02 19:46 UTC (permalink / raw)
To: linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, david, ziy, matthew.brost, joshua.hahnjy, rakie.kim,
byungchul, gourry, ying.huang, apopple, shuah, Dave Jiang
mpol_set_shared_policy() installs a policy over a VMA's page-offset
range, which is the only programmatic way to populate an inode's
shared policy after init.
Two limitations make it unusable for binding an entire backing inode
from in-kernel code:
- It requires a VMA, so it cannot cover unmapped offsets.
(e.g. unmapped file folios faulted by pagecache)
- mpol_shared_policy_init(), the only no-VMA installer, reconstructs
the policy from mpol->w.user_nodemask. That field is only populated
for static/relative or mount-string policies.
a policy built programmatically (e.g. by mempolicy_create()) leaves it
empty and stores w.cpuset_mems_allowed instead and _init mangles it.
Both matter to a caller that wants a whole inode bound at creation.
guest_memfd is one: it has no VMA at that point and may never gain one
for a given offset, and it builds its policy in-kernel rather than from
a mount string, so neither existing installer can express it.
Add mpol_set_shared_policy_range(), which installs an already-built,
fully contextualised policy verbatim over an arbitrary [start, end)
page range with no VMA.
Reimplement mpol_set_shared_policy() as a thin wrapper that derives
the range from the VMA, so both share a single underlying path.
Suggested-by: Dave Jiang <dave.jiang@intel.com>
Co-developed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Gregory Price <gourry@gourry.net>
Assisted-by: Claude:claude-opus-4-8
---
include/linux/mempolicy.h | 2 ++
mm/mempolicy.c | 35 +++++++++++++++++++++++++++++------
2 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index aef018ad92317..398318175cec7 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -124,6 +124,8 @@ int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst);
void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol);
int mpol_set_shared_policy(struct shared_policy *sp,
struct vm_area_struct *vma, struct mempolicy *mpol);
+int mpol_set_shared_policy_range(struct shared_policy *sp, pgoff_t start,
+ pgoff_t end, struct mempolicy *mpol);
void mpol_free_shared_policy(struct shared_policy *sp);
struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp,
pgoff_t idx);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index da133ffe0b1c8..ce10ce4374643 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -3312,24 +3312,47 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
}
EXPORT_SYMBOL_FOR_MODULES(mpol_shared_policy_init, "kvm");
-int mpol_set_shared_policy(struct shared_policy *sp,
- struct vm_area_struct *vma, struct mempolicy *pol)
+/**
+ * mpol_set_shared_policy_range - install @pol over [@start, @end) of @sp
+ * @sp: the shared policy tree
+ * @start: first page offset (inclusive)
+ * @end: last page offset (exclusive)
+ * @pol: a fully-built, validated policy, or NULL to clear the range
+ *
+ * Installs @pol over the given range, replacing any overlapping policy.
+ * @sp takes its own reference, the caller retains its reference on @pol.
+ *
+ * The policy is not reconstructed, so the policy is preserved exactly.
+ *
+ * Unlike mpol_set_shared_policy(), no VMA is required, so a range that
+ * is never mapped into a VMA can be covered, including the whole file.
+ *
+ * Return: 0 on success, -ENOMEM on allocation failure.
+ */
+int mpol_set_shared_policy_range(struct shared_policy *sp, pgoff_t start,
+ pgoff_t end, struct mempolicy *pol)
{
- const pgoff_t pgoff = vma_start_pgoff(vma);
- const pgoff_t pgoff_end = vma_end_pgoff(vma);
struct sp_node *new = NULL;
int err;
if (pol) {
- new = sp_alloc(pgoff, pgoff_end, pol);
+ new = sp_alloc(start, end, pol);
if (!new)
return -ENOMEM;
}
- err = shared_policy_replace(sp, pgoff, pgoff_end, new);
+ err = shared_policy_replace(sp, start, end, new);
if (err && new)
sp_free(new);
return err;
}
+EXPORT_SYMBOL_FOR_MODULES(mpol_set_shared_policy_range, "kvm");
+
+int mpol_set_shared_policy(struct shared_policy *sp,
+ struct vm_area_struct *vma, struct mempolicy *pol)
+{
+ return mpol_set_shared_policy_range(sp, vma->vm_pgoff,
+ vma->vm_pgoff + vma_pages(vma), pol);
+}
EXPORT_SYMBOL_FOR_MODULES(mpol_set_shared_policy, "kvm");
/* Free a backing policy store on inode delete. */
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 3/5] KVM: guest_memfd: bind backing memory to a NUMA node at creation
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
2026-09-02 19:46 ` [PATCH 1/5] mm/mempolicy: add mempolicy_create() Gregory Price
2026-09-02 19:46 ` [PATCH 2/5] mm/mempolicy: add mpol_set_shared_policy_range() Gregory Price
@ 2026-09-02 19:46 ` Gregory Price
2026-09-02 19:46 ` [PATCH 4/5] selftests: KVM: guest_memfd: let the gmem_test() harness bind a node Gregory Price
` (3 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-02 19:46 UTC (permalink / raw)
To: linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, david, ziy, matthew.brost, joshua.hahnjy, rakie.kim,
byungchul, gourry, ying.huang, apopple, shuah, Dave Jiang
guest_memfd presently allocates its page-cache folios through a
per-inode shared mempolicy (kvm_gmem_get_folio()).
Today that policy can only be set after the fact, via mbind() on
a host mmap of the fd. This requires the fd to be mmap-able and
cannot reach folios that are only ever guest-faulted (no host VMA).
Neither holds for a non-mappable (confidential) guest_memfd.
Add GUEST_MEMFD_FLAG_BIND_NODE.
When set, KVM builds an MPOL_BIND policy for the requested node and
installs it over the whole inode's shared policy, so every folio
is allocated on the requested node with no userspace mbind().
The flag is advertised through KVM_CAP_GUEST_MEMFD_FLAGS only when
CONFIG_NUMA is enabled.
Two user-visible behaviors worth noting:
- mempolicy_create() constrains the request against the calling task's
cpuset. Requesting a node outside the cpuset mems_allowed results
in the ioctl failing with -EINVAL. Binding is essentially subject
to the same cpuset constraint as mbind().
This behavior is correct - a task cannot grant a guest_memfd access
to a node it cannot access itself.
- The policy hangs off the inode, and nothing rebinds an inode's
shared policy on a later cpuset change.
mpol_rebind_task() walks tsk->mempolicy
mpol_rebind_mm() walks vma->vm_policy
Neither walker reaches a struct shared_policy.
The binding is therefore fixed for the life of the fd. That matches
shmem, whose inode policy behaves the same way, and is the intent
here: the node is a property of the guest's backing memory, not of
whoever happens to hold the fd.
Suggested-by: Dave Jiang <dave.jiang@intel.com>
Co-developed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Gregory Price <gourry@gourry.net>
Assisted-by: Claude:claude-opus-4-8
---
include/linux/kvm_host.h | 3 +++
include/uapi/linux/kvm.h | 5 ++++-
virt/kvm/guest_memfd.c | 44 ++++++++++++++++++++++++++++++++++++++--
3 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 03bfc92864b6e..738e276633c1e 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -739,6 +739,9 @@ static inline u64 kvm_gmem_get_supported_flags(struct kvm *kvm)
if (!kvm || kvm_arch_supports_gmem_init_shared(kvm))
flags |= GUEST_MEMFD_FLAG_INIT_SHARED;
+ if (IS_ENABLED(CONFIG_NUMA))
+ flags |= GUEST_MEMFD_FLAG_BIND_NODE;
+
return flags;
}
#endif
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index ac2d77d149635..8d3ae7e2ead8e 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1658,11 +1658,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_BIND_NODE (1ULL << 2)
struct kvm_create_guest_memfd {
__u64 size;
__u64 flags;
- __u64 reserved[6];
+ __u32 node;
+ __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 625e62e1a0318..dc9f071dd969b 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -423,6 +423,31 @@ static struct mempolicy *kvm_gmem_get_policy(struct vm_area_struct *vma,
*/
return mpol_shared_policy_lookup(&GMEM_I(inode)->policy, pgoff);
}
+
+static int kvm_gmem_bind_node(struct inode *inode, int node)
+{
+ struct mempolicy *pol;
+ nodemask_t nodes;
+ int err;
+
+ if ((unsigned int)node >= MAX_NUMNODES)
+ return -EINVAL;
+
+ init_nodemask_of_node(&nodes, node);
+ pol = mempolicy_create(MPOL_BIND, 0, &nodes);
+ if (IS_ERR(pol))
+ return PTR_ERR(pol);
+
+ err = mpol_set_shared_policy_range(&GMEM_I(inode)->policy, 0,
+ MAX_LFS_FILESIZE >> PAGE_SHIFT, pol);
+ mpol_put(pol);
+ return err;
+}
+#else
+static int kvm_gmem_bind_node(struct inode *inode, int node)
+{
+ return -EINVAL;
+}
#endif /* CONFIG_NUMA */
static const struct vm_operations_struct kvm_gmem_vm_ops = {
@@ -520,7 +545,7 @@ bool __weak kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
return true;
}
-static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
+static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags, int node)
{
static const char *name = "[kvm-gmem]";
struct gmem_file *f;
@@ -561,6 +586,12 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
GMEM_I(inode)->flags = flags;
+ if (flags & GUEST_MEMFD_FLAG_BIND_NODE) {
+ err = kvm_gmem_bind_node(inode, node);
+ 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);
@@ -593,6 +624,7 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
{
loff_t size = args->size;
u64 flags = args->flags;
+ int node = NUMA_NO_NODE;
if (flags & ~kvm_gmem_get_supported_flags(kvm))
return -EINVAL;
@@ -600,7 +632,15 @@ 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_BIND_NODE) {
+ if (args->pad || args->node >= MAX_NUMNODES)
+ return -EINVAL;
+ node = args->node;
+ } else if (args->node || args->pad) {
+ return -EINVAL;
+ }
+
+ return __kvm_gmem_create(kvm, size, flags, node);
}
int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 4/5] selftests: KVM: guest_memfd: let the gmem_test() harness bind a node
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
` (2 preceding siblings ...)
2026-09-02 19:46 ` [PATCH 3/5] KVM: guest_memfd: bind backing memory to a NUMA node at creation Gregory Price
@ 2026-09-02 19:46 ` Gregory Price
2026-09-02 19:46 ` [PATCH 5/5] selftests: KVM: guest_memfd: test GUEST_MEMFD_FLAG_BIND_NODE Gregory Price
` (2 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-02 19:46 UTC (permalink / raw)
To: linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, david, ziy, matthew.brost, joshua.hahnjy, rakie.kim,
byungchul, gourry, ying.huang, apopple, shuah
To test GUEST_MEMFD_FLAG_BIND_NODE - extend create_guest_memfd() to
accept a node.
Ignore the node argument if the BIND_NODE flag is not provided.
Signed-off-by: Gregory Price <gourry@gourry.net>
Assisted-by: Claude:claude-opus-5
---
.../testing/selftests/kvm/guest_memfd_test.c | 50 ++++++++++++++++---
1 file changed, 42 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 2233d871a38f4..1818e0fea5690 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -25,6 +25,31 @@
static size_t page_size;
+static int __create_guest_memfd_node(struct kvm_vm *vm, u64 size, u64 flags,
+ u32 node, u32 pad)
+{
+ struct kvm_create_guest_memfd guest_memfd = {
+ .size = size,
+ .flags = flags,
+ .node = node,
+ .pad = pad,
+ };
+
+ return __vm_ioctl(vm, KVM_CREATE_GUEST_MEMFD, &guest_memfd);
+}
+
+static int create_guest_memfd(struct kvm_vm *vm, u64 size, u64 flags, u32 node)
+{
+ int fd;
+
+ if (!(flags & GUEST_MEMFD_FLAG_BIND_NODE))
+ return vm_create_guest_memfd(vm, size, flags);
+
+ fd = __create_guest_memfd_node(vm, size, flags, node, 0);
+ TEST_ASSERT(fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_GUEST_MEMFD, fd));
+ return fd;
+}
+
static void test_file_read_write(int fd, size_t total_size)
{
char buf[64];
@@ -418,26 +443,35 @@ static void test_guest_memfd_flags(struct kvm_vm *vm)
}
}
-#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, __node, args...) \
+do { \
+ int fd = create_guest_memfd(__vm, __gmem_size, __flags, __node); \
+ \
+ test_##__test(args); \
+ close(fd); \
} while (0)
#define __gmem_test(__test, __vm, __flags, __gmem_size) \
- ____gmem_test(__test, __vm, __flags, __gmem_size, fd, __gmem_size)
+ ____gmem_test(__test, __vm, __flags, __gmem_size, 0, fd, __gmem_size)
#define gmem_test(__test, __vm, __flags) \
__gmem_test(__test, __vm, __flags, page_size * 4)
#define __gmem_test_vm(__test, __vm, __flags, __gmem_size) \
- ____gmem_test(__test, __vm, __flags, __gmem_size, __vm, fd, __gmem_size)
+ ____gmem_test(__test, __vm, __flags, __gmem_size, 0, \
+ __vm, fd, __gmem_size)
#define gmem_test_vm(__test, __vm, __flags) \
__gmem_test_vm(__test, __vm, __flags, page_size * 4)
+#define __gmem_test_node(__test, __vm, __flags, __gmem_size, __node) \
+ ____gmem_test(__test, __vm, \
+ (__flags) | GUEST_MEMFD_FLAG_BIND_NODE, \
+ __gmem_size, __node, fd, __gmem_size, __node)
+
+#define gmem_test_node(__test, __vm, __flags, __node) \
+ __gmem_test_node(__test, __vm, __flags, page_size * 4, __node)
+
static void __test_guest_memfd(struct kvm_vm *vm, u64 flags)
{
test_create_guest_memfd_multiple(vm);
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH 5/5] selftests: KVM: guest_memfd: test GUEST_MEMFD_FLAG_BIND_NODE
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
` (3 preceding siblings ...)
2026-09-02 19:46 ` [PATCH 4/5] selftests: KVM: guest_memfd: let the gmem_test() harness bind a node Gregory Price
@ 2026-09-02 19:46 ` Gregory Price
2026-09-09 22:41 ` [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Ackerley Tng
2026-09-10 11:30 ` David Hildenbrand (Arm)
6 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-02 19:46 UTC (permalink / raw)
To: linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, david, ziy, matthew.brost, joshua.hahnjy, rakie.kim,
byungchul, gourry, ying.huang, apopple, shuah
Add tests for an umapped guest_memfd mempolicy configured at
creation via GUEST_MEMFD_FLAG_BIND_NODE.
Handles:
- !CONFIG_NUMA (skips)
- single node system
- multi-node system (faults onto remote node)
- invalid arguments (non-zero pad, bad node, node w/o flag).
BIND_NODE is skipped in test_guest_memfd_flags() and testing later
because that loop asserts every advertised flag succeeds on its own,
but BIND_NODE is the only flag that depends on a second field.
Signed-off-by: Gregory Price <gourry@gourry.net>
Assisted-by: Claude:claude-opus-5
---
.../testing/selftests/kvm/guest_memfd_test.c | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c
index 1818e0fea5690..b333cb42fab29 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -196,6 +196,83 @@ static void test_numa_allocation(int fd, size_t total_size)
kvm_munmap(mem, total_size);
}
+static bool has_bind_node(struct kvm_vm *vm)
+{
+ return vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) &
+ GUEST_MEMFD_FLAG_BIND_NODE;
+}
+
+static void test_bind_node_invalid(struct kvm_vm *vm, u64 flags)
+{
+ int fd;
+
+ if (!has_bind_node(vm))
+ return;
+
+ fd = __create_guest_memfd_node(vm, page_size,
+ flags | GUEST_MEMFD_FLAG_BIND_NODE, 0, 1);
+ TEST_ASSERT(fd < 0 && errno == EINVAL,
+ "guest_memfd() with non-zero pad should fail with EINVAL");
+
+ fd = __create_guest_memfd_node(vm, page_size,
+ flags | GUEST_MEMFD_FLAG_BIND_NODE,
+ 1 << 20, 0);
+ TEST_ASSERT(fd < 0 && errno == EINVAL,
+ "guest_memfd() with out-of-range node should fail with EINVAL");
+
+ fd = __create_guest_memfd_node(vm, page_size, flags, 1, 0);
+ TEST_ASSERT(fd < 0 && errno == EINVAL,
+ "guest_memfd() with a node but no BIND_NODE flag should fail with EINVAL");
+}
+
+static void test_bind_node(int fd, size_t total_size, int node)
+{
+ const unsigned long other_mask = 1UL << (node ? 0 : 1);
+ const unsigned long maxnode = BITS_PER_TYPE(other_mask);
+ bool steer_away = is_multi_numa_node_system();
+ void *pages[4];
+ int status[4];
+ char *mem;
+ int i;
+
+ mem = kvm_mmap(total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd);
+ for (i = 0; i < 4; i++)
+ pages[i] = mem + page_size * i;
+
+ /*
+ * Bind on a different node if possible order to check whether faulting
+ * happens as desired. Without a second node use the local node and
+ * just get coverage of create/mmap/fault paths.
+ */
+ if (steer_away)
+ kvm_set_mempolicy(MPOL_BIND, &other_mask, maxnode);
+
+ /* Deliberately no mbind() on this mapping. */
+ memset(mem, 0xaa, total_size);
+
+ kvm_move_pages(0, 4, pages, NULL, status, 0);
+ for (i = 0; i < 4; i++)
+ TEST_ASSERT(status[i] == node,
+ "Expected page %d on node %d, got it on node %d",
+ i, node, status[i]);
+
+ /* Dropped memory should fault back onto the same node */
+ kvm_fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0,
+ total_size);
+ memset(mem, 0xaa, total_size);
+
+ kvm_move_pages(0, 4, pages, NULL, status, 0);
+ for (i = 0; i < 4; i++)
+ TEST_ASSERT(status[i] == node,
+ "Expected page %d back on node %d, got it on node %d",
+ i, node, status[i]);
+
+ if (steer_away)
+ kvm_set_mempolicy(MPOL_DEFAULT, NULL, 0);
+
+ kvm_munmap(mem, total_size);
+}
+
static void test_collapse(int fd, u64 flags)
{
const size_t pmd_size = get_trans_hugepagesz();
@@ -429,6 +506,10 @@ static void test_guest_memfd_flags(struct kvm_vm *vm)
int fd;
for (flag = BIT(0); flag; flag <<= 1) {
+ /* BIND_NODE depends on a valid node field, test separately */
+ if (flag == GUEST_MEMFD_FLAG_BIND_NODE)
+ continue;
+
fd = __vm_create_guest_memfd(vm, page_size, flag);
if (flag & valid_flags) {
TEST_ASSERT(fd >= 0,
@@ -476,6 +557,7 @@ static void __test_guest_memfd(struct kvm_vm *vm, u64 flags)
{
test_create_guest_memfd_multiple(vm);
test_create_guest_memfd_invalid_sizes(vm, flags);
+ test_bind_node_invalid(vm, flags);
gmem_test(file_read_write, vm, flags);
@@ -486,6 +568,8 @@ static void __test_guest_memfd(struct kvm_vm *vm, u64 flags)
gmem_test(mmap_supported, vm, flags);
gmem_test(fault_overflow, vm, flags);
gmem_test(numa_allocation, vm, flags);
+ if (has_bind_node(vm))
+ gmem_test_node(bind_node, vm, flags, 0);
__gmem_test(collapse, vm, flags, pmd_size);
} else {
gmem_test(fault_private, vm, flags);
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
` (4 preceding siblings ...)
2026-09-02 19:46 ` [PATCH 5/5] selftests: KVM: guest_memfd: test GUEST_MEMFD_FLAG_BIND_NODE Gregory Price
@ 2026-09-09 22:41 ` Ackerley Tng
2026-09-09 23:10 ` Gregory Price
2026-09-10 11:30 ` David Hildenbrand (Arm)
6 siblings, 1 reply; 17+ messages in thread
From: Ackerley Tng @ 2026-09-09 22:41 UTC (permalink / raw)
To: Gregory Price, linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, david, ziy, matthew.brost, joshua.hahnjy, rakie.kim,
byungchul, ying.huang, apopple, shuah
Gregory Price <gourry@gourry.net> writes:
> guest_memfd allocates its folios through a per-inode shared mempolicy.
>
> Today that policy can only be set after the fact, with mbind() on a host
> mmap of the fd. That requires the fd to be mappable, and it cannot reach
> folios that are only ever guest-faulted. Neither holds for a
> non-mappable (confidential) guest_memfd.
>
> Add GUEST_MEMFD_FLAG_BIND_NODE and a node field to struct
> kvm_create_guest_memfd. When set, KVM builds an MPOL_BIND policy for the
> requested node and installs it over the whole inode, so every folio is
> allocated there with no userspace mbind().
>
Instead of a custom API to ensure all guest_memfd allocations come from
a single node, how about these options?
1. Using cgroups/cpuset to constrain allocations (could be troublesome
if the guest memory is not preallocated, unless the vCPU threads are
running with the cpuset config)
2. Process-level NUMA policy
3. Why not request the guest_memfd to be mmap-able just to be able to
set a memory policy?
4. How about something like fbind() that takes an fd and offset range
instead of mbind(), which has a prerequisite on mmap()?
This doesn't exist yet, but I'm hoping to discuss this at LPC 2026:
5. What if you could pass an fd representing a mount to guest_memfd at
creation time, so to make all the allocations come from a single node
Step 1: Create a tmpfs mount, specify mpol for mount to MPOL_BIND
Step 2: Get some fd representing the tmpfs mount, hand that to
guest_memfd at creation time
Step 3: guest_memfd allocations will always come from that tmpfs
mount, and abide by that tmpfs mount's memory policy.
May I know more about the use case behind this new feature?
>
> [...snip...]
>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-09 22:41 ` [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Ackerley Tng
@ 2026-09-09 23:10 ` Gregory Price
2026-09-09 23:23 ` Ackerley Tng
2026-09-10 11:32 ` David Hildenbrand (Arm)
0 siblings, 2 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-09 23:10 UTC (permalink / raw)
To: Ackerley Tng
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, david, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
On Wed, Sep 09, 2026 at 03:41:35PM -0700, Ackerley Tng wrote:
> Gregory Price <gourry@gourry.net> writes:
>
> > guest_memfd allocates its folios through a per-inode shared mempolicy.
> >
> > Today that policy can only be set after the fact, with mbind() on a host
> > mmap of the fd. That requires the fd to be mappable, and it cannot reach
> > folios that are only ever guest-faulted. Neither holds for a
> > non-mappable (confidential) guest_memfd.
> >
> > Add GUEST_MEMFD_FLAG_BIND_NODE and a node field to struct
> > kvm_create_guest_memfd. When set, KVM builds an MPOL_BIND policy for the
> > requested node and installs it over the whole inode, so every folio is
> > allocated there with no userspace mbind().
> >
>
> Instead of a custom API to ensure all guest_memfd allocations come from
> a single node, how about these options?
>
> 1. Using cgroups/cpuset to constrain allocations (could be troublesome
> if the guest memory is not preallocated, unless the vCPU threads are
> running with the cpuset config)
>
> 2. Process-level NUMA policy
>
for 1 and 2:
the intent is to put the guest memory on the target node, not all system
memory for a given process. so the scope here is not the same.
in fact at that granularity, the desired node may not even have eligible
memory to host the task's memory.
> 3. Why not request the guest_memfd to be mmap-able just to be able to
> set a memory policy?
>
the eventual intent is to enable this for fully confidential,
host-unmapped guest, isolated to a particular memory device.
Requiring a mapping to get node-placement is quite defeating the point.
> 4. How about something like fbind() that takes an fd and offset range
> instead of mbind(), which has a prerequisite on mmap()?
>
This was a consideration - although it has other limitations and larger
complexities associated with it.
Where does the policy live for random fd's? (inode? address_space?)
How is a reclaimed inode's policy handled? (lost forever?)
figured i'd start by reducing the scope to the narrowest and clearest
use-case, but I did expect to have the fbind() conversation.
I'm open to it, but it seems like over-engineering.
If you look at tmpfs / shmem, you'll see there is the option for a
default filesystem-wide mempolicy that can be plumbed, but i'm not sure
there's a real usecase for per-file mempolicies that isn't literally
guest_memfd.
> This doesn't exist yet, but I'm hoping to discuss this at LPC 2026:
>
> 5. What if you could pass an fd representing a mount to guest_memfd at
> creation time, so to make all the allocations come from a single node
>
> Step 1: Create a tmpfs mount, specify mpol for mount to MPOL_BIND
> Step 2: Get some fd representing the tmpfs mount, hand that to
> guest_memfd at creation time
> Step 3: guest_memfd allocations will always come from that tmpfs
> mount, and abide by that tmpfs mount's memory policy.
>
This i think this is more feasible than something like fbind, but devil
is in the details.
I definitely think it's interesting and would love to chat at LPC!
> May I know more about the use case behind this new feature?
>
see above - confidential vm whose memory is quarantined to a particular
device, without placing that same burden on the host memory.
~Gregory
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-09 23:10 ` Gregory Price
@ 2026-09-09 23:23 ` Ackerley Tng
2026-09-09 23:41 ` Gregory Price
2026-09-10 11:32 ` David Hildenbrand (Arm)
1 sibling, 1 reply; 17+ messages in thread
From: Ackerley Tng @ 2026-09-09 23:23 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, david, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
Gregory Price <gourry@gourry.net> writes:
> On Wed, Sep 09, 2026 at 03:41:35PM -0700, Ackerley Tng wrote:
>> Gregory Price <gourry@gourry.net> writes:
>>
>> > guest_memfd allocates its folios through a per-inode shared mempolicy.
>> >
>> > Today that policy can only be set after the fact, with mbind() on a host
>> > mmap of the fd. That requires the fd to be mappable, and it cannot reach
>> > folios that are only ever guest-faulted. Neither holds for a
>> > non-mappable (confidential) guest_memfd.
>> >
>> > Add GUEST_MEMFD_FLAG_BIND_NODE and a node field to struct
>> > kvm_create_guest_memfd. When set, KVM builds an MPOL_BIND policy for the
>> > requested node and installs it over the whole inode, so every folio is
>> > allocated there with no userspace mbind().
>> >
>>
>> Instead of a custom API to ensure all guest_memfd allocations come from
>> a single node, how about these options?
>>
>> 1. Using cgroups/cpuset to constrain allocations (could be troublesome
>> if the guest memory is not preallocated, unless the vCPU threads are
>> running with the cpuset config)
>>
>> 2. Process-level NUMA policy
>>
>
> for 1 and 2:
>
> the intent is to put the guest memory on the target node, not all system
> memory for a given process. so the scope here is not the same.
>
> in fact at that granularity, the desired node may not even have eligible
> memory to host the task's memory.
>
We have a similar problem where we wanted only guest memory to be
charged a certain memcg. Currently guest_memfd memory is charge to
mm->owner, which is the thread's parent, so doing the fallocate() in a
thread wasn't good enough. The workaround was to have the fallocate()
done in a separate process (fork) and have the child process's memcg be
set to the desired memcg.
This workaround is kind of awkward, but could it technically work for
NUMA allocations?
>> 3. Why not request the guest_memfd to be mmap-able just to be able to
>> set a memory policy?
>>
>
> the eventual intent is to enable this for fully confidential,
> host-unmapped guest, isolated to a particular memory device.
>
> Requiring a mapping to get node-placement is quite defeating the point.
>
>> 4. How about something like fbind() that takes an fd and offset range
>> instead of mbind(), which has a prerequisite on mmap()?
>>
>
> This was a consideration - although it has other limitations and larger
> complexities associated with it.
>
> Where does the policy live for random fd's? (inode? address_space?)
>
Off the top of my head the policy would be saved at gi->policy, where
it's stored for mbind() now. fd+offset is just another way of
referencing the offsets to set the policy.
> How is a reclaimed inode's policy handled? (lost forever?)
>
> figured i'd start by reducing the scope to the narrowest and clearest
> use-case, but I did expect to have the fbind() conversation.
>
> I'm open to it, but it seems like over-engineering.
>
> If you look at tmpfs / shmem, you'll see there is the option for a
> default filesystem-wide mempolicy that can be plumbed, but i'm not sure
> there's a real usecase for per-file mempolicies that isn't literally
> guest_memfd.
>
I see, can't think of other use-cases for per-file mempolicies either.
>> This doesn't exist yet, but I'm hoping to discuss this at LPC 2026:
>>
>> 5. What if you could pass an fd representing a mount to guest_memfd at
>> creation time, so to make all the allocations come from a single node
>>
>> Step 1: Create a tmpfs mount, specify mpol for mount to MPOL_BIND
>> Step 2: Get some fd representing the tmpfs mount, hand that to
>> guest_memfd at creation time
>> Step 3: guest_memfd allocations will always come from that tmpfs
>> mount, and abide by that tmpfs mount's memory policy.
>>
>
> This i think this is more feasible than something like fbind, but devil
> is in the details.
>
> I definitely think it's interesting and would love to chat at LPC!
>
Great! :) Cya!
Along the above lines, for HugeTLBfs we'd do something similar, and the
mount's fd would also indicate the HugeTLB page size.
Relating to memcg above, this doesn't really help though, since tmpfs
doesn't offer a way to configure memcgs.
>> May I know more about the use case behind this new feature?
>>
>
> see above - confidential vm whose memory is quarantined to a particular
> device, without placing that same burden on the host memory.
>
Is lazy allocation a requirement for you? (as opposed to fallocate()-ing
before the guest touches pages)
> ~Gregory
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-09 23:23 ` Ackerley Tng
@ 2026-09-09 23:41 ` Gregory Price
0 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-09 23:41 UTC (permalink / raw)
To: Ackerley Tng
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, david, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
On Wed, Sep 09, 2026 at 04:23:07PM -0700, Ackerley Tng wrote:
> Gregory Price <gourry@gourry.net> writes:
>
> > in fact at that granularity, the desired node may not even have eligible
> > memory to host the task's memory.
> >
>
> We have a similar problem where we wanted only guest memory to be
> charged a certain memcg. Currently guest_memfd memory is charge to
> mm->owner, which is the thread's parent, so doing the fallocate() in a
> thread wasn't good enough. The workaround was to have the fallocate()
> done in a separate process (fork) and have the child process's memcg be
> set to the desired memcg.
>
> This workaround is kind of awkward, but could it technically work for
> NUMA allocations?
>
That is a very clunky work-around, I'm not sure it's good to formalize
that kind of design as an expected user behavior.
Whatever the case, the issue of ineligible memory isn't resolved by
this. A node may be 100% ZONE_MOVABLE, and later entirely unmapped -
literally no eligible memory for kernel allocations for the task itself.
> >
> > This was a consideration - although it has other limitations and larger
> > complexities associated with it.
> >
> > Where does the policy live for random fd's? (inode? address_space?)
> >
>
> Off the top of my head the policy would be saved at gi->policy, where
> it's stored for mbind() now. fd+offset is just another way of
> referencing the offsets to set the policy.
>
It makes sense for anon use cases like this, but for some random file
where fbind() means "put unmapped page cache on node X" the story is
less clear.
> > How is a reclaimed inode's policy handled? (lost forever?)
> >
> > figured i'd start by reducing the scope to the narrowest and clearest
> > use-case, but I did expect to have the fbind() conversation.
> >
> > I'm open to it, but it seems like over-engineering.
> >
> > If you look at tmpfs / shmem, you'll see there is the option for a
> > default filesystem-wide mempolicy that can be plumbed, but i'm not sure
> > there's a real usecase for per-file mempolicies that isn't literally
> > guest_memfd.
> >
>
> I see, can't think of other use-cases for per-file mempolicies either.
>
The only thing i've considered is something like GPU wanting a file
faulted directly onto its node - but that's a mapped region, and that's
handled by existing semantics.
> >> May I know more about the use case behind this new feature?
> >>
> >
> > see above - confidential vm whose memory is quarantined to a particular
> > device, without placing that same burden on the host memory.
> >
>
> Is lazy allocation a requirement for you? (as opposed to fallocate()-ing
> before the guest touches pages)
>
Yes, because eventually some kind of overcommit will be desired. May as
well build it in from the start.
Looking forward to LPC :]
~Gregory
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-09 23:10 ` Gregory Price
2026-09-09 23:23 ` Ackerley Tng
@ 2026-09-10 11:32 ` David Hildenbrand (Arm)
2026-09-10 13:39 ` Gregory Price
1 sibling, 1 reply; 17+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-10 11:32 UTC (permalink / raw)
To: Gregory Price, Ackerley Tng
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
On 9/10/26 01:10, Gregory Price wrote:
> On Wed, Sep 09, 2026 at 03:41:35PM -0700, Ackerley Tng wrote:
>> Gregory Price <gourry@gourry.net> writes:
>>
>>> guest_memfd allocates its folios through a per-inode shared mempolicy.
>>>
>>> Today that policy can only be set after the fact, with mbind() on a host
>>> mmap of the fd. That requires the fd to be mappable, and it cannot reach
>>> folios that are only ever guest-faulted. Neither holds for a
>>> non-mappable (confidential) guest_memfd.
>>>
>>> Add GUEST_MEMFD_FLAG_BIND_NODE and a node field to struct
>>> kvm_create_guest_memfd. When set, KVM builds an MPOL_BIND policy for the
>>> requested node and installs it over the whole inode, so every folio is
>>> allocated there with no userspace mbind().
>>>
>>
>> Instead of a custom API to ensure all guest_memfd allocations come from
>> a single node, how about these options?
>>
>> 1. Using cgroups/cpuset to constrain allocations (could be troublesome
>> if the guest memory is not preallocated, unless the vCPU threads are
>> running with the cpuset config)
>>
>> 2. Process-level NUMA policy
>>
>
> for 1 and 2:
>
> the intent is to put the guest memory on the target node, not all system
> memory for a given process. so the scope here is not the same.
>
> in fact at that granularity, the desired node may not even have eligible
> memory to host the task's memory.
>
>> 3. Why not request the guest_memfd to be mmap-able just to be able to
>> set a memory policy?
>>
>
> the eventual intent is to enable this for fully confidential,
> host-unmapped guest, isolated to a particular memory device.
>
> Requiring a mapping to get node-placement is quite defeating the point.
You only need a VMA, not actually mapped/faulted pages. So I don't immediately
see the problem?
--
Cheers,
David
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-10 11:32 ` David Hildenbrand (Arm)
@ 2026-09-10 13:39 ` Gregory Price
2026-09-10 14:00 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 17+ messages in thread
From: Gregory Price @ 2026-09-10 13:39 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Ackerley Tng, linux-mm, kvm, linux-kselftest, linux-kernel,
kernel-team, pbonzini, seanjc, akpm, ziy, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, ying.huang, apopple, shuah
On Thu, Sep 10, 2026 at 01:32:08PM +0200, David Hildenbrand (Arm) wrote:
> >> 3. Why not request the guest_memfd to be mmap-able just to be able to
> >> set a memory policy?
> >
> > the eventual intent is to enable this for fully confidential,
> > host-unmapped guest, isolated to a particular memory device.
> >
> > Requiring a mapping to get node-placement is quite defeating the point.
>
> You only need a VMA, not actually mapped/faulted pages. So I don't immediately
> see the problem?
>
There is no VMA here - only an inode (GMEM_I), which is where the
shared policy hangs off of.
So yeah, if there was a vma, that's i suppose the missing component
needed to hook up userland mempolicy to all of this - but I would have
thought creating a VMA for guest_memfd is hacky and confusing (since its
intent is to basically not have a VMA).
Is there a series I missed that was proposing this?
~Gregory
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-10 13:39 ` Gregory Price
@ 2026-09-10 14:00 ` David Hildenbrand (Arm)
0 siblings, 0 replies; 17+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-10 14:00 UTC (permalink / raw)
To: Gregory Price
Cc: Ackerley Tng, linux-mm, kvm, linux-kselftest, linux-kernel,
kernel-team, pbonzini, seanjc, akpm, ziy, matthew.brost,
joshua.hahnjy, rakie.kim, byungchul, ying.huang, apopple, shuah
On 9/10/26 15:39, Gregory Price wrote:
> On Thu, Sep 10, 2026 at 01:32:08PM +0200, David Hildenbrand (Arm) wrote:
>>>
>>> the eventual intent is to enable this for fully confidential,
>>> host-unmapped guest, isolated to a particular memory device.
>>>
>>> Requiring a mapping to get node-placement is quite defeating the point.
>>
>> You only need a VMA, not actually mapped/faulted pages. So I don't immediately
>> see the problem?
>>
>
> There is no VMA here - only an inode (GMEM_I), which is where the
> shared policy hangs off of.
>
> So yeah, if there was a vma, that's i suppose the missing component
> needed to hook up userland mempolicy to all of this - but I would have
> thought creating a VMA for guest_memfd is hacky and confusing (since its
> intent is to basically not have a VMA).
The VMA is irrelevant, you just don't want to fault in the pages. In fact, you'd
only need the VMA while setting the policy. (similar to shmem)
>
> Is there a series I missed that was proposing this?
I recall that we definitely discussed this in on of our meetings, but I don't
remember whether we decided to not fully support this case given that in-place
conversation is on the horizon.
I think you can open a private-only guest_memfd with GUEST_MEMFD_FLAG_MMAP, and
it will reject to fault-in any pages, but GUEST_MEMFD_FLAG_MMAP also changes the
way memory pages are obtained:
"When the KVM MMU performs a PFN lookup to service a guest fault and the backing
guest_memfd has the GUEST_MEMFD_FLAG_MMAP set, then the fault will always be
consumed from guest_memfd, regardless of whether it is a shared or a private fault".
For shared-only that makes perfect sense. For private-only, where shared memory
pages would come from a user VMA, this wouldn't really work.
But I don't know how your case would look like (where are shared pages? are
there any ever?)
--
Cheers,
David
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-02 19:46 [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Gregory Price
` (5 preceding siblings ...)
2026-09-09 22:41 ` [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node Ackerley Tng
@ 2026-09-10 11:30 ` David Hildenbrand (Arm)
2026-09-10 13:40 ` Gregory Price
6 siblings, 1 reply; 17+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-10 11:30 UTC (permalink / raw)
To: Gregory Price, linux-mm
Cc: kvm, linux-kselftest, linux-kernel, kernel-team, pbonzini, seanjc,
akpm, ziy, matthew.brost, joshua.hahnjy, rakie.kim, byungchul,
ying.huang, apopple, shuah
On 9/2/26 21:46, Gregory Price wrote:
> guest_memfd allocates its folios through a per-inode shared mempolicy.
>
> Today that policy can only be set after the fact, with mbind() on a host
> mmap of the fd. That requires the fd to be mappable, and it cannot reach
> folios that are only ever guest-faulted. Neither holds for a
> non-mappable (confidential) guest_memfd.
That looks like an investment into a mechanism we will soon consider legacy, so
I am not convinced this is the right approach?
--
Cheers,
David
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-10 11:30 ` David Hildenbrand (Arm)
@ 2026-09-10 13:40 ` Gregory Price
2026-09-10 14:05 ` David Hildenbrand (Arm)
0 siblings, 1 reply; 17+ messages in thread
From: Gregory Price @ 2026-09-10 13:40 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
On Thu, Sep 10, 2026 at 01:30:04PM +0200, David Hildenbrand (Arm) wrote:
> On 9/2/26 21:46, Gregory Price wrote:
> > guest_memfd allocates its folios through a per-inode shared mempolicy.
> >
> > Today that policy can only be set after the fact, with mbind() on a host
> > mmap of the fd. That requires the fd to be mappable, and it cannot reach
> > folios that are only ever guest-faulted. Neither holds for a
> > non-mappable (confidential) guest_memfd.
>
> That looks like an investment into a mechanism we will soon consider legacy, so
> I am not convinced this is the right approach?
>
Hm, I think i've missed recent work in this space / this context.
Any suggested reading here? Don't want to eat too much of your time.
~Gregory
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-10 13:40 ` Gregory Price
@ 2026-09-10 14:05 ` David Hildenbrand (Arm)
2026-09-10 14:46 ` Gregory Price
0 siblings, 1 reply; 17+ messages in thread
From: David Hildenbrand (Arm) @ 2026-09-10 14:05 UTC (permalink / raw)
To: Gregory Price
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
On 9/10/26 15:40, Gregory Price wrote:
> On Thu, Sep 10, 2026 at 01:30:04PM +0200, David Hildenbrand (Arm) wrote:
>> On 9/2/26 21:46, Gregory Price wrote:
>>> guest_memfd allocates its folios through a per-inode shared mempolicy.
>>>
>>> Today that policy can only be set after the fact, with mbind() on a host
>>> mmap of the fd. That requires the fd to be mappable, and it cannot reach
>>> folios that are only ever guest-faulted. Neither holds for a
>>> non-mappable (confidential) guest_memfd.
>>
>> That looks like an investment into a mechanism we will soon consider legacy, so
>> I am not convinced this is the right approach?
>>
>
> Hm, I think i've missed recent work in this space / this context.
> Any suggested reading here? Don't want to eat too much of your time.
So the big development happening right now is in-place conversion (having shared
and private pages managed in guest_memfd):
https://lore.kernel.org/r/20260830-gmem-inplace-conversion-v12-0-85e5fd25252a@google.com
The final series where we added mempolicy support should be:
https://lore.kernel.org/linux-mm/20250827175247.83322-2-shivankg@amd.com/
(I think you are aware of that, you commented on some)
See my other reply regarding the use of GUEST_MEMFD_FLAG_MMAP on private-only
guest_memfd.
--
Cheers,
David
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 0/5] KVM: guest_memfd: bind backing memory to a NUMA node
2026-09-10 14:05 ` David Hildenbrand (Arm)
@ 2026-09-10 14:46 ` Gregory Price
0 siblings, 0 replies; 17+ messages in thread
From: Gregory Price @ 2026-09-10 14:46 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: linux-mm, kvm, linux-kselftest, linux-kernel, kernel-team,
pbonzini, seanjc, akpm, ziy, matthew.brost, joshua.hahnjy,
rakie.kim, byungchul, ying.huang, apopple, shuah
On Thu, Sep 10, 2026 at 04:05:36PM +0200, David Hildenbrand (Arm) wrote:
> On 9/10/26 15:40, Gregory Price wrote:
> > On Thu, Sep 10, 2026 at 01:30:04PM +0200, David Hildenbrand (Arm) wrote:
> >> On 9/2/26 21:46, Gregory Price wrote:
> >>> guest_memfd allocates its folios through a per-inode shared mempolicy.
> >>>
> >>> Today that policy can only be set after the fact, with mbind() on a host
> >>> mmap of the fd. That requires the fd to be mappable, and it cannot reach
> >>> folios that are only ever guest-faulted. Neither holds for a
> >>> non-mappable (confidential) guest_memfd.
> >>
> >> That looks like an investment into a mechanism we will soon consider legacy, so
> >> I am not convinced this is the right approach?
> >>
> >
> > Hm, I think i've missed recent work in this space / this context.
> > Any suggested reading here? Don't want to eat too much of your time.
>
> So the big development happening right now is in-place conversion (having shared
> and private pages managed in guest_memfd):
>
> https://lore.kernel.org/r/20260830-gmem-inplace-conversion-v12-0-85e5fd25252a@google.com
>
>
> The final series where we added mempolicy support should be:
>
> https://lore.kernel.org/linux-mm/20250827175247.83322-2-shivankg@amd.com/
>
> (I think you are aware of that, you commented on some)
>
> See my other reply regarding the use of GUEST_MEMFD_FLAG_MMAP on private-only
> guest_memfd.
>
Ah yes, i do remember this series. In fact...
https://lore.kernel.org/linux-mm/aPD-dbl5KWNSHu5R@gourry-fedora-PF4VCD3F/
```
So this inode mempolicy in guest_memfd is really acting more as a the
filesystem-default mempolicy, which you want to survive even if userland
never maps the memory/unmaps the memory.
So the relationship is more like
guest_memfd -> creates fd/inode <- copies task mempolicy (if set)
vm: allocates memory via filemap_get_folio_mpol()
userland mmap(fd):
creates new inode<->vma mapping
vma->mpol = kvm_gmem_get_policy()
calls to set/get_policy/mbind go through kvm_gmem
```
And quick skim of in-place conversion (wow that's dense)
```
#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)
Set attributes for a range of offsets within a guest_memfd to
KVM_MEMORY_ATTRIBUTE_PRIVATE to limit the specified guest_memfd backed
memory range for guest use. Even if KVM_CAP_GUEST_MEMFD_MMAP is
supported, after a successful call to set KVM_MEMORY_ATTRIBUTE_PRIVATE,
the requested range will not be mappable into host userspace and will
only be mappable by the guest.
```
I guess the thought here would be something like...
1. create guest memfd
2. mmap(fd) -> prior to full guest setup
3. mbind(buf) -> shared policy hits the inode
4. munmap(buf) -> policy is NOT reaped
5. set ATTRIBUTE_PRIVATE
6. no longer user mappable
That doesn't seem unreasonable
~Gregory
^ permalink raw reply [flat|nested] 17+ messages in thread