]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: guest_memfd: Use guest mem inodes instead of anonymous inodes
authorAckerley Tng <ackerleytng@google.com>
Thu, 16 Oct 2025 17:28:44 +0000 (10:28 -0700)
committerSean Christopherson <seanjc@google.com>
Mon, 20 Oct 2025 13:30:40 +0000 (06:30 -0700)
guest_memfd's inode represents memory the guest_memfd is
providing. guest_memfd's file represents a struct kvm's view of that
memory.

Using a custom inode allows customization of the inode teardown
process via callbacks. For example, ->evict_inode() allows
customization of the truncation process on file close, and
->destroy_inode() and ->free_inode() allow customization of the inode
freeing process.

Customizing the truncation process allows flexibility in management of
guest_memfd memory and customization of the inode freeing process
allows proper cleanup of memory metadata stored on the inode.

Memory metadata is more appropriately stored on the inode (as opposed
to the file), since the metadata is for the memory and is not unique
to a specific binding and struct kvm.

Acked-by: David Hildenbrand <david@redhat.com>
Co-developed-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Shivank Garg <shivankg@amd.com>
Tested-by: Ashish Kalra <ashish.kalra@amd.com>
[sean: drop helpers, open code logic in __kvm_gmem_create()]
Link: https://lore.kernel.org/r/20251016172853.52451-4-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
include/uapi/linux/magic.h
virt/kvm/guest_memfd.c
virt/kvm/kvm_main.c
virt/kvm/kvm_mm.h

index bb575f3ab45e59aff283602f8e7c8af13e8bcc6a..638ca21b7a9095d51787b6561471c15646e68605 100644 (file)
 #define DEVMEM_MAGIC           0x454d444d      /* "DMEM" */
 #define SECRETMEM_MAGIC                0x5345434d      /* "SECM" */
 #define PID_FS_MAGIC           0x50494446      /* "PIDF" */
+#define GUEST_MEMFD_MAGIC      0x474d454d      /* "GMEM" */
 
 #endif /* __LINUX_MAGIC_H__ */
index 5cce20ff418dae75cf8882b44095dda98181aac2..ce04fc85e6312964b7dee6a9490f61962870544b 100644 (file)
@@ -1,12 +1,16 @@
 // SPDX-License-Identifier: GPL-2.0
+#include <linux/anon_inodes.h>
 #include <linux/backing-dev.h>
 #include <linux/falloc.h>
+#include <linux/fs.h>
 #include <linux/kvm_host.h>
+#include <linux/pseudo_fs.h>
 #include <linux/pagemap.h>
-#include <linux/anon_inodes.h>
 
 #include "kvm_mm.h"
 
+static struct vfsmount *kvm_gmem_mnt;
+
 /*
  * A guest_memfd instance can be associated multiple VMs, each with its own
  * "view" of the underlying physical memory.
@@ -424,11 +428,6 @@ static struct file_operations kvm_gmem_fops = {
        .fallocate      = kvm_gmem_fallocate,
 };
 
-void kvm_gmem_init(struct module *module)
-{
-       kvm_gmem_fops.owner = module;
-}
-
 static int kvm_gmem_migrate_folio(struct address_space *mapping,
                                  struct folio *dst, struct folio *src,
                                  enum migrate_mode mode)
@@ -500,7 +499,7 @@ bool __weak kvm_arch_supports_gmem_init_shared(struct kvm *kvm)
 
 static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
 {
-       const char *anon_name = "[kvm-gmem]";
+       static const char *name = "[kvm-gmem]";
        struct gmem_file *f;
        struct inode *inode;
        struct file *file;
@@ -516,16 +515,17 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
                goto err_fd;
        }
 
-       file = anon_inode_create_getfile(anon_name, &kvm_gmem_fops, f, O_RDWR, NULL);
-       if (IS_ERR(file)) {
-               err = PTR_ERR(file);
+       /* __fput() will take care of fops_put(). */
+       if (!fops_get(&kvm_gmem_fops)) {
+               err = -ENOENT;
                goto err_gmem;
        }
 
-       file->f_flags |= O_LARGEFILE;
-
-       inode = file->f_inode;
-       WARN_ON(file->f_mapping != inode->i_mapping);
+       inode = anon_inode_make_secure_inode(kvm_gmem_mnt->mnt_sb, name, NULL);
+       if (IS_ERR(inode)) {
+               err = PTR_ERR(inode);
+               goto err_fops;
+       }
 
        inode->i_private = (void *)(unsigned long)flags;
        inode->i_op = &kvm_gmem_iops;
