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


Reply via email to