[PATCH v13 00/85] KVM: Stop grabbing references to PFNMAP'd pages

Sean Christopherson
2024-10-10T18:31:34Z

TL;DR: Eliminate KVM's long-standing (and heinous) behavior of essentially
guessing which pfns are refcounted pages (see kvm_pfn_to_refcounted_page()).

Getting there requires "fixing" arch code that isn't obviously broken.
Specifically, to get rid of kvm_pfn_to_refcounted_page(), KVM needs to
stop marking pages/folios dirty/accessed based solely on the pfn that's
stored in KVM's stage-2 page tables.

Instead of tracking which SPTEs correspond to refcounted pages, simply
remove all of the code that operates on "struct page" based ona the pfn
in stage-2 PTEs. This is the back ~40-50% of the series.

For x86 in particular, which sets accessed/dirty status when that info
would be "lost", e.g. when SPTEs are zapped or KVM clears the dirty flag
in a SPTE, foregoing the updates provides very measurable performance
improvements for related operations. E.g. when clearing dirty bits as
part of dirty logging, and zapping SPTEs to reconstitue huge pages when
disabling dirty logging.

The front ~40% of the series is cleanups and prep work, and most of it is
x86 focused (purely because x86 added the most special cases, *sigh*).
E.g. several of the inputs to hva_to_pfn() (and it's myriad wrappers),
can be removed by cleaning up and deduplicating x86 code.

v13:
- Rebased onto v6.12-rc2
- Collect reviews. [Alex and others]
- Fix a transient bug in arm64 and RISC-V where KVM would leak a page
refcount. [Oliver]
- Fix a dangling comment. [Alex]
- Drop kvm_lookup_pfn(), as the x86 that "needed" it was stupid and is (was?)
eliminated in v6.12.
- Drop check_user_page_hwpoison(). [Paolo]
- Drop the arm64 MTE fixes that went into 6.12.
- Slightly redo the guest_memfd interaction to account for 6.12 changes.

v12: https://lore.kernel.org/all/20240726235234.228822-1-seanjc@google.com

David Stevens (3):
KVM: Replace "async" pointer in gfn=>pfn with "no_wait" and error code
KVM: Introduce kvm_follow_pfn() to eventually replace "gfn_to_pfn"
APIs
KVM: Migrate kvm_vcpu_map() to kvm_follow_pfn()

Sean Christopherson (82):
KVM: Drop KVM_ERR_PTR_BAD_PAGE and instead return NULL to indicate an
error
KVM: Allow calling kvm_release_page_{clean,dirty}() on a NULL page
pointer
KVM: Add kvm_release_page_unused() API to put pages that KVM never
consumes
KVM: x86/mmu: Skip the "try unsync" path iff the old SPTE was a leaf
SPTE
KVM: x86/mmu: Don't overwrite shadow-present MMU SPTEs when
prefaulting
KVM: x86/mmu: Invert @can_unsync and renamed to @synchronizing
KVM: x86/mmu: Mark new SPTE as Accessed when synchronizing existing
SPTE
KVM: x86/mmu: Mark folio dirty when creating SPTE, not when
zapping/modifying
KVM: x86/mmu: Mark page/folio accessed only when zapping leaf SPTEs
KVM: x86/mmu: Use gfn_to_page_many_atomic() when prefetching indirect
PTEs
KVM: Rename gfn_to_page_many_atomic() to kvm_prefetch_pages()
KVM: Drop @atomic param from gfn=>pfn and hva=>pfn APIs
KVM: Annotate that all paths in hva_to_pfn() might sleep
KVM: Return ERR_SIGPENDING from hva_to_pfn() if GUP returns -EGAIN
KVM: Drop extra GUP (via check_user_page_hwpoison()) to detect
poisoned page
KVM: x86/mmu: Drop kvm_page_fault.hva, i.e. don't track intermediate
hva
KVM: Drop unused "hva" pointer from __gfn_to_pfn_memslot()
KVM: Remove pointless sanity check on @map param to kvm_vcpu_(un)map()
KVM: Explicitly initialize all fields at the start of kvm_vcpu_map()
KVM: Use NULL for struct page pointer to indicate mremapped memory
KVM: nVMX: Rely on kvm_vcpu_unmap() to track validity of eVMCS mapping
KVM: nVMX: Drop pointless msr_bitmap_map field from struct nested_vmx
KVM: nVMX: Add helper to put (unmap) vmcs12 pages
KVM: Use plain "struct page" pointer instead of single-entry array
KVM: Provide refcounted page as output field in struct kvm_follow_pfn
KVM: Move kvm_{set,release}_page_{clean,dirty}() helpers up in
kvm_main.c
KVM: pfncache: Precisely track refcounted pages
KVM: Pin (as in FOLL_PIN) pages during kvm_vcpu_map()
KVM: nVMX: Mark vmcs12's APIC access page dirty when unmapping
KVM: Pass in write/dirty to kvm_vcpu_map(), not kvm_vcpu_unmap()
KVM: Get writable mapping for __kvm_vcpu_map() only when necessary
KVM: Disallow direct access (w/o mmu_notifier) to unpinned pfn by
default
KVM: x86: Don't fault-in APIC access page during initial allocation
KVM: x86/mmu: Add "mmu" prefix fault-in helpers to free up generic
names
KVM: x86/mmu: Put direct prefetched pages via kvm_release_page_clean()
KVM: x86/mmu: Add common helper to handle prefetching SPTEs
KVM: x86/mmu: Add helper to "finish" handling a guest page fault
KVM: x86/mmu: Mark pages/folios dirty at the origin of make_spte()
KVM: Move declarations of memslot accessors up in kvm_host.h
KVM: Add kvm_faultin_pfn() to specifically service guest page faults
KVM: x86/mmu: Convert page fault paths to kvm_faultin_pfn()
KVM: guest_memfd: Pass index, not gfn, to __kvm_gmem_get_pfn()
KVM: guest_memfd: Provide "struct page" as output from
kvm_gmem_get_pfn()
KVM: x86/mmu: Put refcounted pages instead of blindly releasing pfns
KVM: x86/mmu: Don't mark unused faultin pages as accessed
KVM: Move x86's API to release a faultin page to common KVM
KVM: VMX: Hold mmu_lock until page is released when updating APIC
access page
KVM: VMX: Use __kvm_faultin_page() to get APIC access page/pfn
KVM: PPC: e500: Mark "struct page" dirty in kvmppc_e500_shadow_map()
KVM: PPC: e500: Mark "struct page" pfn accessed before dropping
mmu_lock
KVM: PPC: e500: Use __kvm_faultin_pfn() to handle page faults
KVM: arm64: Mark "struct page" pfns accessed/dirty before dropping
mmu_lock
KVM: arm64: Use __kvm_faultin_pfn() to handle memory aborts
KVM: RISC-V: Mark "struct page" pfns dirty iff a stage-2 PTE is
installed
KVM: RISC-V: Mark "struct page" pfns accessed before dropping mmu_lock
KVM: RISC-V: Use kvm_faultin_pfn() when mapping pfns into the guest
KVM: PPC: Use __kvm_faultin_pfn() to handle page faults on Book3s HV
KVM: PPC: Use __kvm_faultin_pfn() to handle page faults on Book3s
Radix
KVM: PPC: Drop unused @kvm_ro param from
kvmppc_book3s_instantiate_page()
KVM: PPC: Book3S: Mark "struct page" pfns dirty/accessed after
installing PTE
KVM: PPC: Use kvm_faultin_pfn() to handle page faults on Book3s PR
KVM: LoongArch: Mark "struct page" pfns dirty only in "slow" page
fault path
KVM: LoongArch: Mark "struct page" pfns accessed only in "slow" page
fault path
KVM: LoongArch: Mark "struct page" pfn accessed before dropping
mmu_lock
KVM: LoongArch: Use kvm_faultin_pfn() to map pfns into the guest
KVM: MIPS: Mark "struct page" pfns dirty only in "slow" page fault
path
KVM: MIPS: Mark "struct page" pfns accessed only in "slow" page fault
path
KVM: MIPS: Mark "struct page" pfns accessed prior to dropping mmu_lock
KVM: MIPS: Use kvm_faultin_pfn() to map pfns into the guest
KVM: PPC: Remove extra get_page() to fix page refcount leak
KVM: PPC: Use kvm_vcpu_map() to map guest memory to patch dcbz
instructions
KVM: Convert gfn_to_page() to use kvm_follow_pfn()
KVM: Add support for read-only usage of gfn_to_page()
KVM: arm64: Use __gfn_to_page() when copying MTE tags to/from
userspace
KVM: PPC: Explicitly require struct page memory for Ultravisor sharing
KVM: Drop gfn_to_pfn() APIs now that all users are gone
KVM: s390: Use kvm_release_page_dirty() to unpin "struct page" memory
KVM: Make kvm_follow_pfn.refcounted_page a required field
KVM: x86/mmu: Don't mark "struct page" accessed when zapping SPTEs
KVM: arm64: Don't mark "struct page" accessed when making SPTE young
KVM: Drop APIs that manipulate "struct page" via pfns
KVM: Don't grab reference on VM_MIXEDMAP pfns that have a "struct
page"

Documentation/virt/kvm/locking.rst | 80 ++--
arch/arm64/include/asm/kvm_pgtable.h | 4 +-
arch/arm64/kvm/guest.c | 21 +-
arch/arm64/kvm/hyp/pgtable.c | 7 +-
arch/arm64/kvm/mmu.c | 21 +-
arch/loongarch/kvm/mmu.c | 40 +-
arch/mips/kvm/mmu.c | 26 +-
arch/powerpc/include/asm/kvm_book3s.h | 4 +-
arch/powerpc/kvm/book3s.c | 7 +-
arch/powerpc/kvm/book3s_32_mmu_host.c | 7 +-
arch/powerpc/kvm/book3s_64_mmu_host.c | 12 +-
arch/powerpc/kvm/book3s_64_mmu_hv.c | 25 +-
arch/powerpc/kvm/book3s_64_mmu_radix.c | 35 +-
arch/powerpc/kvm/book3s_hv_nested.c | 4 +-
arch/powerpc/kvm/book3s_hv_uvmem.c | 25 +-
arch/powerpc/kvm/book3s_pr.c | 14 +-
arch/powerpc/kvm/book3s_xive_native.c | 2 +-
arch/powerpc/kvm/e500_mmu_host.c | 19 +-
arch/riscv/kvm/mmu.c | 9 +-
arch/s390/kvm/vsie.c | 4 +-
arch/x86/kvm/lapic.c | 12 -
arch/x86/kvm/mmu/mmu.c | 181 ++++----
arch/x86/kvm/mmu/mmu_internal.h | 7 +-
arch/x86/kvm/mmu/paging_tmpl.h | 31 +-
arch/x86/kvm/mmu/spte.c | 31 +-
arch/x86/kvm/mmu/spte.h | 2 +-
arch/x86/kvm/mmu/tdp_mmu.c | 23 +-
arch/x86/kvm/svm/nested.c | 4 +-
arch/x86/kvm/svm/sev.c | 12 +-
arch/x86/kvm/svm/svm.c | 8 +-
arch/x86/kvm/vmx/nested.c | 42 +-
arch/x86/kvm/vmx/vmx.c | 28 +-
arch/x86/kvm/vmx/vmx.h | 2 -
include/linux/kvm_host.h | 123 +++--
virt/kvm/guest_memfd.c | 28 +-
virt/kvm/kvm_main.c | 602 +++++++++----------------
virt/kvm/kvm_mm.h | 36 +-
virt/kvm/pfncache.c | 20 +-
38 files changed, 689 insertions(+), 869 deletions(-)

base-commit: 8cf0b93919e13d1e8d4466eb4080a4c4d9d66d7b