Re: [PATCH v12 12/45] KVM: guest_memfd: Always fault from guest_memfd if in-place conversion is enabled
From: Ackerley Tng <hidden>
Date: 2026-09-09 14:36:23
Also in:
kvm, linux-coco, linux-doc, linux-kselftest, linux-mm, lkml
Yan Zhao [off-list ref] writes:
On Sun, Aug 30, 2026 at 05:25:13PM -0700, Ackerley Tng via B4 Relay wrote:quoted
From: Ackerley Tng <redacted> If a guest_memfd memslot is created but the guest_memfd does not have the GUEST_MEMFD_FLAG_MMAP set, KVM still fulfils guest faults by looking up the memslot's userspace_addr. Set KVM_MEMSLOT_GMEM_ONLY if in-place conversion is enabled so that the guest_memfd's memory will be used for both shared and private memory. With in-place conversion, guest_memfd will be the only backing memory for the memslot. No validation is performed to require userspace_addr to be a mapping from the associated guest_memfd because even after validation, userspace is free to remap something else at the provided userspace_addr. userspace_addr will still be used by functions like kvm_read_guest(), and if userspace_addr does not match up with the corresponding memory in the memslot's guest_memfd (whether userspace_addr points to the wrong offset or some non-guest_memfd memory, etc), that is a user error. Requiring both shared and private memory to come from the only associated guest_memfd simplifies invalidation in stage 2 page tables. On a PUNCH_HOLE operation on a guest_memfd, the invalidation is now guaranteed to be invalidating only memory mapped from the given guest_memfd. Suggested-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Ackerley Tng <redacted> --- Documentation/virt/kvm/api.rst | 22 ++++++++++++++-------- virt/kvm/guest_memfd.c | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-)diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 90a29424c54c8..668886f50024d 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst@@ -6381,10 +6381,16 @@ mapping for userspace_addr is not required to be valid/populated at the time of KVM_SET_USER_MEMORY_REGION2, e.g. shared memory can be lazily mapped/allocated on-demand. -When mapping a gfn into the guest, KVM selects shared vs. private, i.e consumes -userspace_addr vs. guest_memfd, based on the state in guest_memfd, which is the -sole authority on private vs. shared memory. See :ref:`KVM_CREATE_GUEST_MEMFD` -to find out more about the creation-time shared/private status. +When mapping a gfn into the guest, guest faults are always serviced from +guest_memfd regardless of whether memory is shared or private. KVM determines +shared vs. private based on the state in guest_memfd, which is the sole +authority on private vs. shared memory. See :ref:`KVM_CREATE_GUEST_MEMFD` to +find out more about the creation-time shared/private status. + +userspace_addr is expected to be the mmap()-ed address corresponding to the +right offset within the guest_memfd. Any mismatch between userspace_addr and +guest_memfd is not validated and is a user error. userspace_addr is only used +for host-side guest accesses such as kvm_read_guest(). If in-place conversion is disabled, KVM selects shared vs. private, i.e consumes userspace_addr vs. guest_memfd, based on the gfn's KVM_MEMORY_ATTRIBUTE_PRIVATE@@ -6490,10 +6496,10 @@ specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags: page tables. Private memory cannot. ============================ ================================================ -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. +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 +private fault (unless in-place conversion is disabled and the backing +guest_memfd does not have the GUEST_MEMFD_FLAG_MMAP flag set). See KVM_SET_USER_MEMORY_REGION2 for additional details.diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 0afe1468d2d9d..e41802944756b 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c@@ -746,7 +746,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, */ WRITE_ONCE(slot->gmem.file, file); slot->gmem.pgoff = start; - if (kvm_gmem_supports_mmap(inode)) + if (gmem_in_place_conversion || kvm_gmem_supports_mmap(inode)) slot->flags |= KVM_MEMSLOT_GMEM_ONLY;I like this change, which actually enforces in-place conversion -- when gmem_in_place_conversion is true, if userspace sets slot->userspace_addr to a different backend, the host and guest will no longer be able to access the same backend. However, since gmem_in_place_conversion is globally and statically specified, it essentially disables the coexistence of VMs using out-of-place conversions when gmem_in_place_conversion is true. Previously, VMs using out-of-place conversions could still boot successfully as long as they switched to using per-gmem memory attributes. Is this change intended?
Yes, this is intended. gmem_in_place_conversion is intended to be global for the entire host.
On a separate note, is kvm_gmem_supports_mmap() still a necessary requirement for setting KVM_MEMSLOT_GMEM_ONLY? When gmem_in_place_conversion is false, if userspace mmap()s the gmem but sets slot->userspace_addr to a different backend, should the KVM_MEMSLOT_GMEM_ONLY flag still be set?
Before this patch, requesting an MMAP-able gmem will set KVM_MEMSLOT_GMEM_ONLY, so if we change that behavior, we'd be breaking the existing contract. If userspace requests an MMAP-able gmem and then sets up slot->userspace_addr to something else, I think that should be considered user error? MMAP-able => use gmem for all faults has been a thing since before this patch. Just like how there's no good way to check and maintain the validity of the mapped memory behind slot->userspace_addr in relation to the provided guest_memfd, the mapping can't be used to determine whether to set KVM_MEMSLOT_GMEM_ONLY either.
quoted
xa_store_range(&f->bindings, start, end - 1, slot, GFP_KERNEL); -- 2.55.0.897.gb25b4bd76c-goog