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().
1-2 mm/mempolicy prep. mempolicy_create() builds a validated,
cpuset-contextualised policy without installing it into the
calling task. mpol_set_shared_policy_range() installs one over a
pgoff range with no VMA.
3 The KVM flag.
4-5 Selftest harness support, and the test.
Why a single node and not a full mempolicy?
The fd is the unit of guest NUMA topology. A multi-node guest is one
guest_memfd per guest node - or one range per node, since
kvm_gmem_bind() is offset-based and mpol_set_shared_policy_range() is
already range-capable - each bound to a host node, with the guest
placing memory on top. This is the shape QEMU already builds with one
memory-backend per guest node.
Interleaving a single fd across host nodes would model a guest node
whose pages are scattered underneath it. That defeats every placement
decision the guest makes: guest NUMA balancing, tiering and weighted
interleave would all be reasoning about a topology that doesn't exist.
This narrow implementation enables the only clear use case.
User-visible behaviour:
mempolicy_create() constrains the request to the task's cpuset.
A node outside mems_allowed fails the ioctl with -EINVAL - the same
constraint mbind() carries. A task cannot grant a guest_memfd access
to a node it cannot reach itself.
Nothing rebinds an inode's shared policy on a later cpuset change:
mpol_rebind_task() walks tsk->mempolicy and mpol_rebind_mm() walks
vma->vm_policy, and neither reaches a struct shared_policy. The bind
is fixed for the life of the fd. This matches shmem's implementation.
Testing
guest_memfd_test under virtme-ng, nested KVM:
- 2-node guest, test pinned to the CPUs of the node it is not binding
to, with the task mempolicy aimed at that other node. Pages land on
the bound node, so the placement cannot be explained by the fault
being local. Stripping the flag from the harness puts them on the
other node, confirming the check has teeth.
- CONFIG_NUMA=n: the flag is not advertised and the tests skip.
- Single node: create/mmap/fault coverage, no placement claim.
Gregory Price (5):
mm/mempolicy: add mempolicy_create()
mm/mempolicy: add mpol_set_shared_policy_range()
KVM: guest_memfd: bind backing memory to a NUMA node at creation
selftests: KVM: guest_memfd: let the gmem_test() harness bind a node
selftests: KVM: guest_memfd: test GUEST_MEMFD_FLAG_BIND_NODE
include/linux/kvm_host.h | 3 +
include/linux/mempolicy.h | 5 +
include/uapi/linux/kvm.h | 5 +-
mm/mempolicy.c | 75 +++++++++-
.../testing/selftests/kvm/guest_memfd_test.c | 134 ++++++++++++++++--
virt/kvm/guest_memfd.c | 44 +++++-
6 files changed, 248 insertions(+), 18 deletions(-)
---
base-commit: da6c37ed8beb273e3308e42d4bca3ce11b4432fa
--
2.53.0-Meta