From: Fred Griffoul <[email protected]>

Some drivers manage RAM outside the page allocator: a carve-out, device
memory, or memory that a host component moves between VMs while they
run. This memory often has no struct page. The driver wants to lend
pages to a VM and to its devices, and to take any of them back later.

David Woodhouse's series "KVM: Allow alternative providers of
guest_memfd backed by PFNMAP memory" lets such a driver back a
guest_memfd through struct kvm_gmem_ops:

  https://lore.kernel.org/kvm/[email protected]/

That covers the guest, but not the VM's devices, which his cover
letter left as an open question. This series adds a small interface in
mm: the driver that owns the memory becomes a provider, and the code
that maps it attaches as a consumer. There are two consumers, a
guest_memfd backend and IOMMU_IOAS_MAP_FILE in iommufd.

Why not dma-buf
===============

The earlier RFC shared the memory as a dma-buf. Christian König
rejected that: an importer must not build its own page tables from a
dma-buf, and the use case should stay out of drivers/dma-buf. I
acknowledged that and dropped it; this series does not touch dma-buf.

Why a new interface
===================

kvm_gmem_ops gives KVM a way in. For devices, the only option today is
a dma-buf plus a private call that iommufd looks up with symbol_get(),
which is how vfio-pci works. Each new owner would need another such
lookup, and iommufd would end up knowing each owner by name. With a
common interface, a provider module only calls into mm, and nothing
needs symbol_get().

It also means the provider revokes a range once. The VMM hands the
same file to KVM and to iommufd; on a revoke the core forwards it to
every consumer, so KVM clears the range from stage-2, iommufd from the
IOMMU page tables, and guest_memfd from the VMM's own mapping, all
before the revoke returns.

The contract
============

For a page of a provider file, get_page() returns the frame, its type
(RAM or MMIO, read-only or not), and the largest aligned block of frames
of the same type, so that consumers can use large mappings. A page
without a frame is a hole.

Consumers take no references. A frame stays valid until the revoke that
removes it returns. To guarantee that, a consumer either holds a lock
across get_page() and the mapping that its revoke callback also takes,
or detects a racing revoke and retries.

The interface does not deal with folios. The native guest_memfd backend
and memfd pinning in iommufd handle pages from the page allocator, and
pages that can move or swap would need references. A backend that needs
full control over the file can still write its own kvm_gmem_ops.

Backends
========

The motivating user is a host-side memory manager. It reserves a region
of host RAM, lends parts of it to VMs and their devices, moves pages
between VMs, and has to survive a live update of the host kernel. The
sample module in this series models it.

Device-DAX could also be a provider: its range never moves, get_page()
is short, and it only revokes on unbind. iommufd can map MMIO, but KVM
takes only RAM, because it picks the guest memory type itself.

Neither consumer keeps state about the provider's frames, which helps
with live update. guest_memfd has nothing to save across kexec, since
KVM refills stage-2 on faults. The provider saves its frames and who
owns them. Devices cannot fault, so their mappings stay part of the
existing iommufd live update work, and the provider only has to give
back the same frames.

Confidential VMs
================

This series does not support them yet. For now only x86 VMs of type
KVM_X86_DEFAULT_VM can use a provider. That type also covers legacy SEV
and SEV-ES guests set up with KVM_SEV_INIT: for them, a revoke and the
closing of the file write back the guest's caches, so that no stale
encrypted line reaches the frames once the provider reuses them.

For VMs with private memory, guest_memfd converts pages between shared
and private, so it should tell the other users what to zap. Only
guest_memfd knows which pages are private, and revoking a private page
loses its contents, so a provider should not take part in conversions.

The interface fits that model too. guest_memfd can be a provider for
its own file: it returns the frames of shared pages, reports private
pages as holes, and revokes a range that it converts to private.
iommufd then maps only shared pages, without pinning them, whether
guest_memfd allocates folios or takes its memory from a provider. In
the second case, guest_memfd forwards the provider's revokes. I left
that for a later series.

Patches
=======

  Patch 1 is David's: guest_memfd gets a per-file struct kvm_gmem_ops,
  and the native backend becomes one implementation of it.

  Patch 2 lets a kvm_gmem_ops backend map a page read-only for the
  guest. get_pfn() gets a writable output that it may clear, and a
  guest write to such a page exits with KVM_EXIT_MEMORY_FAULT.
  KVM_MEM_READONLY can't be used for this, because guest_memfd slots
  don't allow it.

  Patch 3 adds the interface and the core, and the FOP_MEM_PROVIDER
  flag in struct file_operations.

  Patch 4 adds the provider backend to guest_memfd, behind
  GUEST_MEMFD_FLAG_USE_RESOURCE. guest_memfd also handles the
  userspace mapping of the file, if the provider allows it, so
  providers don't each have to get the fault and revoke handling right.

  Patch 5 prepares iommufd for tracking pages it does not pin. No
  functional change.

  Patch 6 lets IOMMU_IOAS_MAP_FILE take a provider file. iommufd pins
  nothing and maps only what the provider backs; a revoke unmaps the
  range and maps it again. A scratch page for devices that cannot
  fault is left for a later IOAS quirk, as David suggested.

  Patch 7 maps each block the provider reports with large IOMMU pages
  where the IOVA and the frames are aligned alike, one call per page so
  that a revoke never splits a map call. A large IOMMU page can only be
  unmapped whole, so a revoke that cuts one unmaps and remaps all of it.

  Patches 8 and 9 add iommufd selftests: two mock-domain queries and a
  mock provider.

  Patch 10 adds a sample provider. It gives each VM a child file, can
  move, donate and reclaim pages, and supports read-only pages. A child
  file is read-only to its holder; the owner changes it through the
  control device. It only uses the mm interface.

  Patch 11 adds a KVM selftest with one provider and two VMMs. After
  each change it checks what the guest, the device and the VMM's mapping
  see. A third VM checks that KVM maps an aligned block with a 2 MiB
  page, and drops it when a page inside goes away.

