[PATCH 13/17] KVM: x86: Move nested GPC lock helpers to x86.h as kvm_gpc_lock_page()
From: David Woodhouse
Date: Sun Sep 20 2026 - 17:48:18 EST
From: David Woodhouse <dwmw@xxxxxxxxxxxx>
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 <dwmw@xxxxxxxxxxxx>
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