Message ID | 20250430165655.605595-9-tabba@google.com |
---|---|
State | New |
Headers | show |
Series | KVM: Mapping guest_memfd backed memory at the host for software protected VMs | expand |
On 30.04.25 18:56, Fuad Tabba wrote: > Add support for mmap() and fault() for guest_memfd backed memory > in the host for VMs that support in-place conversion between > shared and private. To that end, this patch adds the ability to > check whether the VM type supports in-place conversion, and only > allows mapping its memory if that's the case. > > This patch introduces the configuration option KVM_GMEM_SHARED_MEM, > which enables support for in-place shared memory. > > It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which > indicates that the host can create VMs that support shared memory. > Supporting shared memory implies that memory can be mapped when shared > with the host. I think you should clarify here that it's not about "supports in-place conversion" in the context of this series. It's about mapping shared pages only; initially, we'll introduce the option to only have shared memory in guest memfd, and later we'll introduce the option for in-place conversion.
On 30.04.25 18:56, Fuad Tabba wrote: > Add support for mmap() and fault() for guest_memfd backed memory > in the host for VMs that support in-place conversion between > shared and private. To that end, this patch adds the ability to > check whether the VM type supports in-place conversion, and only > allows mapping its memory if that's the case. > > This patch introduces the configuration option KVM_GMEM_SHARED_MEM, > which enables support for in-place shared memory. > > It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which > indicates that the host can create VMs that support shared memory. > Supporting shared memory implies that memory can be mapped when shared > with the host. > > Signed-off-by: Fuad Tabba <tabba@google.com> > --- > include/linux/kvm_host.h | 15 ++++++- > include/uapi/linux/kvm.h | 1 + > virt/kvm/Kconfig | 5 +++ > virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ > virt/kvm/kvm_main.c | 4 ++ > 5 files changed, 116 insertions(+), 1 deletion(-) > > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h > index 9419fb99f7c2..f3af6bff3232 100644 > --- a/include/linux/kvm_host.h > +++ b/include/linux/kvm_host.h > @@ -729,6 +729,17 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) > } > #endif > > +/* > + * Arch code must define kvm_arch_gmem_supports_shared_mem if support for > + * private memory is enabled and it supports in-place shared/private conversion. > + */ > +#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) > +static inline bool kvm_arch_gmem_supports_shared_mem(struct kvm *kvm) > +{ > + return false; > +} > +#endif > + > #ifndef kvm_arch_has_readonly_mem > static inline bool kvm_arch_has_readonly_mem(struct kvm *kvm) > { > @@ -2516,7 +2527,9 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn) > > static inline bool kvm_mem_from_gmem(struct kvm *kvm, gfn_t gfn) > { > - /* For now, only private memory gets consumed from guest_memfd. */ > + if (kvm_arch_gmem_supports_shared_mem(kvm)) > + return true; After our discussion yesterday, am I correct that we will not be querying the KVM capability, but instead the "SHARED_TRACKING" (or however that flag is called) on the underlying guest_memfd instead? I assume the function would then look something like if (!kvm_supports_gmem(kvm)) return false; if (kvm_arch_gmem_supports_shared_mem(kvm)) return .. TBD, test the gmem flag for the slot via gfn return kvm_mem_is_private(kvm, gfn); > + > return kvm_mem_is_private(kvm, gfn); > } > > diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h > index b6ae8ad8934b..8bc8046c7f3a 100644 > --- a/include/uapi/linux/kvm.h > +++ b/include/uapi/linux/kvm.h > @@ -930,6 +930,7 @@ struct kvm_enable_cap { > #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 > #define KVM_CAP_X86_GUEST_MODE 238 > #define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 > +#define KVM_CAP_GMEM_SHARED_MEM 240 > > struct kvm_irq_routing_irqchip { > __u32 irqchip; > diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig > index 559c93ad90be..f4e469a62a60 100644 > --- a/virt/kvm/Kconfig > +++ b/virt/kvm/Kconfig > @@ -128,3 +128,8 @@ config HAVE_KVM_ARCH_GMEM_PREPARE > config HAVE_KVM_ARCH_GMEM_INVALIDATE > bool > depends on KVM_GMEM > + > +config KVM_GMEM_SHARED_MEM > + select KVM_GMEM > + bool > + prompt "Enables in-place shared memory for guest_memfd" > diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c > index 6db515833f61..8bc8fc991d58 100644 > --- a/virt/kvm/guest_memfd.c > +++ b/virt/kvm/guest_memfd.c > @@ -312,7 +312,99 @@ static pgoff_t kvm_gmem_get_index(struct kvm_memory_slot *slot, gfn_t gfn) > return gfn - slot->base_gfn + slot->gmem.pgoff; > } > > +#ifdef CONFIG_KVM_GMEM_SHARED_MEM > +/* > + * Returns true if the folio is shared with the host and the guest. > + */ > +static bool kvm_gmem_offset_is_shared(struct file *file, pgoff_t index) > +{ > + struct kvm_gmem *gmem = file->private_data; > + > + /* For now, VMs that support shared memory share all their memory. */ > + return kvm_arch_gmem_supports_shared_mem(gmem->kvm); Similar here: likely we want to check the guest_memfd flag.
David Hildenbrand <david@redhat.com> writes: > On 30.04.25 18:56, Fuad Tabba wrote: >> Add support for mmap() and fault() for guest_memfd backed memory >> in the host for VMs that support in-place conversion between >> shared and private. To that end, this patch adds the ability to >> check whether the VM type supports in-place conversion, and only >> allows mapping its memory if that's the case. >> >> This patch introduces the configuration option KVM_GMEM_SHARED_MEM, >> which enables support for in-place shared memory. >> >> It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which >> indicates that the host can create VMs that support shared memory. >> Supporting shared memory implies that memory can be mapped when shared >> with the host. >> >> Signed-off-by: Fuad Tabba <tabba@google.com> >> --- >> include/linux/kvm_host.h | 15 ++++++- >> include/uapi/linux/kvm.h | 1 + >> virt/kvm/Kconfig | 5 +++ >> virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ >> virt/kvm/kvm_main.c | 4 ++ >> 5 files changed, 116 insertions(+), 1 deletion(-) >> >> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h >> index 9419fb99f7c2..f3af6bff3232 100644 >> --- a/include/linux/kvm_host.h >> +++ b/include/linux/kvm_host.h >> @@ -729,6 +729,17 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) >> } >> #endif >> >> +/* >> + * Arch code must define kvm_arch_gmem_supports_shared_mem if support for >> + * private memory is enabled and it supports in-place shared/private conversion. >> + */ >> +#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) >> +static inline bool kvm_arch_gmem_supports_shared_mem(struct kvm *kvm) >> +{ >> + return false; >> +} >> +#endif >> + >> #ifndef kvm_arch_has_readonly_mem >> static inline bool kvm_arch_has_readonly_mem(struct kvm *kvm) >> { >> @@ -2516,7 +2527,9 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn) >> >> static inline bool kvm_mem_from_gmem(struct kvm *kvm, gfn_t gfn) >> { >> - /* For now, only private memory gets consumed from guest_memfd. */ >> + if (kvm_arch_gmem_supports_shared_mem(kvm)) >> + return true; > > After our discussion yesterday, am I correct that we will not be > querying the KVM capability, but instead the "SHARED_TRACKING" (or > however that flag is called) on the underlying guest_memfd instead? > > I assume the function would then look something like > > if (!kvm_supports_gmem(kvm)) > return false; > if (kvm_arch_gmem_supports_shared_mem(kvm)) > return .. TBD, test the gmem flag for the slot via gfn > return kvm_mem_is_private(kvm, gfn); > Yes, I believe we're aligned here. I added a patch that will do this, but it depends on other parts of the patch series and I think it's better if you review it altogether when Fuad posts it (as opposed to reviewing a snippet I drop here. >> + >> return kvm_mem_is_private(kvm, gfn); >> } >> >> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h >> index b6ae8ad8934b..8bc8046c7f3a 100644 >> --- a/include/uapi/linux/kvm.h >> +++ b/include/uapi/linux/kvm.h >> @@ -930,6 +930,7 @@ struct kvm_enable_cap { >> #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 >> #define KVM_CAP_X86_GUEST_MODE 238 >> #define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 >> +#define KVM_CAP_GMEM_SHARED_MEM 240 >> >> struct kvm_irq_routing_irqchip { >> __u32 irqchip; >> diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig >> index 559c93ad90be..f4e469a62a60 100644 >> --- a/virt/kvm/Kconfig >> +++ b/virt/kvm/Kconfig >> @@ -128,3 +128,8 @@ config HAVE_KVM_ARCH_GMEM_PREPARE >> config HAVE_KVM_ARCH_GMEM_INVALIDATE >> bool >> depends on KVM_GMEM >> + >> +config KVM_GMEM_SHARED_MEM >> + select KVM_GMEM >> + bool >> + prompt "Enables in-place shared memory for guest_memfd" >> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c >> index 6db515833f61..8bc8fc991d58 100644 >> --- a/virt/kvm/guest_memfd.c >> +++ b/virt/kvm/guest_memfd.c >> @@ -312,7 +312,99 @@ static pgoff_t kvm_gmem_get_index(struct kvm_memory_slot *slot, gfn_t gfn) >> return gfn - slot->base_gfn + slot->gmem.pgoff; >> } >> >> +#ifdef CONFIG_KVM_GMEM_SHARED_MEM >> +/* >> + * Returns true if the folio is shared with the host and the guest. >> + */ >> +static bool kvm_gmem_offset_is_shared(struct file *file, pgoff_t index) >> +{ >> + struct kvm_gmem *gmem = file->private_data; >> + >> + /* For now, VMs that support shared memory share all their memory. */ >> + return kvm_arch_gmem_supports_shared_mem(gmem->kvm); > > Similar here: likely we want to check the guest_memfd flag. > > -- > Cheers, > > David / dhildenb
Fuad Tabba <tabba@google.com> writes: > Add support for mmap() and fault() for guest_memfd backed memory > in the host for VMs that support in-place conversion between > shared and private. To that end, this patch adds the ability to > check whether the VM type supports in-place conversion, and only > allows mapping its memory if that's the case. > > This patch introduces the configuration option KVM_GMEM_SHARED_MEM, > which enables support for in-place shared memory. > > It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which > indicates that the host can create VMs that support shared memory. > Supporting shared memory implies that memory can be mapped when shared > with the host. > > Signed-off-by: Fuad Tabba <tabba@google.com> > --- > include/linux/kvm_host.h | 15 ++++++- > include/uapi/linux/kvm.h | 1 + > virt/kvm/Kconfig | 5 +++ > virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ > virt/kvm/kvm_main.c | 4 ++ > 5 files changed, 116 insertions(+), 1 deletion(-) > > <snip> At the guest_memfd call on 2025-05-01, we discussed that if guest_memfd is created with GUEST_MEMFD_FLAG_SUPPORT_SHARED set, then if slot->userspace_addr != 0, we would validate that the folio slot->userspace_addr points to matches up with the folio guest_memfd would return for the same offset. I can think of one way to do this validation, which is to call KVM's hva_to_pfn() function and then call kvm_gmem_get_folio() on the fd and offset, and then check that the PFNs are equal. However, that would cause the page to be allocated. Any ideas on how we could do this validation without allocating the page?
Fuad Tabba wrote: > Add support for mmap() and fault() for guest_memfd backed memory > in the host for VMs that support in-place conversion between > shared and private. To that end, this patch adds the ability to > check whether the VM type supports in-place conversion, and only > allows mapping its memory if that's the case. > > This patch introduces the configuration option KVM_GMEM_SHARED_MEM, > which enables support for in-place shared memory. > > It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which > indicates that the host can create VMs that support shared memory. > Supporting shared memory implies that memory can be mapped when shared > with the host. > > Signed-off-by: Fuad Tabba <tabba@google.com> > --- > include/linux/kvm_host.h | 15 ++++++- > include/uapi/linux/kvm.h | 1 + > virt/kvm/Kconfig | 5 +++ > virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ > virt/kvm/kvm_main.c | 4 ++ > 5 files changed, 116 insertions(+), 1 deletion(-) > > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h > index 9419fb99f7c2..f3af6bff3232 100644 > --- a/include/linux/kvm_host.h > +++ b/include/linux/kvm_host.h > @@ -729,6 +729,17 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) > } > #endif > > +/* > + * Arch code must define kvm_arch_gmem_supports_shared_mem if support for > + * private memory is enabled and it supports in-place shared/private conversion. > + */ > +#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) Perhaps the bots already caught this? I just tried enabling KVM_GMEM_SHARED_MEM on x86 with this patch and it fails with: || In file included from arch/x86/kvm/../../../virt/kvm/binary_stats.c:8: || ./include/linux/kvm_host.h: In function ‘kvm_mem_from_gmem’: include/linux/kvm_host.h|2530 col 13| error: implicit declaration of function ‘kvm_arch_gmem_supports_shared_mem’ [-Wimplicit-function-declaration] || 2530 | if (kvm_arch_gmem_supports_shared_mem(kvm)) || | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ || make[4]: *** Waiting for unfinished jobs.... I think the predicate on !CONFIG_KVM_GMEM_SHARED_MEM is wrong. Shouldn't this always default off? I __think__ this then gets enabled in 11/13? IOW diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index f3af6bff3232..577674e95c09 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -733,7 +733,7 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) * Arch code must define kvm_arch_gmem_supports_shared_mem if support for * private memory is enabled and it supports in-place shared/private conversion. */ -#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) +#if !defined(kvm_arch_gmem_supports_shared_mem) static inline bool kvm_arch_gmem_supports_shared_mem(struct kvm *kvm) { return false;
On Fri, May 02, 2025 at 03:29:53PM -0700, Ackerley Tng wrote: > Fuad Tabba <tabba@google.com> writes: > > > Add support for mmap() and fault() for guest_memfd backed memory > > in the host for VMs that support in-place conversion between > > shared and private. To that end, this patch adds the ability to > > check whether the VM type supports in-place conversion, and only > > allows mapping its memory if that's the case. > > > > This patch introduces the configuration option KVM_GMEM_SHARED_MEM, > > which enables support for in-place shared memory. > > > > It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which > > indicates that the host can create VMs that support shared memory. > > Supporting shared memory implies that memory can be mapped when shared > > with the host. > > > > Signed-off-by: Fuad Tabba <tabba@google.com> > > --- > > include/linux/kvm_host.h | 15 ++++++- > > include/uapi/linux/kvm.h | 1 + > > virt/kvm/Kconfig | 5 +++ > > virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ > > virt/kvm/kvm_main.c | 4 ++ > > 5 files changed, 116 insertions(+), 1 deletion(-) > > > > <snip> > > At the guest_memfd call on 2025-05-01, we discussed that if guest_memfd > is created with GUEST_MEMFD_FLAG_SUPPORT_SHARED set, then if > slot->userspace_addr != 0, we would validate that the folio > slot->userspace_addr points to matches up with the folio guest_memfd > would return for the same offset. Where will the validation be executed? In kvm_gmem_bind()? > > I can think of one way to do this validation, which is to call KVM's > hva_to_pfn() function and then call kvm_gmem_get_folio() on the fd and > offset, and then check that the PFNs are equal. > > However, that would cause the page to be allocated. Any ideas on how we > could do this validation without allocating the page? If the check is in kvm_gmem_bind() and if there's no worry about munmap() and re-mmap() of the shared memory pointed by slot->userspace_addr, maybe below? mm = kvm->mm; mmap_read_lock(mm); vma = vma_lookup(mm, vaddr); pgoff = ((slot->userspace_addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; mmap_read_unlock(mm); Then check if pgoff equals to slot->gmem.guest_memfd_offset.
Hi Ira, On Mon, 5 May 2025 at 22:05, Ira Weiny <ira.weiny@intel.com> wrote: > > Fuad Tabba wrote: > > Add support for mmap() and fault() for guest_memfd backed memory > > in the host for VMs that support in-place conversion between > > shared and private. To that end, this patch adds the ability to > > check whether the VM type supports in-place conversion, and only > > allows mapping its memory if that's the case. > > > > This patch introduces the configuration option KVM_GMEM_SHARED_MEM, > > which enables support for in-place shared memory. > > > > It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which > > indicates that the host can create VMs that support shared memory. > > Supporting shared memory implies that memory can be mapped when shared > > with the host. > > > > Signed-off-by: Fuad Tabba <tabba@google.com> > > --- > > include/linux/kvm_host.h | 15 ++++++- > > include/uapi/linux/kvm.h | 1 + > > virt/kvm/Kconfig | 5 +++ > > virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ > > virt/kvm/kvm_main.c | 4 ++ > > 5 files changed, 116 insertions(+), 1 deletion(-) > > > > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h > > index 9419fb99f7c2..f3af6bff3232 100644 > > --- a/include/linux/kvm_host.h > > +++ b/include/linux/kvm_host.h > > @@ -729,6 +729,17 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) > > } > > #endif > > > > +/* > > + * Arch code must define kvm_arch_gmem_supports_shared_mem if support for > > + * private memory is enabled and it supports in-place shared/private conversion. > > + */ > > +#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) > > Perhaps the bots already caught this? > > I just tried enabling KVM_GMEM_SHARED_MEM on x86 with this patch and it fails with: > > || In file included from arch/x86/kvm/../../../virt/kvm/binary_stats.c:8: > || ./include/linux/kvm_host.h: In function ‘kvm_mem_from_gmem’: > include/linux/kvm_host.h|2530 col 13| error: implicit declaration of function ‘kvm_arch_gmem_supports_shared_mem’ [-Wimplicit-function-declaration] > || 2530 | if (kvm_arch_gmem_supports_shared_mem(kvm)) > || | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > || make[4]: *** Waiting for unfinished jobs.... > > > I think the predicate on !CONFIG_KVM_GMEM_SHARED_MEM is wrong. > > Shouldn't this always default off? I __think__ this then gets enabled in > 11/13? You're right. With the other comments from David and Ackerley, this functions is gone, replaced by checking a per-vm flag. Thanks, /fuad > IOW > > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h > index f3af6bff3232..577674e95c09 100644 > --- a/include/linux/kvm_host.h > +++ b/include/linux/kvm_host.h > @@ -733,7 +733,7 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) > * Arch code must define kvm_arch_gmem_supports_shared_mem if support for > * private memory is enabled and it supports in-place shared/private conversion. > */ > -#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) > +#if !defined(kvm_arch_gmem_supports_shared_mem) > static inline bool kvm_arch_gmem_supports_shared_mem(struct kvm *kvm) > { > return false;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 9419fb99f7c2..f3af6bff3232 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -729,6 +729,17 @@ static inline bool kvm_arch_supports_gmem(struct kvm *kvm) } #endif +/* + * Arch code must define kvm_arch_gmem_supports_shared_mem if support for + * private memory is enabled and it supports in-place shared/private conversion. + */ +#if !defined(kvm_arch_gmem_supports_shared_mem) && !IS_ENABLED(CONFIG_KVM_GMEM_SHARED_MEM) +static inline bool kvm_arch_gmem_supports_shared_mem(struct kvm *kvm) +{ + return false; +} +#endif + #ifndef kvm_arch_has_readonly_mem static inline bool kvm_arch_has_readonly_mem(struct kvm *kvm) { @@ -2516,7 +2527,9 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn) static inline bool kvm_mem_from_gmem(struct kvm *kvm, gfn_t gfn) { - /* For now, only private memory gets consumed from guest_memfd. */ + if (kvm_arch_gmem_supports_shared_mem(kvm)) + return true; + return kvm_mem_is_private(kvm, gfn); } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index b6ae8ad8934b..8bc8046c7f3a 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -930,6 +930,7 @@ struct kvm_enable_cap { #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 #define KVM_CAP_X86_GUEST_MODE 238 #define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 +#define KVM_CAP_GMEM_SHARED_MEM 240 struct kvm_irq_routing_irqchip { __u32 irqchip; diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig index 559c93ad90be..f4e469a62a60 100644 --- a/virt/kvm/Kconfig +++ b/virt/kvm/Kconfig @@ -128,3 +128,8 @@ config HAVE_KVM_ARCH_GMEM_PREPARE config HAVE_KVM_ARCH_GMEM_INVALIDATE bool depends on KVM_GMEM + +config KVM_GMEM_SHARED_MEM + select KVM_GMEM + bool + prompt "Enables in-place shared memory for guest_memfd" diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 6db515833f61..8bc8fc991d58 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -312,7 +312,99 @@ static pgoff_t kvm_gmem_get_index(struct kvm_memory_slot *slot, gfn_t gfn) return gfn - slot->base_gfn + slot->gmem.pgoff; } +#ifdef CONFIG_KVM_GMEM_SHARED_MEM +/* + * Returns true if the folio is shared with the host and the guest. + */ +static bool kvm_gmem_offset_is_shared(struct file *file, pgoff_t index) +{ + struct kvm_gmem *gmem = file->private_data; + + /* For now, VMs that support shared memory share all their memory. */ + return kvm_arch_gmem_supports_shared_mem(gmem->kvm); +} + +static vm_fault_t kvm_gmem_fault_shared(struct vm_fault *vmf) +{ + struct inode *inode = file_inode(vmf->vma->vm_file); + struct folio *folio; + vm_fault_t ret = VM_FAULT_LOCKED; + + filemap_invalidate_lock_shared(inode->i_mapping); + + folio = kvm_gmem_get_folio(inode, vmf->pgoff); + if (IS_ERR(folio)) { + int err = PTR_ERR(folio); + + if (err == -EAGAIN) + ret = VM_FAULT_RETRY; + else + ret = vmf_error(err); + + goto out_filemap; + } + + if (folio_test_hwpoison(folio)) { + ret = VM_FAULT_HWPOISON; + goto out_folio; + } + + if (!kvm_gmem_offset_is_shared(vmf->vma->vm_file, vmf->pgoff)) { + ret = VM_FAULT_SIGBUS; + goto out_folio; + } + + if (WARN_ON_ONCE(folio_test_large(folio))) { + ret = VM_FAULT_SIGBUS; + goto out_folio; + } + + if (!folio_test_uptodate(folio)) { + clear_highpage(folio_page(folio, 0)); + kvm_gmem_mark_prepared(folio); + } + + vmf->page = folio_file_page(folio, vmf->pgoff); + +out_folio: + if (ret != VM_FAULT_LOCKED) { + folio_unlock(folio); + folio_put(folio); + } + +out_filemap: + filemap_invalidate_unlock_shared(inode->i_mapping); + + return ret; +} + +static const struct vm_operations_struct kvm_gmem_vm_ops = { + .fault = kvm_gmem_fault_shared, +}; + +static int kvm_gmem_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct kvm_gmem *gmem = file->private_data; + + if (!kvm_arch_gmem_supports_shared_mem(gmem->kvm)) + return -ENODEV; + + if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) != + (VM_SHARED | VM_MAYSHARE)) { + return -EINVAL; + } + + vm_flags_set(vma, VM_DONTDUMP); + vma->vm_ops = &kvm_gmem_vm_ops; + + return 0; +} +#else +#define kvm_gmem_mmap NULL +#endif /* CONFIG_KVM_GMEM_SHARED_MEM */ + static struct file_operations kvm_gmem_fops = { + .mmap = kvm_gmem_mmap, .open = generic_file_open, .release = kvm_gmem_release, .fallocate = kvm_gmem_fallocate, diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 6289ea1685dd..c75d8e188eb7 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -4845,6 +4845,10 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) #ifdef CONFIG_KVM_GMEM case KVM_CAP_GUEST_MEMFD: return !kvm || kvm_arch_supports_gmem(kvm); +#endif +#ifdef CONFIG_KVM_GMEM_SHARED_MEM + case KVM_CAP_GMEM_SHARED_MEM: + return !kvm || kvm_arch_gmem_supports_shared_mem(kvm); #endif default: break;
Add support for mmap() and fault() for guest_memfd backed memory in the host for VMs that support in-place conversion between shared and private. To that end, this patch adds the ability to check whether the VM type supports in-place conversion, and only allows mapping its memory if that's the case. This patch introduces the configuration option KVM_GMEM_SHARED_MEM, which enables support for in-place shared memory. It also introduces the KVM capability KVM_CAP_GMEM_SHARED_MEM, which indicates that the host can create VMs that support shared memory. Supporting shared memory implies that memory can be mapped when shared with the host. Signed-off-by: Fuad Tabba <tabba@google.com> --- include/linux/kvm_host.h | 15 ++++++- include/uapi/linux/kvm.h | 1 + virt/kvm/Kconfig | 5 +++ virt/kvm/guest_memfd.c | 92 ++++++++++++++++++++++++++++++++++++++++ virt/kvm/kvm_main.c | 4 ++ 5 files changed, 116 insertions(+), 1 deletion(-)