From: Ackerley Tng <ackerleytng@google.com>
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.
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>
---
include/uapi/linux/magic.h | 1 +
virt/kvm/guest_memfd.c | 134 +++++++++++++++++++++++++++++++------
virt/kvm/kvm_main.c | 7 +-
virt/kvm/kvm_mm.h | 10 ++-
4 files changed, 127 insertions(+), 25 deletions(-)
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index bb575f3ab45e..638ca21b7a90 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -103,5 +103,6 @@
#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__ */
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index d01bd7a2c2bd..dabcc2317291 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -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;
+
struct kvm_gmem {
struct kvm *kvm;
struct xarray bindings;
@@ -388,9 +392,51 @@ static struct file_operations kvm_gmem_fops = {
.fallocate = kvm_gmem_fallocate,
};
-void kvm_gmem_init(struct module *module)
+static const struct super_operations kvm_gmem_super_operations = {
+ .statfs = simple_statfs,
+};
+
+static int kvm_gmem_init_fs_context(struct fs_context *fc)
+{
+ struct pseudo_fs_context *ctx;
+
+ if (!init_pseudo(fc, GUEST_MEMFD_MAGIC))
+ return -ENOMEM;
+
+ ctx = fc->fs_private;
+ ctx->ops = &kvm_gmem_super_operations;
+
+ return 0;
+}
+
+static struct file_system_type kvm_gmem_fs = {
+ .name = "kvm_guest_memory",
+ .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;
}
static int kvm_gmem_migrate_folio(struct address_space *mapping,
@@ -472,11 +518,71 @@ static const struct inode_operations kvm_gmem_iops = {
.setattr = kvm_gmem_setattr,
};
+static struct inode *kvm_gmem_inode_make_secure_inode(const char *name,
+ loff_t size, u64 flags)
+{
+ struct inode *inode;
+
+ inode = anon_inode_make_secure_inode(kvm_gmem_mnt->mnt_sb, name, NULL);
+ if (IS_ERR(inode))
+ return inode;
+
+ inode->i_private = (void *)(unsigned long)flags;
+ inode->i_op = &kvm_gmem_iops;
+ inode->i_mapping->a_ops = &kvm_gmem_aops;
+ inode->i_mode |= S_IFREG;
+ inode->i_size = size;
+ mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
+ mapping_set_inaccessible(inode->i_mapping);
+ /* Unmovable mappings are supposed to be marked unevictable as well. */
+ WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
+
+ return inode;
+}
+
+static struct file *kvm_gmem_inode_create_getfile(void *priv, loff_t size,
+ u64 flags)
+{
+ static const char *name = "[kvm-gmem]";
+ struct inode *inode;
+ struct file *file;
+ int err;
+
+ err = -ENOENT;
+ if (!try_module_get(kvm_gmem_fops.owner))
+ goto err;
+
+ inode = kvm_gmem_inode_make_secure_inode(name, size, flags);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ goto err_put_module;
+ }
+
+ file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR,
+ &kvm_gmem_fops);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_put_inode;
+ }
+
+ file->f_flags |= O_LARGEFILE;
+ file->private_data = priv;
+
+out:
+ return file;
+
+err_put_inode:
+ iput(inode);
+err_put_module:
+ module_put(kvm_gmem_fops.owner);
+err:
+ file = ERR_PTR(err);
+ goto out;
+}
+
static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags)
{
- const char *anon_name = "[kvm-gmem]";
struct kvm_gmem *gmem;
- struct inode *inode;
struct file *file;
int fd, err;
@@ -490,32 +596,16 @@ 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, gmem,
- O_RDWR, NULL);
+ file = kvm_gmem_inode_create_getfile(gmem, size, flags);
if (IS_ERR(file)) {
err = PTR_ERR(file);
goto err_gmem;
}
- file->f_flags |= O_LARGEFILE;
-
- inode = file->f_inode;
- WARN_ON(file->f_mapping != inode->i_mapping);
-
- inode->i_private = (void *)(unsigned long)flags;
- inode->i_op = &kvm_gmem_iops;
- inode->i_mapping->a_ops = &kvm_gmem_aops;
- inode->i_mode |= S_IFREG;
- inode->i_size = size;
- mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
- mapping_set_inaccessible(inode->i_mapping);
- /* Unmovable mappings are supposed to be marked unevictable as well. */
- WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping));
-
kvm_get_kvm(kvm);
gmem->kvm = kvm;
xa_init(&gmem->bindings);
- list_add(&gmem->entry, &inode->i_mapping->i_private_list);
+ list_add(&gmem->entry, &file_inode(file)->i_mapping->i_private_list);
fd_install(fd, file);
return fd;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index f1ac872e01e9..9ccdedc9460a 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -6486,7 +6486,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)
@@ -6507,6 +6509,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();
@@ -6538,6 +6542,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();
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
index ec311c0d6718..089f87ed00dc 100644
--- a/virt/kvm/kvm_mm.h
+++ b/virt/kvm/kvm_mm.h
@@ -68,17 +68,23 @@ static inline void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm,
#endif /* HAVE_KVM_PFNCACHE */
#ifdef CONFIG_KVM_GMEM
-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 void kvm_gmem_init(struct module *module)
+
static inline int kvm_gmem_bind(struct kvm *kvm,
struct kvm_memory_slot *slot,
unsigned int fd, loff_t offset)
--
2.43.0
On 13.07.25 19:43, Shivank Garg wrote: > From: Ackerley Tng <ackerleytng@google.com> > > 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. > > 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> > --- [...] > > #include "kvm_mm.h" > > +static struct vfsmount *kvm_gmem_mnt; > + > struct kvm_gmem { > struct kvm *kvm; > struct xarray bindings; > @@ -388,9 +392,51 @@ static struct file_operations kvm_gmem_fops = { > .fallocate = kvm_gmem_fallocate, > }; > > -void kvm_gmem_init(struct module *module) > +static const struct super_operations kvm_gmem_super_operations = { > + .statfs = simple_statfs, > +}; > + > +static int kvm_gmem_init_fs_context(struct fs_context *fc) > +{ > + struct pseudo_fs_context *ctx; > + > + if (!init_pseudo(fc, GUEST_MEMFD_MAGIC)) > + return -ENOMEM; > + > + ctx = fc->fs_private; > + ctx->ops = &kvm_gmem_super_operations; Curious, why is that required? (secretmem doesn't have it, so I wonder) > + > + return 0; > +} > + > +static struct file_system_type kvm_gmem_fs = { > + .name = "kvm_guest_memory", It's GUEST_MEMFD_MAGIC but here "kvm_guest_memory". For secretmem it's SECRETMEM_MAGIC vs. "secretmem". So naturally, I wonder if that is to be made consistent :) > + .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; > } > > static int kvm_gmem_migrate_folio(struct address_space *mapping, > @@ -472,11 +518,71 @@ static const struct inode_operations kvm_gmem_iops = { > .setattr = kvm_gmem_setattr, > }; > > +static struct inode *kvm_gmem_inode_make_secure_inode(const char *name, > + loff_t size, u64 flags) > +{ > + struct inode *inode; > + > + inode = anon_inode_make_secure_inode(kvm_gmem_mnt->mnt_sb, name, NULL); > + if (IS_ERR(inode)) > + return inode; > + > + inode->i_private = (void *)(unsigned long)flags; > + inode->i_op = &kvm_gmem_iops; > + inode->i_mapping->a_ops = &kvm_gmem_aops; > + inode->i_mode |= S_IFREG; > + inode->i_size = size; > + mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER); > + mapping_set_inaccessible(inode->i_mapping); > + /* Unmovable mappings are supposed to be marked unevictable as well. */ > + WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping)); > + > + return inode; > +} > + > +static struct file *kvm_gmem_inode_create_getfile(void *priv, loff_t size, > + u64 flags) > +{ > + static const char *name = "[kvm-gmem]"; > + struct inode *inode; > + struct file *file; > + int err; > + > + err = -ENOENT; > + if (!try_module_get(kvm_gmem_fops.owner)) > + goto err; Curious, shouldn't there be a module_put() somewhere after this function returned a file? > + > + inode = kvm_gmem_inode_make_secure_inode(name, size, flags); > + if (IS_ERR(inode)) { > + err = PTR_ERR(inode); > + goto err_put_module; > + } > + > + file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, > + &kvm_gmem_fops); > + if (IS_ERR(file)) { > + err = PTR_ERR(file); > + goto err_put_inode; > + } > + > + file->f_flags |= O_LARGEFILE; > + file->private_data = priv; > + > Nothing else jumped at me. -- Cheers, David / dhildenb
David Hildenbrand <david@redhat.com> writes: > On 13.07.25 19:43, Shivank Garg wrote: >> From: Ackerley Tng <ackerleytng@google.com> >> >> 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. >> >> 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> >> --- > > [...] > >> >> #include "kvm_mm.h" >> >> +static struct vfsmount *kvm_gmem_mnt; >> + >> struct kvm_gmem { >> struct kvm *kvm; >> struct xarray bindings; >> @@ -388,9 +392,51 @@ static struct file_operations kvm_gmem_fops = { >> .fallocate = kvm_gmem_fallocate, >> }; >> >> -void kvm_gmem_init(struct module *module) >> +static const struct super_operations kvm_gmem_super_operations = { >> + .statfs = simple_statfs, >> +}; >> + >> +static int kvm_gmem_init_fs_context(struct fs_context *fc) >> +{ >> + struct pseudo_fs_context *ctx; >> + >> + if (!init_pseudo(fc, GUEST_MEMFD_MAGIC)) >> + return -ENOMEM; >> + >> + ctx = fc->fs_private; >> + ctx->ops = &kvm_gmem_super_operations; > > Curious, why is that required? (secretmem doesn't have it, so I wonder) > Good point! pseudo_fs_fill_super() fills in a struct super_operations which already does simple_statfs, so guest_memfd doesn't need this. >> + >> + return 0; >> +} >> + >> +static struct file_system_type kvm_gmem_fs = { >> + .name = "kvm_guest_memory", > > It's GUEST_MEMFD_MAGIC but here "kvm_guest_memory". > > For secretmem it's SECRETMEM_MAGIC vs. "secretmem". > > So naturally, I wonder if that is to be made consistent :) > I'll update this to "guest_memfd" to be consistent. >> + .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; >> } >> >> static int kvm_gmem_migrate_folio(struct address_space *mapping, >> @@ -472,11 +518,71 @@ static const struct inode_operations kvm_gmem_iops = { >> .setattr = kvm_gmem_setattr, >> }; >> >> +static struct inode *kvm_gmem_inode_make_secure_inode(const char *name, >> + loff_t size, u64 flags) >> +{ >> + struct inode *inode; >> + >> + inode = anon_inode_make_secure_inode(kvm_gmem_mnt->mnt_sb, name, NULL); >> + if (IS_ERR(inode)) >> + return inode; >> + >> + inode->i_private = (void *)(unsigned long)flags; >> + inode->i_op = &kvm_gmem_iops; >> + inode->i_mapping->a_ops = &kvm_gmem_aops; >> + inode->i_mode |= S_IFREG; >> + inode->i_size = size; >> + mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER); >> + mapping_set_inaccessible(inode->i_mapping); >> + /* Unmovable mappings are supposed to be marked unevictable as well. */ >> + WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping)); >> + >> + return inode; >> +} >> + >> +static struct file *kvm_gmem_inode_create_getfile(void *priv, loff_t size, >> + u64 flags) >> +{ >> + static const char *name = "[kvm-gmem]"; >> + struct inode *inode; >> + struct file *file; >> + int err; >> + >> + err = -ENOENT; >> + if (!try_module_get(kvm_gmem_fops.owner)) >> + goto err; > > Curious, shouldn't there be a module_put() somewhere after this function > returned a file? > This was interesting indeed, but IIUC this is correct. I think this flow was basically copied from __anon_inode_getfile(), which does this try_module_get(). The corresponding module_put() is in __fput(), which calls fops_put() and calls module_put() on the owner. >> + >> + inode = kvm_gmem_inode_make_secure_inode(name, size, flags); >> + if (IS_ERR(inode)) { >> + err = PTR_ERR(inode); >> + goto err_put_module; >> + } >> + >> + file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, >> + &kvm_gmem_fops); >> + if (IS_ERR(file)) { >> + err = PTR_ERR(file); >> + goto err_put_inode; >> + } >> + >> + file->f_flags |= O_LARGEFILE; >> + file->private_data = priv; >> + >> > > Nothing else jumped at me. > Thanks for the review! Since we're going to submit this patch through Shivank's mempolicy support series, I'll follow up soon by sending a replacement patch in reply to this series so Shivank could build on top of that? > -- > Cheers, > > David / dhildenb
On 8/8/2025 3:04 AM, Ackerley Tng wrote: > David Hildenbrand <david@redhat.com> writes: > >> On 13.07.25 19:43, Shivank Garg wrote: >>> From: Ackerley Tng <ackerleytng@google.com> >>> >>> + ctx->ops = &kvm_gmem_super_operations; >> >> Curious, why is that required? (secretmem doesn't have it, so I wonder) >> > > Good point! pseudo_fs_fill_super() fills in a struct super_operations > which already does simple_statfs, so guest_memfd doesn't need this. > Right, simple_statfs isn't strictly needed in this patch, but the super_operations is required for the subsequent patches in the series which add custom alloc_inode, destroy_inode, and free_inode callback. >>> + if (!try_module_get(kvm_gmem_fops.owner)) >>> + goto err; >> >> Curious, shouldn't there be a module_put() somewhere after this function >> returned a file? >> > > This was interesting indeed, but IIUC this is correct. > > I think this flow was basically copied from __anon_inode_getfile(), > which does this try_module_get(). > > The corresponding module_put() is in __fput(), which calls fops_put() > and calls module_put() on the owner. > >>> + >>> >> >> Nothing else jumped at me. >> > > Thanks for the review! > > Since we're going to submit this patch through Shivank's mempolicy > support series, I'll follow up soon by sending a replacement patch in > reply to this series so Shivank could build on top of that? > yes, I'll post the V10 soon. Thanks, Shivank
Ackerley Tng <ackerleytng@google.com> writes: > David Hildenbrand <david@redhat.com> writes: > [snip] >> >> Nothing else jumped at me. >> > > Thanks for the review! > > Since we're going to submit this patch through Shivank's mempolicy > support series, I'll follow up soon by sending a replacement patch in > reply to this series so Shivank could build on top of that? > >> -- >> Cheers, >> >> David / dhildenb I hope sending a patch within a reply this way works! --- From 11845fed725ff68c3bad07cd9c717ae968465bf4 Mon Sep 17 00:00:00 2001 Message-ID: <11845fed725ff68c3bad07cd9c717ae968465bf4.1754603750.git.ackerleytng@google.com> From: Ackerley Tng <ackerleytng@google.com> Date: Sun, 13 Jul 2025 17:43:35 +0000 Subject: [PATCH 1/1] KVM: guest_memfd: Use guest mem inodes instead of anonymous inodes 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. Co-developed-by: Fuad Tabba <tabba@google.com> Change-Id: I64925f069637323023fbff91fc8521f92b8561bd Signed-off-by: Fuad Tabba <tabba@google.com> Signed-off-by: Shivank Garg <shivankg@amd.com> Signed-off-by: Ackerley Tng <ackerleytng@google.com> --- include/uapi/linux/magic.h | 1 + virt/kvm/guest_memfd.c | 128 ++++++++++++++++++++++++++++++------- virt/kvm/kvm_main.c | 7 +- virt/kvm/kvm_mm.h | 9 +-- 4 files changed, 118 insertions(+), 27 deletions(-) diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index bb575f3ab45e5..638ca21b7a909 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -103,5 +103,6 @@ #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__ */ diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 08a6bc7d25b60..0e93323fc8392 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -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; + struct kvm_gmem { struct kvm *kvm; struct xarray bindings; @@ -385,9 +389,45 @@ static struct file_operations kvm_gmem_fops = { .fallocate = kvm_gmem_fallocate, }; -void kvm_gmem_init(struct module *module) +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; } static int kvm_gmem_migrate_folio(struct address_space *mapping, @@ -463,11 +503,71 @@ bool __weak kvm_arch_supports_gmem_mmap(struct kvm *kvm) return true; } +static struct inode *kvm_gmem_inode_make_secure_inode(const char *name, + loff_t size, u64 flags) +{ + struct inode *inode; + + inode = anon_inode_make_secure_inode(kvm_gmem_mnt->mnt_sb, name, NULL); + if (IS_ERR(inode)) + return inode; + + inode->i_private = (void *)(unsigned long)flags; + inode->i_op = &kvm_gmem_iops; + inode->i_mapping->a_ops = &kvm_gmem_aops; + inode->i_mode |= S_IFREG; + inode->i_size = size; + mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER); + mapping_set_inaccessible(inode->i_mapping); + /* Unmovable mappings are supposed to be marked unevictable as well. */ + WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping)); + + return inode; +} + +static struct file *kvm_gmem_inode_create_getfile(void *priv, loff_t size, + u64 flags) +{ + static const char *name = "[kvm-gmem]"; + struct inode *inode; + struct file *file; + int err; + + err = -ENOENT; + if (!try_module_get(kvm_gmem_fops.owner)) + goto err; + + inode = kvm_gmem_inode_make_secure_inode(name, size, flags); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto err_put_module; + } + + file = alloc_file_pseudo(inode, kvm_gmem_mnt, name, O_RDWR, + &kvm_gmem_fops); + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto err_put_inode; + } + + file->f_flags |= O_LARGEFILE; + file->private_data = priv; + +out: + return file; + +err_put_inode: + iput(inode); +err_put_module: + module_put(kvm_gmem_fops.owner); +err: + file = ERR_PTR(err); + goto out; +} + static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags) { - const char *anon_name = "[kvm-gmem]"; struct kvm_gmem *gmem; - struct inode *inode; struct file *file; int fd, err; @@ -481,32 +581,16 @@ 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, gmem, - O_RDWR, NULL); + file = kvm_gmem_inode_create_getfile(gmem, size, flags); if (IS_ERR(file)) { err = PTR_ERR(file); goto err_gmem; } - file->f_flags |= O_LARGEFILE; - - inode = file->f_inode; - WARN_ON(file->f_mapping != inode->i_mapping); - - inode->i_private = (void *)(unsigned long)flags; - inode->i_op = &kvm_gmem_iops; - inode->i_mapping->a_ops = &kvm_gmem_aops; - inode->i_mode |= S_IFREG; - inode->i_size = size; - mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER); - mapping_set_inaccessible(inode->i_mapping); - /* Unmovable mappings are supposed to be marked unevictable as well. */ - WARN_ON_ONCE(!mapping_unevictable(inode->i_mapping)); - kvm_get_kvm(kvm); gmem->kvm = kvm; xa_init(&gmem->bindings); - list_add(&gmem->entry, &inode->i_mapping->i_private_list); + list_add(&gmem->entry, &file_inode(file)->i_mapping->i_private_list); fd_install(fd, file); return fd; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 18f29ef935437..301d48d6e00d0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -6489,7 +6489,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) @@ -6510,6 +6512,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(); @@ -6541,6 +6545,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(); diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h index 31defb08ccbab..9fcc5d5b7f8d0 100644 --- a/virt/kvm/kvm_mm.h +++ b/virt/kvm/kvm_mm.h @@ -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) -- 2.50.1.703.g449372360f-goog
© 2016 - 2025 Red Hat, Inc.