diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2026-10-16 09:51:00 +0200 |
|---|---|---|
| committer | Paolo Bonzini <pbonzini@redhat.com> | 2026-10-16 09:51:00 +0200 |
| commit | 7930afcf146df456d9ba17cf11d8269955c694cf (patch) | |
| tree | 4af3f47f87283b898fd0a3e6123787a8992a1927 | |
| parent | 7213f4e515354c398276689e82cbac011e678646 (diff) | |
| parent | 31adfe155db60a58b61e5b3d472cf85336ff3daf (diff) | |
Merge tag 'kvm-x86-mmu-7.4' of https://github.com/kvm-x86/linux into HEAD
KVM x86 MMU changes for 7.4
- Grab KVM's SRCU "lock" in kvm_zap_gfn_range() to fix a recurring bug where
callers fail to do so.
- Fix a variety of issues in the pre-fault code that are mostly benign in the
current code base, but will show up in big ways if/when the pre-faulting
logic is used to implementing prefetch-on-fault for the TDP MMU.
- Guard rmaps with mmu_lock for PREEMPT_RT=y kernels to fix a longstanding
issue introducing by "lockless aging" where KVM would take its effective
raw bit-spinlock with preemption enabled (because mmu_lock gets turned into
a sleepable lock).
- Prepare a memory_fault exit (paired with -EFAULT) when KVM hits an error
PFN, e.g. so that userspace can resolve transient issues instead of killing
the guest. Convert a variety of WARN()+EFAULT paths into KVM_BUG_ON()+EIO
so that KVM can hard commit to filling memory_fault on -EFAULTs that occur
in the page fault path.
- Fix a NX hugepage un-accounting bug where KVM would always uncharge the
shadow MMU, even when handling TDP MMU pages.
| -rw-r--r-- | arch/x86/kvm/mmu/mmu.c | 124 | ||||
| -rw-r--r-- | arch/x86/kvm/mmu/paging_tmpl.h | 4 | ||||
| -rw-r--r-- | arch/x86/kvm/x86.c | 2 |
3 files changed, 97 insertions, 33 deletions
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 205dfe00111b..5a2cc6a17a9b 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -864,11 +864,12 @@ void untrack_possible_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp, list_del_init(&sp->possible_nx_huge_page_link); } -static void unaccount_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp) +static void unaccount_nx_huge_page(struct kvm *kvm, struct kvm_mmu_page *sp, + enum kvm_mmu_type mmu_type) { sp->nx_huge_page_disallowed = false; - untrack_possible_nx_huge_page(kvm, sp, KVM_SHADOW_MMU); + untrack_possible_nx_huge_page(kvm, sp, mmu_type); } static struct kvm_memory_slot *gfn_to_memslot_dirty_bitmap(struct kvm_vcpu *vcpu, @@ -895,6 +896,7 @@ static struct kvm_memory_slot *gfn_to_memslot_dirty_bitmap(struct kvm_vcpu *vcpu */ #define KVM_RMAP_MANY BIT(0) +#ifndef CONFIG_PREEMPT_RT /* * rmaps and PTE lists are mostly protected by mmu_lock (the shadow MMU always * operates with mmu_lock held for write), but rmaps can be walked without @@ -1008,7 +1010,8 @@ static unsigned long kvm_rmap_get(struct kvm_rmap_head *rmap_head) * actual locking is the same, but the caller is disallowed from modifying the * rmap, and so the unlock flow is a nop if the rmap is/was empty. */ -static unsigned long kvm_rmap_lock_readonly(struct kvm_rmap_head *rmap_head) +static unsigned long kvm_rmap_lock_readonly(struct kvm *kvm, + struct kvm_rmap_head *rmap_head) { unsigned long rmap_val; @@ -1032,6 +1035,35 @@ static void kvm_rmap_unlock_readonly(struct kvm_rmap_head *rmap_head, __kvm_rmap_unlock(rmap_head, old_val); preempt_enable(); } +#else +static unsigned long kvm_rmap_get(struct kvm_rmap_head *rmap_head) +{ + return atomic_long_read(&rmap_head->val); +} +static unsigned long kvm_rmap_lock(struct kvm *kvm, + struct kvm_rmap_head *rmap_head) +{ + lockdep_assert_held_write(&kvm->mmu_lock); + return kvm_rmap_get(rmap_head); +} + +static void kvm_rmap_unlock(struct kvm *kvm, + struct kvm_rmap_head *rmap_head, + unsigned long new_val) +{ + atomic_long_set_release(&rmap_head->val, new_val); +} + +static unsigned long kvm_rmap_lock_readonly(struct kvm *kvm, + struct kvm_rmap_head *rmap_head) +{ + lockdep_assert_held_read(&kvm->mmu_lock); + return kvm_rmap_get(rmap_head); +} + +static void kvm_rmap_unlock_readonly(struct kvm_rmap_head *rmap_head, + unsigned long old_val) { } +#endif /* * Returns the number of pointers in the rmap chain, not counting the new one. @@ -1745,11 +1777,24 @@ static bool kvm_rmap_age_gfn_range(struct kvm *kvm, gfn_t gfn; int level; + BUILD_BUG_ON(!IS_ENABLED(CONFIG_KVM_MMU_LOCKLESS_AGING)); + + /* + * For realtime kernels, do aging under mmu_lock (per-rmap locking is + * compiled out), as realtime deployments are unlikely to benefit from + * increased aging throughput and reduced jitter for memory-overcommitted + * nested VMs, whereas keeping preemption enabled is extremely valuable + * (mmu_lock becomes a sleepable lock on realtime kernels). + */ +#ifdef CONFIG_PREEMPT_RT + guard(read_lock)(&kvm->mmu_lock); +#endif + for (level = PG_LEVEL_4K; level <= KVM_MAX_HUGEPAGE_LEVEL; level++) { for (gfn = range->start; gfn < range->end; gfn += KVM_PAGES_PER_HPAGE(level)) { rmap_head = gfn_to_rmap(gfn, level, range->slot); - rmap_val = kvm_rmap_lock_readonly(rmap_head); + rmap_val = kvm_rmap_lock_readonly(kvm, rmap_head); for_each_rmap_spte_lockless(rmap_val, &iter, sptep, old_spte) { if (!is_accessed_spte(old_spte)) @@ -2791,7 +2836,7 @@ static bool __kvm_mmu_prepare_zap_page(struct kvm *kvm, } if (sp->nx_huge_page_disallowed) - unaccount_nx_huge_page(kvm, sp); + unaccount_nx_huge_page(kvm, sp, KVM_SHADOW_MMU); sp->role.invalid = 1; @@ -3027,6 +3072,9 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, if (prefetch) return -EEXIST; + if (KVM_BUG_ON(sp->role.level != PG_LEVEL_4K, kvm)) + continue; + /* * TDP MMU page faults require an additional spinlock as they * run with mmu_lock held for read, not write, and the unsync @@ -3050,7 +3098,6 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, continue; } - WARN_ON_ONCE(sp->role.level != PG_LEVEL_4K); kvm_unsync_page(kvm, sp); } if (locked) @@ -3537,8 +3584,8 @@ static int direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) fault->req_level >= it.level); } - if (WARN_ON_ONCE(it.level != fault->goal_level)) - return -EFAULT; + if (KVM_BUG_ON(it.level != fault->goal_level, vcpu->kvm)) + return -EIO; ret = mmu_set_spte(vcpu, fault->slot, it.sptep, access, base_gfn, fault->pfn, fault); @@ -3576,6 +3623,7 @@ static int kvm_handle_error_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fa return RET_PF_RETRY; } + kvm_mmu_prepare_memory_fault_exit(vcpu, fault); return -EFAULT; } @@ -4901,8 +4949,8 @@ int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code, #ifndef CONFIG_X86_64 /* A 64-bit CR2 should be impossible on 32-bit KVM. */ - if (WARN_ON_ONCE(fault_address >> 32)) - return -EFAULT; + if (KVM_BUG_ON(fault_address >> 32, vcpu->kvm)) + return -EIO; #endif /* * Legacy #PF exception only have a 32-bit error code. Simply drop the @@ -5067,6 +5115,10 @@ static int kvm_tdp_page_prefault(struct kvm_vcpu *vcpu, gpa_t gpa, if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu)) return -EIO; + r = kvm_mmu_reload(vcpu); + if (r) + return r; + cond_resched(); r = kvm_mmu_do_page_fault(vcpu, gpa, error_code, true, NULL, level); } while (r == RET_PF_RETRY); @@ -5112,14 +5164,6 @@ long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, if (kvm_is_gfn_alias(vcpu->kvm, gpa_to_gfn(range->gpa))) return -EINVAL; - /* - * reload is efficient when called repeatedly, so we can do it on - * every iteration. - */ - r = kvm_mmu_reload(vcpu); - if (r) - return r; - direct_bits = 0; if (kvm_arch_has_private_mem(vcpu->kvm) && kvm_is_private_gfn(vcpu->kvm, gpa_to_gfn(range->gpa))) @@ -5224,14 +5268,6 @@ int kvm_tdp_mmu_map_private_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn) if (kvm_gfn_is_write_tracked(kvm, fault.slot, fault.gfn)) return -EPERM; - r = kvm_mmu_reload(vcpu); - if (r) - return r; - - r = mmu_topup_memory_caches(vcpu, false); - if (r) - return r; - do { if (signal_pending(current)) return -EINTR; @@ -5239,10 +5275,38 @@ int kvm_tdp_mmu_map_private_pfn(struct kvm_vcpu *vcpu, gfn_t gfn, kvm_pfn_t pfn) if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu)) return -EIO; + r = kvm_mmu_reload(vcpu); + if (r) + return r; + + r = mmu_topup_memory_caches(vcpu, false); + if (r) + return r; + cond_resched(); guard(read_lock)(&kvm->mmu_lock); + /* + * Because slots_lock is held, it should be impossible for *any* + * roots to be invalidated after the initial MMU reload. WARN, + * but continue on; the above MMU reload will do the right thing + * if the current root is actually invalid. + */ + WARN_ON_ONCE(kvm_test_request(KVM_REQ_MMU_FREE_OBSOLETE_ROOTS, vcpu)); + + /* + * Snapshot the invalidation sequence counter after acquiring + * mmu_lock, as guest_memfd guarantees the validity of the pfn, + * i.e. any concurrent invalidations are guaranteed to be + * irrelevant. + */ + fault.mmu_seq = vcpu->kvm->mmu_invalidate_seq; + if (is_page_fault_stale(vcpu, &fault)) { + r = RET_PF_RETRY; + continue; + } + r = kvm_tdp_mmu_map(vcpu, &fault); } while (r == RET_PF_RETRY); @@ -6606,8 +6670,8 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err r = RET_PF_INVALID; if (unlikely(error_code & PFERR_RSVD_MASK)) { - if (WARN_ON_ONCE(error_code & PFERR_PRIVATE_ACCESS)) - return -EFAULT; + if (KVM_BUG_ON(error_code & PFERR_PRIVATE_ACCESS, vcpu->kvm)) + return -EIO; r = handle_mmio_page_fault(vcpu, cr2_or_gpa, direct); if (r == RET_PF_EMULATE) @@ -7137,6 +7201,8 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) if (WARN_ON_ONCE(gfn_end <= gfn_start)) return; + guard(srcu)(&kvm->srcu); + write_lock(&kvm->mmu_lock); kvm_mmu_invalidate_start(kvm); @@ -8005,7 +8071,7 @@ static void kvm_recover_nx_huge_pages(struct kvm *kvm, WARN_ON_ONCE(!sp->nx_huge_page_disallowed); WARN_ON_ONCE(!sp->role.direct); - unaccount_nx_huge_page(kvm, sp); + unaccount_nx_huge_page(kvm, sp, mmu_type); if (is_tdp_mmu) spin_unlock(tdp_mmu_pages_lock); diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index c8ec47b09264..8e350095508c 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -778,8 +778,8 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, fault->req_level >= it.level); } - if (WARN_ON_ONCE(it.level != fault->goal_level)) - return -EFAULT; + if (KVM_BUG_ON(it.level != fault->goal_level, vcpu->kvm)) + return -EIO; ret = mmu_set_spte(vcpu, fault->slot, it.sptep, gw->pte_access, base_gfn, fault->pfn, fault); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 269d9886248e..50efebf156d5 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7938,10 +7938,8 @@ void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm, kvm->arch.apicv_inhibit_reasons = new; if (new) { unsigned long gfn = gpa_to_gfn(APIC_DEFAULT_PHYS_BASE); - int idx = srcu_read_lock(&kvm->srcu); kvm_zap_gfn_range(kvm, gfn, gfn+1); - srcu_read_unlock(&kvm->srcu, idx); } } else { kvm->arch.apicv_inhibit_reasons = new; |