@@ -537,6 +537,15 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
        /* Unmovable mappings are supposed to be marked unevictable as well. */
        WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
 
+       file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, &kvm_gmem_fops);
+       if (IS_ERR(file)) {
+               err = PTR_ERR(file);
+               goto err_inode;
+       }
+
+       file->f_flags |= O_LARGEFILE;
+       file->private_data = f;
+
        kvm_get_kvm(kvm);
        f->kvm = kvm;
        xa_init(&f->bindings);
@@ -545,6 +554,10 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
        fd_install(fd, file);
        return fd;
 
+err_inode:
+       iput(inode);
+err_fops:
+       fops_put(&kvm_gmem_fops);
 err_gmem:
        kfree(f);
 err_fd:
@@ -816,3 +829,44 @@ put_folio_and_exit:
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_gmem_populate);
 #endif
+
+static int kvm_gmem_init_fs_context(struct fs_context *fc)
+{
+       if (!init_pseudo(fc, GUEST_MEMFD_MAGIC))
+               return -ENOMEM;
+
+       fc->s_iflags |= SB_I_NOEXEC;
+       fc->s_iflags |= SB_I_NODEV;
+
+       return 0;
+}
+
+static struct file_system_type kvm_gmem_fs = {
+       .name            = "guest_memfd",
+       .init_fs_context = kvm_gmem_init_fs_context,
+       .kill_sb         = kill_anon_super,
+};
+
+static int kvm_gmem_init_mount(void)
+{
+       kvm_gmem_mnt = kern_mount(&kvm_gmem_fs);
+
+       if (IS_ERR(kvm_gmem_mnt))
+               return PTR_ERR(kvm_gmem_mnt);
+
+       kvm_gmem_mnt->mnt_flags |= MNT_NOEXEC;
+       return 0;
+}
+
+int kvm_gmem_init(struct module *module)
+{
+       kvm_gmem_fops.owner = module;
+
+       return kvm_gmem_init_mount();
+}
+
+void kvm_gmem_exit(void)
+{
+       kern_unmount(kvm_gmem_mnt);
+       kvm_gmem_mnt = NULL;
+}
index b7a0ae2a7b205c014cd9ddac7194ab93aecfad5a..4845e5739436a9cb091104f969134b7d60944e53 100644 (file)
@@ -6517,7 +6517,9 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
        if (WARN_ON_ONCE(r))
                goto err_vfio;
 
-       kvm_gmem_init(module);
+       r = kvm_gmem_init(module);
+       if (r)
+               goto err_gmem;
 
        r = kvm_init_virtualization();
        if (r)
@@ -6538,6 +6540,8 @@ int kvm_init(unsigned vcpu_size, unsigned vcpu_align, struct module *module)
 err_register:
        kvm_uninit_virtualization();
 err_virt:
+       kvm_gmem_exit();
+err_gmem:
        kvm_vfio_ops_exit();
 err_vfio:
        kvm_async_pf_deinit();
@@ -6569,6 +6573,7 @@ void kvm_exit(void)
        for_each_possible_cpu(cpu)
                free_cpumask_var(per_cpu(cpu_kick_mask, cpu));
        kmem_cache_destroy(kvm_vcpu_cache);
+       kvm_gmem_exit();
        kvm_vfio_ops_exit();
        kvm_async_pf_deinit();
        kvm_irqfd_exit();
index 31defb08ccbab431bd8858ebee9c744e5d2cf77e..9fcc5d5b7f8d00f94e63fa89a587dbab0fed9ebb 100644 (file)
@@ -68,17 +68,18 @@ static inline void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm,
 #endif /* HAVE_KVM_PFNCACHE */
 
 #ifdef CONFIG_KVM_GUEST_MEMFD
-void kvm_gmem_init(struct module *module);
+int kvm_gmem_init(struct module *module);
+void kvm_gmem_exit(void);
 int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args);
 int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
                  unsigned int fd, loff_t offset);
 void kvm_gmem_unbind(struct kvm_memory_slot *slot);
 #else
-static inline void kvm_gmem_init(struct module *module)
+static inline int kvm_gmem_init(struct module *module)
 {
-
+       return 0;
 }
-
+static inline void kvm_gmem_exit(void) {};
 static inline int kvm_gmem_bind(struct kvm *kvm,
                                         struct kvm_memory_slot *slot,
                                         unsigned int fd, loff_t offset)