mbox series

[v3,00/15] KVM: mm: fd-based approach for supporting KVM guest private memory

Message ID 20211221151125.19446-1-chao.p.peng@linux.intel.com (mailing list archive)
Headers show
Series KVM: mm: fd-based approach for supporting KVM guest private memory | expand

Message

Chao Peng Dec. 21, 2021, 3:11 p.m. UTC
This is the third version of this series which try to implement the
fd-based KVM guest private memory.

In general this patch series introduce fd-based memslot which provide
guest memory through a memfd file descriptor fd[offset,size] instead of
hva/size. The fd then can be created from a supported memory filesystem
like tmpfs/hugetlbfs etc which we refer as memory backend. KVM and the
memory backend exchange some callbacks when such memslot gets created.
At runtime KVM will call into callbacks provided by backend to get the
pfn with the fd+offset. Memory backend will also call into KVM callbacks
when userspace fallocate/punch hole on the fd to notify KVM to map/unmap
secondary MMU page tables.

Comparing to existing hva-based memslot, this new type of memslot allow
guest memory unmapped from host userspace like QEMU and even the kernel
itself, therefore reduce attack surface and prevent userspace bugs.

Based on this fd-based memslot, we can build guest private memory that
is going to be used in confidential computing environments such as Intel
TDX and AMD SEV. When supported, the memory backend can provide more
enforcement on the fd and KVM can use a single memslot to hold both the
private and shared part of the guest memory. 

Memfd/shmem extension
---------------------
Introduces new MFD_INACCESSIBLE flag for memfd_create(), the file
created with this flag cannot read(), write() or mmap() etc.

In addition, two sets of callbacks are introduced as new MEMFD_OPS:
  - memfd_falloc_notifier: memfd -> KVM notifier when memory gets
    allocated/invalidated through fallocate().
  - memfd_pfn_ops: kvm -> memfd to get a pfn with the fd+offset.

Memslot extension
-----------------
Add the private fd and the offset into the fd to existing 'shared' memslot
so that both private/shared guest memory can live in one single memslot.
A page in the memslot is either private or shared. A page is private only
when it's already allocated in the backend fd, all the other cases it's
treated as shared, this includes those already mapped as shared as well as
those having not been mapped. This means the memory backend is the place
which tells the truth of which page is private.

Private memory map/unmap and conversion
---------------------------------------
Userspace's map/unmap operations are done by fallocate() ioctl on the
backend fd.
  - map: default fallocate() with mode=0.
  - unmap: fallocate() with FALLOC_FL_PUNCH_HOLE.
The map/unmap will trigger above memfd_falloc_notifier to let KVM
map/unmap second MMU page tables.

