KVM: allow mapping non-refcounted pages
v4 cover KVM: allow mapping non-refcounted pages
From: David Stevens <[email protected]>
This patch series adds support for mapping non-refcount VM_IO and VM_PFNMAP memory into the guest.
Currently, the gfn_to_pfn functions require being able to pin the target pfn, so they will fail if the pfn returned by follow_pte isn't a ref-counted page. However, the KVM secondary MMUs do not require that the pfn be pinned, since they are integrated with the mmu notifier API. This series adds a new set of gfn_to_pfn_page functions which parallel the gfn_to_pfn functions but do not pin the pfn. The new functions return the page from gup if it was present, so callers can use it and call put_page when done.
The gfn_to_pfn functions should be depreciated, since as they are unsafe due to relying on trying to obtain a struct page from a pfn returned by follow_pte. I added new functions instead of simply adding another optional parameter to the existing functions to make it easier to track down users of the deprecated functions.
This series updates x86 and arm64 secondary MMUs to the new API.
v3 -> v4:
- rebase on kvm next branch again
- Add some more context to a comment in ensure_pfn_ref
v2 -> v3:
- rebase on kvm next branch
v1 -> v2:
- Introduce new gfn_to_pfn_page functions instead of modifying the behavior of existing gfn_to_pfn functions, to make the change less invasive.
- Drop changes to mmu_audit.c
- Include Nicholas Piggin's patch to avoid corrupting refcount in the follow_pte case, and use it in depreciated gfn_to_pfn functions.
- Rebase on kvm/next
David Stevens (4):
KVM: mmu: introduce new gfn_to_pfn_page functions
KVM: x86/mmu: use gfn_to_pfn_page
KVM: arm64/mmu: use gfn_to_pfn_page
KVM: mmu: remove over-aggressive warnings
arch/arm64/kvm/mmu.c | 27 +++-- arch/x86/kvm/mmu/mmu.c | 50 ++++---- arch/x86/kvm/mmu/mmu_internal.h | 3 +- arch/x86/kvm/mmu/paging_tmpl.h | 23 ++-- arch/x86/kvm/mmu/tdp_mmu.c | 6 +- arch/x86/kvm/mmu/tdp_mmu.h | 4 +- arch/x86/kvm/x86.c | 6 +- include/linux/kvm_host.h | 17 +++ virt/kvm/kvm_main.c | 198 +++++++++++++++++++++++--------- 9 files changed, 231 insertions(+), 103 deletions(-)
Signature · 1 line
2.33.0.685.g46640cef36-goog
PATCH v4 1/4 KVM: mmu: introduce new gfn_to_pfn_page functions
From: David Stevens <[email protected]>
Introduce new gfn_to_pfn_page functions that parallel existing gfn_to_pfn functions. The new functions are identical except they take an additional out parameter that is used to return the struct page if the hva was resolved by gup. This allows callers to differentiate the gup and follow_pte cases, which in turn allows callers to only touch the page refcount when necessitated by gup.
The old gfn_to_pfn functions are depreciated, and all callers should be migrated to the new gfn_to_pfn_page functions. In the interim, the gfn_to_pfn functions are reimplemented as wrappers of the corresponding gfn_to_pfn_page functions. The wrappers take a reference to the pfn's page that had previously been taken in hva_to_pfn_remapped.
--- include/linux/kvm_host.h | 17 ++++ virt/kvm/kvm_main.c | 196 +++++++++++++++++++++++++++++---------- 2 files changed, 162 insertions(+), 51 deletions(-)
include/linux/kvm_host.h +17 -0
@@ -868,6 +868,19 @@ kvm_pfn_t __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool atomic, bool *async, bool write_fault, bool *writable, hva_t *hva); +kvm_pfn_t gfn_to_pfn_page (struct kvm *kvm,gfn_t gfn,struct page **page); +kvm_pfn_t gfn_to_pfn_page_prot (struct kvm *kvm,gfn_t gfn, +bool write_fault,bool *writable, +struct page **page); +kvm_pfn_t gfn_to_pfn_page_memslot (struct kvm_memory_slot *slot, +gfn_t gfn,struct page **page); +kvm_pfn_t gfn_to_pfn_page_memslot_atomic (struct kvm_memory_slot *slot, +gfn_t gfn,struct page **page); +kvm_pfn_t __gfn_to_pfn_page_memslot (struct kvm_memory_slot *slot, +gfn_t gfn,bool atomic,bool *async, +bool write_fault,bool *writable, +hva_t *hva,struct page **page); +void kvm_release_pfn_clean (kvm_pfn_t pfn);void kvm_release_pfn_dirty (kvm_pfn_t pfn);void kvm_set_pfn_dirty (kvm_pfn_t pfn);
@@ -948,6 +961,10 @@ struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu);struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot (struct kvm_vcpu *vcpu,gfn_t gfn);kvm_pfn_t kvm_vcpu_gfn_to_pfn_atomic (struct kvm_vcpu *vcpu,gfn_t gfn);kvm_pfn_t kvm_vcpu_gfn_to_pfn (struct kvm_vcpu *vcpu,gfn_t gfn); +kvm_pfn_t kvm_vcpu_gfn_to_pfn_page_atomic (struct kvm_vcpu *vcpu,gfn_t gfn, +struct page **page); +kvm_pfn_t kvm_vcpu_gfn_to_pfn_page (struct kvm_vcpu *vcpu,gfn_t gfn, +struct page **page);int kvm_vcpu_map (struct kvm_vcpu *vcpu,gpa_t gpa,struct kvm_host_map *map);int kvm_map_gfn (struct kvm_vcpu *vcpu,gfn_t gfn,struct kvm_host_map *map,struct gfn_to_pfn_cache *cache,bool atomic);
virt/kvm/kvm_main.c +145 -51
306 lines not on this page · raw message
Signature · 1 line
2.33.0.685.g46640cef36-goog
PATCH v4 2/4 KVM: x86/mmu: use gfn_to_pfn_page
In reply to David StevensFrom: David Stevens <[email protected]>
Covert usages of the deprecated gfn_to_pfn functions to the new gfn_to_pfn_page functions.
--- arch/x86/kvm/mmu/mmu.c | 43 ++++++++++++++++++++------------- arch/x86/kvm/mmu/mmu_internal.h | 3 ++- arch/x86/kvm/mmu/paging_tmpl.h | 23 +++++++++++------- arch/x86/kvm/mmu/tdp_mmu.c | 6 ++--- arch/x86/kvm/mmu/tdp_mmu.h | 4 +-- arch/x86/kvm/x86.c | 6 +++-- 6 files changed, 51 insertions(+), 34 deletions(-)
arch/x86/kvm/mmu/mmu.c +26 -17
@@ -2783,8 +2783,9 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,return ret; } -static kvm_pfn_t pte_prefetch_gfn_to_pfn (struct kvm_vcpu *vcpu,gfn_t gfn, -bool no_dirty_log) +static kvm_pfn_t pte_prefetch_gfn_to_pfn_page (struct kvm_vcpu *vcpu, +gfn_t gfn,bool no_dirty_log, +struct page **page) {struct kvm_memory_slot *slot;
@@ -2792,7 +2793,7 @@ static kvm_pfn_t pte_prefetch_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn,if (!slot)return KVM_PFN_ERR_FAULT ; -return gfn_to_pfn_memslot_atomic (slot, gfn); +return gfn_to_pfn_page_memslot_atomic (slot, gfn, page); } static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu,
@@ -2923,7 +2924,8 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm,int kvm_mmu_hugepage_adjust (struct kvm_vcpu *vcpu,gfn_t gfn,int max_level,kvm_pfn_t *pfnp, -bool huge_page_disallowed,int *req_level) +struct page *page,bool huge_page_disallowed, +int *req_level) {struct kvm_memory_slot *slot;kvm_pfn_t pfn = *pfnp;
@@ -2935,6 +2937,9 @@ int kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, gfn_t gfn,if (unlikely (max_level ==PG_LEVEL_4K ))return PG_LEVEL_4K ; +if (!page) +return PG_LEVEL_4K ; +if (is_error_noslot_pfn (pfn) ||kvm_is_reserved_pfn (pfn))return PG_LEVEL_4K ;
@@ -2984,7 +2989,8 @@ void disallowed_hugepage_adjust(u64 spte, gfn_t gfn, int cur_level, }static int __direct_map (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code, -int map_writable,int max_level,kvm_pfn_t pfn, +int map_writable,int max_level, +kvm_pfn_t pfn,struct page *page,bool prefault,bool is_tdp) {bool nx_huge_page_workaround_enabled =is_nx_huge_page_enabled ();
@@ -2997,7 +3003,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,gfn_t gfn = gpa >>PAGE_SHIFT ;gfn_t base_gfn = gfn; - level =kvm_mmu_hugepage_adjust (vcpu, gfn, max_level, &pfn, + level =kvm_mmu_hugepage_adjust (vcpu, gfn, max_level, &pfn, page, huge_page_disallowed, &req_level);trace_kvm_mmu_spte_requested (gpa, level, pfn);
@@ -3901,8 +3907,9 @@ static bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, }static bool kvm_faultin_pfn (struct kvm_vcpu *vcpu,bool prefault,gfn_t gfn, -gpa_t cr2_or_gpa,kvm_pfn_t *pfn,hva_t *hva, -bool write,bool *writable,int *r) +gpa_t cr2_or_gpa,kvm_pfn_t *pfn, +hva_t *hva,bool write,bool *writable, +struct page **page,int *r) {struct kvm_memory_slot *slot =kvm_vcpu_gfn_to_memslot (vcpu, gfn);bool async;
@@ -3936,8 +3943,8 @@ static bool kvm_faultin_pfn(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, } async = false; - *pfn =__gfn_to_pfn_memslot (slot, gfn, false, &async, - write, writable, hva); + *pfn =__gfn_to_pfn_page_memslot (slot, gfn, false, &async, + write, writable, hva, page);if (!async)return false;/* *pfn has correct page already */
@@ -3951,8 +3958,8 @@ static bool kvm_faultin_pfn(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn, goto out_retry; } - *pfn =__gfn_to_pfn_memslot (slot, gfn, false,NULL , - write, writable, hva); + *pfn =__gfn_to_pfn_page_memslot (slot, gfn, false,NULL , + write, writable, hva, page); out_retry: *r =RET_PF_RETRY ;
@@ -3969,6 +3976,7 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,gfn_t gfn = gpa >>PAGE_SHIFT ;unsigned long mmu_seq;kvm_pfn_t pfn; +struct page *page;hva_t hva;int r;
@@ -3987,7 +3995,7 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,smp_rmb ();if (kvm_faultin_pfn (vcpu, prefault, gfn, gpa, &pfn, &hva, - write, &map_writable, &r)) + write, &map_writable, &page, &r))return r;if (handle_abnormal_pfn (vcpu, is_tdp ?0 : gpa, gfn, pfn,ACC_ALL , &r))
@@ -4008,17 +4016,18 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,if (is_tdp_mmu_fault) r =kvm_tdp_mmu_map (vcpu, gpa, error_code, map_writable, max_level, - pfn, prefault); + pfn, page, prefault);else - r =__direct_map (vcpu, gpa, error_code, map_writable, max_level, pfn, - prefault, is_tdp); + r =__direct_map (vcpu, gpa, error_code, map_writable, max_level, + pfn, page, prefault, is_tdp); out_unlock:if (is_tdp_mmu_fault)read_unlock (&vcpu->kvm->mmu_lock);else write_unlock (&vcpu->kvm->mmu_lock); -kvm_release_pfn_clean (pfn); +if (page) +put_page (page);return r; }
arch/x86/kvm/mmu/mmu_internal.h +2 -1
@@ -165,7 +165,8 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm, kvm_pfn_t pfn, int max_level);int kvm_mmu_hugepage_adjust (struct kvm_vcpu *vcpu,gfn_t gfn,int max_level,kvm_pfn_t *pfnp, -bool huge_page_disallowed,int *req_level); +struct page *page,bool huge_page_disallowed, +int *req_level);void disallowed_hugepage_adjust (u64 spte,gfn_t gfn,int cur_level,kvm_pfn_t *pfnp,int *goal_levelp);
arch/x86/kvm/mmu/paging_tmpl.h +14 -9
@@ -564,6 +564,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,unsigned pte_access;gfn_t gfn;kvm_pfn_t pfn; +struct page *page;if (FNAME (prefetch_invalid_gpte)(vcpu, sp, spte, gpte))return false;
@@ -573,8 +574,8 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, gfn =gpte_to_gfn (gpte); pte_access = sp->role.access &FNAME (gpte_access)(gpte);FNAME (protect_clean_gpte)(vcpu->arch.mmu, &pte_access, gpte); - pfn =pte_prefetch_gfn_to_pfn (vcpu, gfn, - no_dirty_log && (pte_access &ACC_WRITE_MASK )); + pfn =pte_prefetch_gfn_to_pfn_page (vcpu, gfn, + no_dirty_log && (pte_access &ACC_WRITE_MASK ), &page);if (is_error_pfn (pfn))return false;
@@ -585,7 +586,8 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,mmu_set_spte (vcpu, spte, pte_access, false,PG_LEVEL_4K , gfn, pfn, true, true); -kvm_release_pfn_clean (pfn); +if (page) +put_page (page);return true; }
@@ -665,8 +667,8 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,*/ static int FNAME (fetch )(struct kvm_vcpu *vcpu,gpa_t addr,struct guest_walker *gw,u32 error_code, -int max_level,kvm_pfn_t pfn,bool map_writable, -bool prefault) +int max_level,kvm_pfn_t pfn,struct page *page, +bool map_writable,bool prefault) {bool nx_huge_page_workaround_enabled =is_nx_huge_page_enabled ();bool write_fault = error_code &PFERR_WRITE_MASK ;
@@ -723,7 +725,8 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gpa_t addr, } level =kvm_mmu_hugepage_adjust (vcpu, gw->gfn, max_level, &pfn, - huge_page_disallowed, &req_level); + page, huge_page_disallowed, + &req_level);trace_kvm_mmu_spte_requested (addr, gw->level, pfn);
@@ -830,6 +833,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gpa_t addr, u32 error_code,struct guest_walker walker;int r;kvm_pfn_t pfn; +struct page *page;hva_t hva;unsigned long mmu_seq;bool map_writable, is_self_change_mapping;
@@ -882,7 +886,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gpa_t addr, u32 error_code,smp_rmb ();if (kvm_faultin_pfn (vcpu, prefault, walker.gfn, addr, &pfn, &hva, - write_fault, &map_writable, &r)) + write_fault, &map_writable, &page, &r))return r;if (handle_abnormal_pfn (vcpu, addr, walker.gfn, pfn, walker.pte_access, &r))
@@ -916,13 +920,14 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gpa_t addr, u32 error_code, r =make_mmu_pages_available (vcpu);if (r) goto out_unlock; - r =FNAME (fetch)(vcpu, addr, &walker, error_code, max_level, pfn, + r =FNAME (fetch)(vcpu, addr, &walker, error_code, max_level, pfn, page, map_writable, prefault);kvm_mmu_audit (vcpu,AUDIT_POST_PAGE_FAULT ); out_unlock:write_unlock (&vcpu->kvm->mmu_lock); -kvm_release_pfn_clean (pfn); +if (page) +put_page (page);return r; }
arch/x86/kvm/mmu/tdp_mmu.c +3 -3
@@ -987,8 +987,8 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu, int write,* page tables and SPTEs to translate the faulting guest physical address. */ int kvm_tdp_mmu_map (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code, -int map_writable,int max_level,kvm_pfn_t pfn, -bool prefault) +int map_writable,int max_level, +kvm_pfn_t pfn,struct page *page,bool prefault) {bool nx_huge_page_workaround_enabled =is_nx_huge_page_enabled ();bool write = error_code &PFERR_WRITE_MASK ;
@@ -1004,7 +1004,7 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,int level;int req_level; - level =kvm_mmu_hugepage_adjust (vcpu, gfn, max_level, &pfn, + level =kvm_mmu_hugepage_adjust (vcpu, gfn, max_level, &pfn, page, huge_page_disallowed, &req_level);trace_kvm_mmu_spte_requested (gpa, level, pfn);
arch/x86/kvm/mmu/tdp_mmu.h +2 -2
@@ -49,8 +49,8 @@ void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm);void kvm_tdp_mmu_zap_invalidated_roots (struct kvm *kvm);int kvm_tdp_mmu_map (struct kvm_vcpu *vcpu,gpa_t gpa,u32 error_code, -int map_writable,int max_level,kvm_pfn_t pfn, -bool prefault); +int map_writable,int max_level, +kvm_pfn_t pfn,struct page *page,bool prefault);bool kvm_tdp_mmu_unmap_gfn_range (struct kvm *kvm,struct kvm_gfn_range *range,bool flush);
arch/x86/kvm/x86.c +4 -2
@@ -7527,6 +7527,7 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, {gpa_t gpa = cr2_or_gpa;kvm_pfn_t pfn; +struct page *page;if (!(emulation_type &EMULTYPE_ALLOW_RETRY_PF ))return false;
@@ -7556,7 +7557,7 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,* retry instruction -> write #PF -> emulation fail -> retry * instruction -> ... */ - pfn =gfn_to_pfn (vcpu->kvm,gpa_to_gfn (gpa)); + pfn =gfn_to_pfn_page (vcpu->kvm,gpa_to_gfn (gpa), &page);/* * If the instruction failed on the error pfn, it can not be fixed,
@@ -7565,7 +7566,8 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,if (is_error_noslot_pfn (pfn))return false; -kvm_release_pfn_clean (pfn); +if (page) +put_page (page);/* The instructions are well-emulated on direct mmu. */ if (vcpu->arch.mmu->direct_map) {
Signature · 1 line
2.33.0.685.g46640cef36-goog
PATCH v4 3/4 KVM: arm64/mmu: use gfn_to_pfn_page
In reply to David StevensFrom: David Stevens <[email protected]>
Covert usages of the deprecated gfn_to_pfn functions to the new gfn_to_pfn_page functions.
--- arch/arm64/kvm/mmu.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
arch/arm64/kvm/mmu.c +17 -10
@@ -829,7 +829,7 @@ static bool fault_supports_stage2_huge_mapping(struct kvm_memory_slot *memslot,static unsigned long transparent_hugepage_adjust (struct kvm *kvm,struct kvm_memory_slot *memslot,unsigned long hva,kvm_pfn_t *pfnp, -phys_addr_t *ipap) +struct page **page,phys_addr_t *ipap) {kvm_pfn_t pfn = *pfnp;
@@ -838,7 +838,8 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,* sure that the HVA and IPA are sufficiently aligned and that the * block map is contained within the memslot. */ -if (fault_supports_stage2_huge_mapping (memslot, hva,PMD_SIZE ) && +if (*page && +fault_supports_stage2_huge_mapping (memslot, hva,PMD_SIZE ) &&get_user_mapping_size (kvm, hva) >=PMD_SIZE ) {/* * The address we faulted on is backed by a transparent huge
@@ -859,10 +860,11 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,* page accordingly. */ *ipap &=PMD_MASK ; -kvm_release_pfn_clean (pfn); +put_page (*page); pfn &= ~(PTRS_PER_PMD -1 ); -get_page (pfn_to_page (pfn)); *pfnp = pfn; + *page =pfn_to_page (pfn); +get_page (*page);return PMD_SIZE ; }
@@ -955,6 +957,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,short vma_shift;gfn_t gfn;kvm_pfn_t pfn; +struct page *page;bool logging_active =memslot_is_logging (memslot);unsigned long fault_level =kvm_vcpu_trap_get_fault_level (vcpu);unsigned long vma_pagesize, fault_granule;
@@ -1056,8 +1059,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,*/ smp_rmb (); - pfn =__gfn_to_pfn_memslot (memslot, gfn, false,NULL , - write_fault, &writable,NULL ); + pfn =__gfn_to_pfn_page_memslot (memslot, gfn, false,NULL , + write_fault, &writable,NULL , &page);if (pfn ==KVM_PFN_ERR_HWPOISON ) {kvm_send_hwpoison_signal (hva, vma_shift);return 0 ;
@@ -1102,7 +1105,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, vma_pagesize = fault_granule; else vma_pagesize =transparent_hugepage_adjust (kvm, memslot, - hva, &pfn, + hva, + &pfn, &page, &fault_ipa); }
@@ -1142,14 +1146,17 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,/* Mark the page dirty only if the fault is handled successfully */ if (writable && !ret) { -kvm_set_pfn_dirty (pfn); +if (page) +kvm_set_pfn_dirty (pfn);mark_page_dirty_in_slot (kvm, memslot, gfn); } out_unlock:spin_unlock (&kvm->mmu_lock); -kvm_set_pfn_accessed (pfn); -kvm_release_pfn_clean (pfn); +if (page) { +kvm_set_pfn_accessed (pfn); +put_page (page); + }return ret != -EAGAIN ? ret :0 ; }
Signature · 1 line
2.33.0.685.g46640cef36-goog
PATCH v4 4/4 KVM: mmu: remove over-aggressive warnings
In reply to David StevensFrom: David Stevens <[email protected]>
Remove two warnings that require ref counts for pages to be non-zero, as mapped pfns from follow_pfn may not have an initialized ref count.
--- arch/x86/kvm/mmu/mmu.c | 7 ------- virt/kvm/kvm_main.c | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-)
arch/x86/kvm/mmu/mmu.c +0 -7
@@ -617,13 +617,6 @@ static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) pfn =spte_to_pfn (old_spte); -/* -* KVM does not hold the refcount of the page used by -* kvm mmu, before reclaiming the page, we should -* unmap it from mmu first. -*/ -WARN_ON (!kvm_is_reserved_pfn (pfn) && !page_count (pfn_to_page (pfn))); -if (is_accessed_spte (old_spte))kvm_set_pfn_accessed (pfn);
virt/kvm/kvm_main.c +1 -1
@@ -168,7 +168,7 @@ bool kvm_is_zone_device_pfn(kvm_pfn_t pfn)* the device has been pinned, e.g. by get_user_pages(). WARN if the * page_count() is zero to help detect bad usage of this helper. */ -if (!pfn_valid (pfn) ||WARN_ON_ONCE (!page_count (pfn_to_page (pfn)))) +if (!pfn_valid (pfn) || !page_count (pfn_to_page (pfn)))return false;return is_zone_device_page (pfn_to_page (pfn));
Signature · 1 line
2.33.0.685.g46640cef36-goog
Sean Christopherson
maintainerRe: [PATCH v4 4/4] KVM: mmu: remove over-aggressive warnings
Quoted 16 lines · David Stevens
From: David Stevens <[email protected]>
Remove two warnings that require ref counts for pages to be non-zero, as mapped pfns from
follow_pfnmay not have an initialized ref count.Signed-off-by David Stevens <[email protected]>--- arch/x86/kvm/mmu/mmu.c | 7 ------- virt/kvm/kvm_main.c | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-)arch/x86/kvm/mmu/mmu.c +0
-0
arch/x86/kvm/mmu/mmu.c static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) · line 625
Quoted 9 lines
arch/x86/kvm/mmu/mmu.c +0
-6@@ -617,13 +617,6 @@ static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) pfn =spte_to_pfn (old_spte); -/* -* KVM does not hold the refcount of the page used by -* kvm mmu, before reclaiming the page, we should -* unmap it from mmu first. -*/ -WARN_ON (!kvm_is_reserved_pfn (pfn) && !page_count (pfn_to_page (pfn)));
Have you actually observed false positives with this WARN? I would expect anything without a struct page to get filtered out by !kvm_is_reserved_pfn(pfn).
If you have observed false positives, I would strongly prefer we find a way to keep the page_count() sanity check, it has proven very helpful in the past in finding/debugging bugs during MMU development.
Quoted 4 lines
arch/x86/kvm/mmu/mmu.c +0
-1-if (is_accessed_spte (old_spte))kvm_set_pfn_accessed (pfn);
Re: [PATCH v4 4/4] KVM: mmu: remove over-aggressive warnings
Quoted 21 lines · Sean Christopherson
Quoted 19 lines · David Stevens
From: David Stevens <[email protected]>
Remove two warnings that require ref counts for pages to be non-zero, as mapped pfns from
follow_pfnmay not have an initialized ref count.Signed-off-by David Stevens <[email protected]>--- arch/x86/kvm/mmu/mmu.c | 7 ------- virt/kvm/kvm_main.c | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-)arch/x86/kvm/mmu/mmu.c +0
-0@@ -617,13 +617,6 @@ static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) pfn =spte_to_pfn (old_spte);
Quoted 9 lines
arch/x86/kvm/mmu/mmu.c static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) · line 625
Quoted 6 lines
arch/x86/kvm/mmu/mmu.c +0
-6-/* -* KVM does not hold the refcount of the page used by -* kvm mmu, before reclaiming the page, we should -* unmap it from mmu first. -*/ -WARN_ON (!kvm_is_reserved_pfn (pfn) && !page_count (pfn_to_page (pfn)));Have you actually observed false positives with this WARN? I would expect anything without a struct page to get filtered out by !kvm_is_reserved_pfn(pfn).
Those are the type of pfns that were responsible for CVE-2021-22543 [1]. One specific example is that amdgpu uses ttm_pool, which makes higher order, non-compound allocation. Without the head/tail metadata, only the first base page in such an allocation has non-zero page_count.
[1] https://github.com/google/security-research/security/advisories/GHSA-7wq5-phmq-m584
Quoted 3 lines
If you have observed false positives, I would strongly prefer we find a way to keep the
page_count()sanity check, it has proven very helpful in the past in finding/debugging bugs during MMU development.
When we see a refcount of zero, I think we can look up spte->(gfn, slot)->hva->vma and determine whether or not the zero refcount is okay, based on vm_flags. That's kind of heavy for a debug check, although at least we'd only pay the cost for unusual mappings. But it still might make sense to switch to a MMU_WARN_ON, in that case. Or we could just ignore the cost, since at least from a superficial reading and some basic tests, tdp_mmu doesn't seem to execute this code path.
Thoughts? I'd lean towards MMU_WARN_ON, but I'd like to know what the maintainers' preferences are before sending an updated patch series.
-David
Quoted 5 lines
Quoted 5 lines
arch/x86/kvm/mmu/mmu.c +0
-1-if (is_accessed_spte (old_spte))kvm_set_pfn_accessed (pfn);
Sean Christopherson
maintainerRe: [PATCH v4 4/4] KVM: mmu: remove over-aggressive warnings
Quoted 32 lines · David Stevens
Quoted 21 lines · Sean Christopherson
Quoted 19 lines · David Stevens
From: David Stevens <[email protected]>
Remove two warnings that require ref counts for pages to be non-zero, as mapped pfns from
follow_pfnmay not have an initialized ref count.Signed-off-by David Stevens <[email protected]>--- arch/x86/kvm/mmu/mmu.c | 7 ------- virt/kvm/kvm_main.c | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-)arch/x86/kvm/mmu/mmu.c +0
-0@@ -617,13 +617,6 @@ static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) pfn =spte_to_pfn (old_spte);Quoted 9 lines
arch/x86/kvm/mmu/mmu.c static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) · line 625
Quoted 6 lines
arch/x86/kvm/mmu/mmu.c +0
-6-/* -* KVM does not hold the refcount of the page used by -* kvm mmu, before reclaiming the page, we should -* unmap it from mmu first. -*/ -WARN_ON (!kvm_is_reserved_pfn (pfn) && !page_count (pfn_to_page (pfn)));Have you actually observed false positives with this WARN? I would expect anything without a struct page to get filtered out by !kvm_is_reserved_pfn(pfn).
Quoted 5 lines
Those are the type of pfns that were responsible for CVE-2021-22543 [1]. One specific example is that amdgpu uses
ttm_pool, which makes higher order, non-compound allocation. Without the head/tail metadata, only the first base page in such an allocation has non-zeropage_count.
Huh. I hadn't actually read the CVE, or obviously thought critically about the problem. :-)
Quoted 14 lines
[1] https://github.com/google/security-research/security/advisories/GHSA-7wq5-phmq-m584
Quoted 3 lines
If you have observed false positives, I would strongly prefer we find a way to keep the
page_count()sanity check, it has proven very helpful in the past in finding/debugging bugs during MMU development.When we see a refcount of zero, I think we can look up spte->(gfn, slot)->hva->vma and determine whether or not the zero refcount is okay, based on
vm_flags. That's kind of heavy for a debug check, although at least we'd only pay the cost for unusual mappings. But it still might make sense to switch to aMMU_WARN_ON, in that case. Or we could just ignore the cost, since at least from a superficial reading and some basic tests,tdp_mmudoesn't seem to execute this code path.
Quoted 2 lines
Thoughts? I'd lean towards
MMU_WARN_ON, but I'd like to know what the maintainers' preferences are before sending an updated patch series.
MMU_WARN_ON is a poor choice, but only because no one turns it on. I think we've discussed turning it into a proper Kconfig (and killing off mmu_audit.c) multiple times, but no one has actually followed through.
The TDP MMU indeed doesn't hit this path. So I'd say just keep this patch as is and punt the whole MMU_WARN_ON / audit cleanup to the future. I bet if we spend any time at all, we can think of a big pile of MMU sanity checks we could add to KVM, i.e. this can be but one of many checks that applies to all flavors of MMUs.