Memory providers may need to check memslot flags and backing ranges before KVM installs a memslot. Add an optional prepare_memory_region() callback to the common provider operations Pass the byte offset, size and requested dirty-logging and read-only flags, and return any error before publishing the slot. Providers do not need access to KVM's memslot structure or internal flags. Existing providers that do not implement the callback keep their current behavior. Cc: Paolo Bonzini Cc: Sean Christopherson Cc: David Hildenbrand Cc: kvm@vger.kernel.org Cc: patches@lists.linux.dev Signed-off-by: Aneesh Kumar K.V (Arm) --- include/linux/guest_memfd.h | 8 ++++++++ virt/kvm/guest_memfd.c | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/linux/guest_memfd.h b/include/linux/guest_memfd.h index 3e2c0714ca33..17407504307b 100644 --- a/include/linux/guest_memfd.h +++ b/include/linux/guest_memfd.h @@ -14,6 +14,12 @@ struct mempolicy; * @attach: Attach to a resource file, perform filesystem-specific validation, * and return an opaque provider context. * @release: Release provider context and unpin any resources. + * @prepare_memory_region: Optional check of a memory range and preparation of + * access to its backing memory. Called after common + * file checks and before publishing the memslot. + * Offset and size are in bytes. The flags contain only + * KVM_MEM_LOG_DIRTY_PAGES and KVM_MEM_READONLY bits. + * Return 0 on success or a negative error code. * @alloc_folio: Allocate an uninserted folio for a given index and NUMA policy. * Must return a pointer to a struct folio on success, or an * ERR_PTR() error code on failure. Must never return NULL. @@ -44,6 +50,8 @@ struct mempolicy; struct guest_memfd_provider_operations { void *(*attach)(struct inode *gmem_inode, struct file *resource_file); void (*release)(void *provider); + int (*prepare_memory_region)(void *provider, u64 offset, u64 size, + u32 flags); struct folio *(*alloc_folio)(void *provider, pgoff_t index, struct mempolicy *mpol); void (*invalidate_folio)(void *provider, struct folio *folio); diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index db568ffaebd4..0aae8f9cef88 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -1374,10 +1374,11 @@ int kvm_gmem_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *slot unsigned int fd, uoff_t offset) { uoff_t size = slot->npages << PAGE_SHIFT; + struct gmem_inode *gi; struct gmem_file *f; struct inode *inode; struct file *file; - + int ret; BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset)); BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff)); @@ -1397,10 +1398,24 @@ int kvm_gmem_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *slot goto err; inode = file_inode(file); + gi = GMEM_I(inode); if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode)) goto err; + if (gi->provider_ops->prepare_memory_region) { + u32 flags = slot->flags & (KVM_MEM_LOG_DIRTY_PAGES | KVM_MEM_READONLY); + + filemap_invalidate_lock_shared(inode->i_mapping); + ret = gi->provider_ops->prepare_memory_region(gi->provider, offset, + size, flags); + filemap_invalidate_unlock_shared(inode->i_mapping); + if (ret) { + fput(file); + return ret; + } + } + /* * memslots of flag KVM_MEM_GUEST_MEMFD are immutable to change, so * kvm_gmem_bind() must occur on a new memslot. Because the memslot -- 2.43.0