From: Ackerley Tng <ackerley...@google.com> KVM_LINK_GUEST_MEMFD will link a gmem fd's underlying inode to a new file (and fd).
Signed-off-by: Ackerley Tng <ackerley...@google.com> Co-developed-by: Ryan Afranji <afra...@google.com> Signed-off-by: Ryan Afranji <afra...@google.com> --- include/uapi/linux/kvm.h | 8 ++++++ virt/kvm/guest_memfd.c | 57 ++++++++++++++++++++++++++++++++++++++++ virt/kvm/kvm_main.c | 10 +++++++ virt/kvm/kvm_mm.h | 7 +++++ 4 files changed, 82 insertions(+) diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index c6988e2c68d5..8f17f0b462aa 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1583,4 +1583,12 @@ struct kvm_pre_fault_memory { __u64 padding[5]; }; +#define KVM_LINK_GUEST_MEMFD _IOWR(KVMIO, 0xd6, struct kvm_link_guest_memfd) + +struct kvm_link_guest_memfd { + __u64 fd; + __u64 flags; + __u64 reserved[6]; +}; + #endif /* __LINUX_KVM_H */ diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index a3918d1695b9..d76bd1119198 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -555,6 +555,63 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) return __kvm_gmem_create(kvm, size, flags); } +int kvm_gmem_link(struct kvm *kvm, struct kvm_link_guest_memfd *args) +{ + static const char *name = "[kvm-gmem]"; + u64 flags = args->flags; + u64 valid_flags = 0; + struct file *dst_file, *src_file; + struct kvm_gmem *gmem; + struct timespec64 ts; + struct inode *inode; + struct fd f; + int ret, fd; + + if (flags & ~valid_flags) + return -EINVAL; + + f = fdget(args->fd); + src_file = fd_file(f); + if (!src_file) + return -EINVAL; + + ret = -EINVAL; + if (src_file->f_op != &kvm_gmem_fops) + goto out; + + /* Cannot link a gmem file with the same vm again */ + gmem = src_file->private_data; + if (gmem->kvm == kvm) + goto out; + + ret = fd = get_unused_fd_flags(0); + if (ret < 0) + goto out; + + inode = file_inode(src_file); + dst_file = kvm_gmem_alloc_view(kvm, inode, name); + if (IS_ERR(dst_file)) { + ret = PTR_ERR(dst_file); + goto out_fd; + } + + ts = inode_set_ctime_current(inode); + inode_set_atime_to_ts(inode, ts); + + inc_nlink(inode); + ihold(inode); + + fd_install(fd, dst_file); + fdput(f); + return fd; + +out_fd: + put_unused_fd(fd); +out: + fdput(f); + return ret; +} + int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, unsigned int fd, loff_t offset) { diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1e3fd81868bc..a9b01841a243 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -5285,6 +5285,16 @@ static long kvm_vm_ioctl(struct file *filp, r = kvm_gmem_create(kvm, &guest_memfd); break; } + case KVM_LINK_GUEST_MEMFD: { + struct kvm_link_guest_memfd params; + + r = -EFAULT; + if (copy_from_user(¶ms, argp, sizeof(params))) + goto out; + + r = kvm_gmem_link(kvm, ¶ms); + break; + } #endif default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h index dcacb76b8f00..85baf8a7e0de 100644 --- a/virt/kvm/kvm_mm.h +++ b/virt/kvm/kvm_mm.h @@ -71,6 +71,7 @@ static inline void gfn_to_pfn_cache_invalidate_start(struct kvm *kvm, 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_link(struct kvm *kvm, struct kvm_link_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); @@ -82,6 +83,12 @@ static inline int kvm_gmem_init(struct module *module) static inline void kvm_gmem_exit(void) {}; +static inline int kvm_gmem_link(struct kvm *kvm, + struct kvm_link_guest_memfd *args) +{ + return -EOPNOTSUPP; +} + static inline int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, unsigned int fd, loff_t offset) -- 2.49.0.1101.gccaa498523-goog