From: David Woodhouse <[email protected]> Move the SRCU lock/activate/retry helpers for accessing a page of L1 guest memory through a gfn_to_pfn_cache from vmx/nested.c to x86.h, so that nested SVM can use them too.
There is nothing nesting-specific about these helpers: they lock a gfn_to_pfn_cache as a mapping of a given whole guest page, activating or re-keying it as required. Name them kvm_gpc_lock_page() and kvm_gpc_unlock() accordingly; they are candidates for hoisting into the generic GPC API in kvm_host.h when a non-x86 user appears (the Hyper-V VP assist and enlightened VMCS pages are likely candidates). No functional change. Signed-off-by: David Woodhouse <[email protected]> Assisted-by: Claude:claude-mythos-5 --- arch/x86/kvm/vmx/nested.c | 60 +++++++++------------------------------ arch/x86/kvm/x86.h | 33 +++++++++++++++++++++ 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 105ff6cbfe58..b9f12313c960 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -320,38 +320,6 @@ static void vmx_switch_vmcs(struct kvm_vcpu *vcpu, struct loaded_vmcs *vmcs) kvm_reset_dirty_registers(vcpu); } -/* - * Map a single page of L1 guest memory at @gpa and enter an SRCU read-side - * critical section protecting the mapping. Returns the SRCU index (>= 0) to - * pass to nested_gpc_unlock(), or a negative error code. While locked, the - * cache's khva and pfn are guaranteed to remain valid: an invalidation - * cannot complete until the reader drains. - */ -static int nested_gpc_lock(struct gfn_to_pfn_cache *gpc, gpa_t gpa) -{ - int idx, err; - - if (!PAGE_ALIGNED(gpa)) - return -EINVAL; -retry: - idx = srcu_read_lock_atomic(&gpc->kvm->gpc_srcu); - if (!kvm_gpc_check(gpc, PAGE_SIZE) || gpc->gpa != gpa) { - srcu_read_unlock_atomic(&gpc->kvm->gpc_srcu, idx); - err = kvm_gpc_activate(gpc, gpa, PAGE_SIZE); - if (err) - return err; - - goto retry; - } - - return idx; -} - -static void nested_gpc_unlock(struct gfn_to_pfn_cache *gpc, int idx) -{ - srcu_read_unlock_atomic(&gpc->kvm->gpc_srcu, idx); -} - /* * Map a page of L1 memory, pin it for direct use by the guest (i.e. by * the CPU while running the L2 guest, via a physical address in vmcs02), @@ -366,24 +334,24 @@ static int nested_gpc_hpa(struct gfn_to_pfn_cache *gpc, gpa_t gpa, hpa_t *hpa) int idx; do { - idx = nested_gpc_lock(gpc, gpa); + idx = kvm_gpc_lock_page(gpc, gpa); if (idx < 0) return idx; /* * The pin can fail only if an invalidation cleared the - * valid bit after nested_gpc_lock() checked it; go back + * valid bit after kvm_gpc_lock_page() checked it; go back * around to refresh (which will not complete until the * invalidation is over) and try again. */ if (kvm_gpc_pin_for_guest(gpc)) break; - nested_gpc_unlock(gpc, idx); + kvm_gpc_unlock(gpc, idx); } while (1); *hpa = pfn_to_hpa(gpc->pfn); - nested_gpc_unlock(gpc, idx); + kvm_gpc_unlock(gpc, idx); return 0; } @@ -393,7 +361,7 @@ static int nested_gpc_hpa(struct gfn_to_pfn_cache *gpc, gpa_t gpa, hpa_t *hpa) * posted interrupts not configured for this L2), -EWOULDBLOCK if it is * active but currently invalidated (only a sleeping refresh would make * it usable). Does not pin; the mapping is stable only until - * nested_gpc_unlock(). + * kvm_gpc_unlock(). */ static int nested_gpc_try_lock_if_active(struct gfn_to_pfn_cache *gpc) { @@ -890,7 +858,7 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, return true; } - idx = nested_gpc_lock(&vmx->nested.msr_bitmap_cache, vmcs12->msr_bitmap); + idx = kvm_gpc_lock_page(&vmx->nested.msr_bitmap_cache, vmcs12->msr_bitmap); if (idx < 0) return false; @@ -974,7 +942,7 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, nested_vmx_merge_pmu_msr_bitmaps(vcpu, msr_bitmap_l1, msr_bitmap_l0); - nested_gpc_unlock(&vmx->nested.msr_bitmap_cache, idx); + kvm_gpc_unlock(&vmx->nested.msr_bitmap_cache, idx); vmx->nested.force_msr_bitmap_recalc = false; @@ -4286,7 +4254,7 @@ static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu) vmx->nested.pi_pending = false; if (!pi_test_and_clear_on(pi_desc)) { - nested_gpc_unlock(&vmx->nested.pi_desc_cache, pi_idx); + kvm_gpc_unlock(&vmx->nested.pi_desc_cache, pi_idx); return 0; } @@ -4294,7 +4262,7 @@ static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu) if (max_irr > 0) { vapic_idx = nested_gpc_lock_if_active(&vmx->nested.virtual_apic_cache); if (vapic_idx < 0) { - nested_gpc_unlock(&vmx->nested.pi_desc_cache, pi_idx); + kvm_gpc_unlock(&vmx->nested.pi_desc_cache, pi_idx); goto mmio_needed; } vapic_page = vmx->nested.virtual_apic_cache.khva; @@ -4302,7 +4270,7 @@ static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu) __kvm_apic_update_irr(pi_desc->pir, vapic_page, &max_irr); kvm_gpc_mark_dirty_in_slot(&vmx->nested.virtual_apic_cache); - nested_gpc_unlock(&vmx->nested.virtual_apic_cache, vapic_idx); + kvm_gpc_unlock(&vmx->nested.virtual_apic_cache, vapic_idx); status = vmcs_read16(GUEST_INTR_STATUS); if ((u8)max_irr > ((u8)status & 0xff)) { @@ -4313,7 +4281,7 @@ static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu) } kvm_gpc_mark_dirty_in_slot(&vmx->nested.pi_desc_cache); - nested_gpc_unlock(&vmx->nested.pi_desc_cache, pi_idx); + kvm_gpc_unlock(&vmx->nested.pi_desc_cache, pi_idx); return 0; mmio_needed: @@ -4472,7 +4440,7 @@ static bool vmx_has_nested_events(struct kvm_vcpu *vcpu, bool for_injection) vapic = vmx->nested.virtual_apic_cache.khva; vppr = *((u32 *)(vapic + APIC_PROCPRI)); - nested_gpc_unlock(&vmx->nested.virtual_apic_cache, idx); + kvm_gpc_unlock(&vmx->nested.virtual_apic_cache, idx); max_irr = vmx_get_rvi(); if ((max_irr & 0xf0) > (vppr & 0xf0)) @@ -4489,12 +4457,12 @@ static bool vmx_has_nested_events(struct kvm_vcpu *vcpu, bool for_injection) if (pi_test_on(pi_desc)) { max_irr = pi_find_highest_vector(pi_desc); if (max_irr > 0 && (max_irr & 0xf0) > (vppr & 0xf0)) { - nested_gpc_unlock(&vmx->nested.pi_desc_cache, idx); + kvm_gpc_unlock(&vmx->nested.pi_desc_cache, idx); return true; } } - nested_gpc_unlock(&vmx->nested.pi_desc_cache, idx); + kvm_gpc_unlock(&vmx->nested.pi_desc_cache, idx); } return false; diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 0f5919b092e4..e3d9889a245d 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -14,6 +14,39 @@ #define KVM_MAX_MCE_BANKS 32 +/* + * Map a single page of L1 guest memory at @gpa into a gfn_to_pfn_cache + * and enter an SRCU read-side critical section protecting the mapping. + * Returns the SRCU index (>= 0) to pass to kvm_gpc_unlock(), or a + * negative error code. While locked, the cache's khva and pfn are + * guaranteed to remain valid: an invalidation cannot complete until the + * reader drains. + */ +static inline int kvm_gpc_lock_page(struct gfn_to_pfn_cache *gpc, gpa_t gpa) +{ + int idx, err; + + if (!PAGE_ALIGNED(gpa)) + return -EINVAL; +retry: + idx = srcu_read_lock_atomic(&gpc->kvm->gpc_srcu); + if (!kvm_gpc_check(gpc, PAGE_SIZE) || gpc->gpa != gpa) { + srcu_read_unlock_atomic(&gpc->kvm->gpc_srcu, idx); + err = kvm_gpc_activate(gpc, gpa, PAGE_SIZE); + if (err) + return err; + + goto retry; + } + + return idx; +} + +static inline void kvm_gpc_unlock(struct gfn_to_pfn_cache *gpc, int idx) +{ + srcu_read_unlock_atomic(&gpc->kvm->gpc_srcu, idx); +} + int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops); void kvm_x86_vendor_exit(void); -- 2.55.0

