include/linux/pagemap.h | 39 ++++++++++++++++++ mm/filemap.c | 30 +++++++++++--- mm/mempolicy.c | 6 +++ virt/kvm/guest_memfd.c | 87 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 151 insertions(+), 11 deletions(-)
KVM's guest-memfd memory backend currently lacks support for NUMA policy
enforcement, causing guest memory allocations to be distributed arbitrarily
across host NUMA nodes regardless of the policy specified by the VMM. This
occurs because conventional userspace NUMA control mechanisms like mbind()
are ineffective with guest-memfd, as the memory isn't directly mapped to
userspace when allocations occur.
This patch-series adds NUMA binding capabilities to guest_memfd backend
KVM guests. It has evolved through several approaches based on community
feedback:
- v1,v2: Extended the KVM_CREATE_GUEST_MEMFD IOCTL to pass mempolicy.
- v3: Introduced fbind() syscall for VMM memory-placement configuration.
- v4,v5: Current approach using shared_policy support and vm_ops (based on
suggestions from David[1] and guest_memfd biweekly upstream call[2]).
For SEV-SNP guests, which use the guest-memfd memory backend, NUMA-aware
memory placement is essential for optimal performance, particularly for
memory-intensive workloads.
This series implements proper NUMA policy support for guest-memfd by:
1. Adding mempolicy-aware allocation APIs to the filemap layer.
2. Implementing get/set_policy vm_ops in the guest_memfd to support the
shared policy.
With these changes, VMMs can now control guest memory placement by
specifying:
- Policy modes: default, bind, interleave, or preferred
- Host NUMA nodes: List of target nodes for memory allocation
This series builds on the existing guest-memfd support in KVM and provides
a clean integration path for NUMA-aware memory management in confidential
computing environments. The work is primarily focused on supporting SEV-SNP
requirements, though the benefits extend to any VMM using the guest-memfd
backend that needs control over guest memory placement.
== Example usage with QEMU (requires patched QEMU from [3]) ==
Snippet of the QEMU changes[3] needed to support this feature:
/* Create and map guest-memfd region */
new_block->guest_memfd = kvm_create_guest_memfd(
new_block->max_length, 0, errp);
...
void *ptr_memfd = mmap(NULL, new_block->max_length,
PROT_READ | PROT_WRITE, MAP_SHARED,
new_block->guest_memfd, 0);
...
/* Apply NUMA policy */
int ret = mbind(ptr_memfd, new_block->max_length,
backend->policy, backend->host_nodes,
maxnode+1, 0);
...
QEMU Command to run SEV-SNP guest with interleaved memory across
nodes 0 and 1 of the host:
$ qemu-system-x86_64 \
-enable-kvm \
...
-machine memory-encryption=sev0,vmport=off \
-object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1 \
-numa node,nodeid=0,memdev=ram0,cpus=0-15 \
-object memory-backend-memfd,id=ram0,host-nodes=0-1,policy=interleave,size=1024M,share=true,prealloc=false
== Experiment and Analysis ==
SEV-SNP enabled host, 6.14.0-rc1, AMD Zen 3, 2 socket 2 NUMA node system
NUMA for Policy Guest Node 0: policy=interleave, host-node=0-1
Test: Allocate and touch 50GB inside guest on node=0.
Generic Kernel (without NUMA supported guest-memfd):
Node 0 Node 1 Total
Before running Test:
MemUsed 9981.60 3312.00 13293.60
After running Test:
MemUsed 61451.72 3201.62 64653.34
Arbitrary allocations: all ~50GB allocated on node 0.
With NUMA supported guest-memfd:
Node 0 Node 1 Total
Before running Test:
MemUsed 5003.88 3963.07 8966.94
After running Test:
MemUsed 30607.55 29670.00 60277.55
Balanced memory distribution: Equal increase (~25GB) on both nodes.
== Conclusion ==
Adding the NUMA-aware memory management to guest_memfd will make a lot of
sense. Improving performance of memory-intensive and locality-sensitive
workloads with fine-grained control over guest memory allocations, as
pointed out in the analysis.
[1] https://lore.kernel.org/linux-mm/6fbef654-36e2-4be5-906e-2a648a845278@redhat.com
[2] https://lore.kernel.org/linux-mm/82c53460-a550-4236-a65a-78f292814edb@redhat.com
[3] https://github.com/shivankgarg98/qemu/tree/guest_memfd_mbind_NUMA
== Earlier postings and changelogs ==
v5 (current):
- Fix documentation and style issues.
- Use EXPORT_SYMBOL_GPL
- Split preparatory change in separate patch
v4:
- https://lore.kernel.org/linux-mm/20250210063227.41125-1-shivankg@amd.com
- Dropped fbind() approach in favor of shared policy support.
v3:
- https://lore.kernel.org/linux-mm/20241105164549.154700-1-shivankg@amd.com
- Introduce fbind() syscall and drop the IOCTL-based approach.
v2:
- https://lore.kernel.org/linux-mm/20240919094438.10987-1-shivankg@amd.com
- Add fixes suggested by Matthew Wilcox.
v1:
- https://lore.kernel.org/linux-mm/20240916165743.201087-1-shivankg@amd.com
- Proposed IOCTL based approach to pass NUMA mempolicy.
Shivank Garg (3):
mm/mempolicy: export memory policy symbols
KVM: guest_memfd: Pass file pointer instead of inode pointer
KVM: guest_memfd: Enforce NUMA mempolicy using shared policy
Shivansh Dhiman (1):
mm/filemap: add mempolicy support to the filemap layer
include/linux/pagemap.h | 39 ++++++++++++++++++
mm/filemap.c | 30 +++++++++++---
mm/mempolicy.c | 6 +++
virt/kvm/guest_memfd.c | 87 ++++++++++++++++++++++++++++++++++++++---
4 files changed, 151 insertions(+), 11 deletions(-)
--
2.34.1
On 2/19/2025 3:45 PM, Shivank Garg wrote: > KVM's guest-memfd memory backend currently lacks support for NUMA policy > enforcement, causing guest memory allocations to be distributed arbitrarily > across host NUMA nodes regardless of the policy specified by the VMM. This > occurs because conventional userspace NUMA control mechanisms like mbind() > are ineffective with guest-memfd, as the memory isn't directly mapped to > userspace when allocations occur. > > This patch-series adds NUMA binding capabilities to guest_memfd backend > KVM guests. It has evolved through several approaches based on community > feedback: > - v1,v2: Extended the KVM_CREATE_GUEST_MEMFD IOCTL to pass mempolicy. > - v3: Introduced fbind() syscall for VMM memory-placement configuration. > - v4,v5: Current approach using shared_policy support and vm_ops (based on > suggestions from David[1] and guest_memfd biweekly upstream call[2]). > <--snip> Hi All, This patch-series was discussed during the bi-weekly guest_memfd upstream call on 2025-02-20 [1]. Here are my notes from the discussion: The current design using mmap and shared_policy support with vm_ops appears good and aligns well with how shared memory handles NUMA policy. This makes perfect sense with upcoming changes from Fuad [2]. Integration with Fuad's work should be straightforward as my work primarily involves set_policy and get_policy callbacks in vm_ops. Additionally, this approach helps us avoid any fpolicy/fbind()[3] complexity. David mentioned documenting the behavior of setting memory policy after memory has already been allocated. Specifically, the policy change will only affect future allocations and will not migrate existing memory. This matches mbind(2)'s default behavior which affects only new allocations (unless overridden with MPOL_MF_MOVE/MPOL_MF_MOVE_ALL flags). In the future, we may explore supporting MPOL_MF_MOVE for guest_memfd, but for now, this behavior is sufficient and should be clearly documented. Before sending the non-RFC version of the patch-series, I will: - Document and clarify the memory allocation behavior after policy changes - Write kselftests to validate NUMA policy enforcement, including edge cases like changing policies after memory allocation I aim to send the updated patch-series soon. If there are any further suggestions or concerns, please let me know. [1] https://lore.kernel.org/linux-mm/40290a46-bcf4-4ef6-ae13-109e18ad0dfd@redhat.com [2] https://lore.kernel.org/linux-mm/20250218172500.807733-1-tabba@google.com [3] https://lore.kernel.org/linux-mm/20241105164549.154700-1-shivankg@amd.com Thanks, Shivank
© 2016 - 2025 Red Hat, Inc.