Testing
=======

On x86_64 in QEMU, with KASAN and lockdep: mem_provider_test,
guest_memfd_test and the iommufd provider selftests pass, with the
mock IOMMU domain only. Each patch builds with KVM built in, as a
module, and with SEV. arm64 is not tested.

Changes since v1
================

v1: https://lore.kernel.org/all/[email protected]/

  - Sit on David's kvm_gmem_ops patch alone instead of his whole
    series; its revoke, iommufd exporter lookups, sample and selftests
    are replaced by this series.

  - Take the provider file through Ackerley Tng's
    GUEST_MEMFD_FLAG_USE_RESOURCE and resource_fd [1], as discussed
    with Aneesh and Ackerley. A file that is not from a provider fails
    with -EOPNOTSUPP.

  - Map the blocks the provider reports, so the IOMMU can use large
    pages, and flush the IOTLB once per revoke (patch 7).

[1] 
https://lore.kernel.org/all/[email protected]/

David Woodhouse (1):
  KVM: guest_memfd: Introduce guest memory ops and route native gmem
    through them

Fred Griffoul (10):
  KVM: guest_memfd: Let get_pfn() report read-only pages
  mm: Add memory providers
  KVM: guest_memfd: Add a memory provider backing
  iommufd: Track the domains of pages that are not pinned
  iommufd: Map memory provider files
  iommufd: Use large IOMMU pages for provider mappings
  iommufd/selftest: Add mock-domain IOVA queries
  iommufd/selftest: Add a mock memory provider
  samples/kvm: Add a memory provider sample
  KVM: selftests: Test a memory provider shared by KVM and iommufd

 Documentation/virt/kvm/api.rst                |  46 +-
 MAINTAINERS                                   |   8 +
 arch/arm64/kvm/mmu.c                          |  13 +-
 arch/arm64/kvm/nested.c                       |  16 +-
 arch/x86/kvm/Makefile                         |   5 +-
 arch/x86/kvm/mmu/mmu.c                        |  18 +-
 arch/x86/kvm/svm/sev.c                        |  13 +-
 arch/x86/kvm/x86.c                            |   9 +
 drivers/iommu/iommufd/Kconfig                 |   1 +
 drivers/iommu/iommufd/io_pagetable.c          |  37 +-
 drivers/iommu/iommufd/io_pagetable.h          |  58 +-
 drivers/iommu/iommufd/iommufd_test.h          |  40 +
 drivers/iommu/iommufd/pages.c                 | 435 ++++++++-
 drivers/iommu/iommufd/selftest.c              | 325 +++++++
 include/linux/fs.h                            |   2 +
 include/linux/kvm_host.h                      | 100 +-
 include/linux/mem_provider.h                  | 211 +++++
 include/uapi/linux/iommufd.h                  |  16 +-
 include/uapi/linux/kvm.h                      |  11 +-
 mm/Kconfig                                    |   7 +
 mm/Makefile                                   |   1 +
 mm/mem_provider.c                             | 177 ++++
 samples/Kconfig                               |  17 +
 samples/Makefile                              |   1 +
 samples/kvm/Makefile                          |   2 +
 samples/kvm/mem_provider_sample.c             | 855 ++++++++++++++++++
 samples/kvm/mem_provider_sample.h             | 135 +++
 tools/include/uapi/linux/kvm.h                |  11 +-
 tools/testing/selftests/iommu/iommufd.c       | 185 ++++
 tools/testing/selftests/iommu/iommufd_utils.h |  69 ++
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../testing/selftests/kvm/guest_memfd_test.c  |  15 +
 .../selftests/kvm/x86/mem_provider_test.c     | 653 +++++++++++++
 virt/kvm/Kconfig                              |   1 +
 virt/kvm/guest_memfd.c                        | 645 +++++++++++--
 virt/kvm/kvm_main.c                           |   8 +-
 virt/kvm/kvm_mm.h                             |   4 +-
 37 files changed, 3987 insertions(+), 164 deletions(-)
 create mode 100644 include/linux/mem_provider.h
 create mode 100644 mm/mem_provider.c
 create mode 100644 samples/kvm/Makefile
 create mode 100644 samples/kvm/mem_provider_sample.c
 create mode 100644 samples/kvm/mem_provider_sample.h
 create mode 100644 tools/testing/selftests/kvm/x86/mem_provider_test.c


base-commit: 0e35b9b6ec0ffcc5e23cbdec09f5c622ad532b53
-- 
2.47.3


Reply via email to