Test
----
This code has been tested with latest TDX code patches hosted at
(https://github.com/intel/tdx/tree/kvm-upstream) with minimal TDX
adaption and QEMU support.

Example QEMU command line:
-object tdx-guest,id=tdx \
-object memory-backend-memfd-private,id=ram1,size=2G \
-machine q35,kvm-type=tdx,pic=no,kernel_irqchip=split,memory-encryption=tdx,memory-backend=ram1

Changelog
----------
v3:
  - Added locking protection when calling
    invalidate_page_range/fallocate callbacks.
  - Changed memslot structure to keep use useraddr for shared memory.
  - Re-organized F_SEAL_INACCESSIBLE and MEMFD_OPS.
  - Added MFD_INACCESSIBLE flag to force F_SEAL_INACCESSIBLE.
  - Commit message improvement.
  - Many small fixes for comments from the last version.

Links of previous discussions
-----------------------------
[1]
https://lkml.kernel.org/kvm/51a6f74f-6c05-74b9-3fd7-b7cd900fb8cc@redhat.com/
[2]
https://lkml.kernel.org/linux-fsdevel/20211111141352.26311-1-chao.p.peng@linux.intel.com/


Chao Peng (13):
  mm/memfd: Introduce MFD_INACCESSIBLE flag
  KVM: Extend the memslot to support fd-based private memory
  KVM: Implement fd-based memory using MEMFD_OPS interfaces
  KVM: Refactor hva based memory invalidation code
  KVM: Special handling for fd-based memory invalidation
  KVM: Split out common memory invalidation code
  KVM: Implement fd-based memory invalidation
  KVM: Add kvm_map_gfn_range
  KVM: Implement fd-based memory fallocation
  KVM: Add KVM_EXIT_MEMORY_ERROR exit
  KVM: Handle page fault for private memory
  KVM: Use kvm_userspace_memory_region_ext
  KVM: Register/unregister private memory slot to memfd

Kirill A. Shutemov (2):
  mm/shmem: Introduce F_SEAL_INACCESSIBLE
  mm/memfd: Introduce MEMFD_OPS

 arch/arm64/kvm/mmu.c               |  14 +-
 arch/mips/kvm/mips.c               |  14 +-
 arch/powerpc/include/asm/kvm_ppc.h |  28 ++--
 arch/powerpc/kvm/book3s.c          |  14 +-
 arch/powerpc/kvm/book3s_hv.c       |  14 +-
 arch/powerpc/kvm/book3s_pr.c       |  14 +-
 arch/powerpc/kvm/booke.c           |  14 +-
 arch/powerpc/kvm/powerpc.c         |  14 +-
 arch/riscv/kvm/mmu.c               |  14 +-
 arch/s390/kvm/kvm-s390.c           |  14 +-
 arch/x86/kvm/Kconfig               |   1 +
 arch/x86/kvm/Makefile              |   3 +-
 arch/x86/kvm/mmu/mmu.c             | 112 +++++++++++++-
 arch/x86/kvm/mmu/paging_tmpl.h     |  11 +-
 arch/x86/kvm/x86.c                 |  16 +-
 include/linux/kvm_host.h           |  57 +++++--
 include/linux/memfd.h              |  22 +++
 include/linux/shmem_fs.h           |  16 ++
 include/uapi/linux/fcntl.h         |   1 +
 include/uapi/linux/kvm.h           |  27 ++++
 include/uapi/linux/memfd.h         |   1 +
 mm/Kconfig                         |   4 +
 mm/memfd.c                         |  33 +++-
 mm/shmem.c                         | 195 ++++++++++++++++++++++-
 virt/kvm/kvm_main.c                | 239 +++++++++++++++++++++--------
 virt/kvm/memfd.c                   | 100 ++++++++++++
 26 files changed, 822 insertions(+), 170 deletions(-)
 create mode 100644 virt/kvm/memfd.c

Comments

Sean Christopherson Dec. 21, 2021, 3:44 p.m. UTC | #1
On Tue, Dec 21, 2021, Chao Peng wrote:
> This is the third version of this series which try to implement the
> fd-based KVM guest private memory.

...

> Test
> ----
> This code has been tested with latest TDX code patches hosted at
> (https://github.com/intel/tdx/tree/kvm-upstream) with minimal TDX
> adaption and QEMU support.
> 
> Example QEMU command line:
> -object tdx-guest,id=tdx \
> -object memory-backend-memfd-private,id=ram1,size=2G \
> -machine q35,kvm-type=tdx,pic=no,kernel_irqchip=split,memory-encryption=tdx,memory-backend=ram1
> 
> Changelog
> ----------
> v3:
>   - Added locking protection when calling
>     invalidate_page_range/fallocate callbacks.
>   - Changed memslot structure to keep use useraddr for shared memory.
>   - Re-organized F_SEAL_INACCESSIBLE and MEMFD_OPS.
>   - Added MFD_INACCESSIBLE flag to force F_SEAL_INACCESSIBLE.
>   - Commit message improvement.
>   - Many small fixes for comments from the last version.

Can you rebase on top of kvm/queue and send a new version?  There's a massive
overhaul of KVM's memslots code that's queued for 5.17, and the KVM core changes
in this series conflict mightily.

It's ok if the private memslot support isn't tested exactly as-is, it's not like
any of us reviewers can test it anyways, but I would like to be able to apply
cleanly and verify that the series doesn't break existing functionality.

This version also appears to be based on an internal development branch, e.g. patch
12/15 has some bits from the TDX series.

@@ -336,6 +348,7 @@ struct kvm_tdx_exit {
 #define KVM_EXIT_X86_BUS_LOCK     33
 #define KVM_EXIT_XEN              34
 #define KVM_EXIT_RISCV_SBI        35
+#define KVM_EXIT_MEMORY_ERROR     36
 #define KVM_EXIT_TDX              50   /* dump number to avoid conflict. */

 /* For KVM_EXIT_INTERNAL_ERROR */
@@ -554,6 +567,8 @@ struct kvm_run {
                        unsigned long args[6];
                        unsigned long ret[2];
                } riscv_sbi;
+               /* KVM_EXIT_MEMORY_ERROR */
+               struct kvm_memory_exit mem;
                /* KVM_EXIT_TDX_VMCALL */
                struct kvm_tdx_exit tdx;
                /* Fix the size of the union. */
Chao Peng Dec. 22, 2021, 1:22 a.m. UTC | #2
On Tue, Dec 21, 2021 at 03:44:40PM +0000, Sean Christopherson wrote:
> On Tue, Dec 21, 2021, Chao Peng wrote:
> > This is the third version of this series which try to implement the
> > fd-based KVM guest private memory.
> 
> ...
> 
> > Test
> > ----
> > This code has been tested with latest TDX code patches hosted at
> > (https://github.com/intel/tdx/tree/kvm-upstream) with minimal TDX
> > adaption and QEMU support.
> > 
> > Example QEMU command line:
> > -object tdx-guest,id=tdx \
> > -object memory-backend-memfd-private,id=ram1,size=2G \
> > -machine q35,kvm-type=tdx,pic=no,kernel_irqchip=split,memory-encryption=tdx,memory-backend=ram1
> > 
> > Changelog
> > ----------
> > v3:
> >   - Added locking protection when calling
> >     invalidate_page_range/fallocate callbacks.
> >   - Changed memslot structure to keep use useraddr for shared memory.
> >   - Re-organized F_SEAL_INACCESSIBLE and MEMFD_OPS.
> >   - Added MFD_INACCESSIBLE flag to force F_SEAL_INACCESSIBLE.
> >   - Commit message improvement.
> >   - Many small fixes for comments from the last version.
> 
> Can you rebase on top of kvm/queue and send a new version?  There's a massive
> overhaul of KVM's memslots code that's queued for 5.17, and the KVM core changes
> in this series conflict mightily.

Sure, will do the rebase and send a new version.

> 
> It's ok if the private memslot support isn't tested exactly as-is, it's not like
> any of us reviewers can test it anyways, but I would like to be able to apply
> cleanly and verify that the series doesn't break existing functionality.

Good, it will ease me if that is acceptable (e.g. test on the relative
new TDX codebase but send out the patch on latest kvm/queue which is not
verified for the new function). This gets rid of the 'chicken and egg'
dependency between this series and TDX patchset.

> 
> This version also appears to be based on an internal development branch, e.g. patch
> 12/15 has some bits from the TDX series.

Right, it's based on latest TDX code https://github.com/intel/tdx/tree/kvm-upstream.
I did this because this is the only way I can test the code. 

Thanks,
Chao
> 
> @@ -336,6 +348,7 @@ struct kvm_tdx_exit {
>  #define KVM_EXIT_X86_BUS_LOCK     33
>  #define KVM_EXIT_XEN              34
>  #define KVM_EXIT_RISCV_SBI        35
> +#define KVM_EXIT_MEMORY_ERROR     36
>  #define KVM_EXIT_TDX              50   /* dump number to avoid conflict. */
> 
>  /* For KVM_EXIT_INTERNAL_ERROR */
> @@ -554,6 +567,8 @@ struct kvm_run {
>                         unsigned long args[6];
>                         unsigned long ret[2];
>                 } riscv_sbi;
> +               /* KVM_EXIT_MEMORY_ERROR */
> +               struct kvm_memory_exit mem;
>                 /* KVM_EXIT_TDX_VMCALL */
>                 struct kvm_tdx_exit tdx;
>                 /* Fix the size of the union. */