[PATCH 17/23] KVM: x86/pmu: Handle GLOBAL_CTRL under PerfMon masking

Zide Chen <[email protected]>
Newsgroups org.kernel.vger.kvm,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Guest writes must be gated, and guest reads masked, with
pmu->global_ctrl_rsvd rather than pmu->perfmon_mask, because bits
63:52 in global_ctrl_rsvd are guaranteed to be cleared, and bits 51:0
are the complement between the two masks.

Since the host could schedule !exclude_guest events on host-owned
resources in non-root mode, OR the host-owned bits into
GUEST_IA32_PERF_GLOBAL_CTRL when loading guest PMU state. The host-
owned bits are supposed to be set during perf_load_guest_context().

Add a host_global_ctrl parameter to the mediated_load() callback so
that the host value can be available before the register is cleared.
This allows intel_mediated_pmu_load() to preserve host-owned bits.

When caching pmu->global_ctrl at VM exit, mask the guest value with
pmu->global_ctrl_rsvd to retain guest-owned bits only.

Signed-off-by: Zide Chen <[email protected]>
---
 arch/x86/kvm/pmu.c           |  7 ++++++-
 arch/x86/kvm/pmu.h           |  2 +-
 arch/x86/kvm/svm/pmu.c       |  2 +-
 arch/x86/kvm/vmx/pmu_intel.c | 13 ++++++++++++-
 arch/x86/kvm/vmx/vmx.c       |  4 ++++
 5 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 337d2f55a216..b051ba66edce 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -1406,6 +1406,8 @@ static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu)
 
 void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu)
 {
+	u64 host_global_ctrl = 0;
+
 	if (!kvm_vcpu_has_mediated_pmu(vcpu) ||
 	    KVM_BUG_ON(!lapic_in_kernel(vcpu), vcpu->kvm))
 		return;
@@ -1429,13 +1431,16 @@ void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu)
 	 * even for SVM to minimize the damage if a perf event is left enabled,
 	 * and to ensure a consistent starting state.
 	 */
+	if (kvm_vcpu_has_perfmon_mask(vcpu))
+		rdmsrq(kvm_pmu_ops.PERF_GLOBAL_CTRL, host_global_ctrl);
+
 	wrmsrq(kvm_pmu_ops.PERF_GLOBAL_CTRL, 0);
 
 	perf_load_guest_lvtpc(kvm_lapic_get_reg(vcpu->arch.apic, APIC_LVTPC));
 
 	kvm_pmu_load_guest_pmcs(vcpu);
 
-	kvm_pmu_call(mediated_load)(vcpu);
+	kvm_pmu_call(mediated_load)(vcpu, host_global_ctrl);
 }
 
 static void kvm_pmu_put_guest_pmcs(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index c5feeb60bcf6..30e456302c65 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -39,7 +39,7 @@ struct kvm_pmu_ops {
 	bool (*pmc_is_disabled_in_current_mode)(struct kvm_pmc *pmc);
 
 	bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu);
-	void (*mediated_load)(struct kvm_vcpu *vcpu);
+	void (*mediated_load)(struct kvm_vcpu *vcpu, u64 host_global_ctrl);
 	void (*mediated_put)(struct kvm_vcpu *vcpu);
 	void (*write_global_ctrl)(u64 global_ctrl);
 
diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c
index f81817606baa..d9cd2a5ab411 100644
--- a/arch/x86/kvm/svm/pmu.c
+++ b/arch/x86/kvm/svm/pmu.c
@@ -246,7 +246,7 @@ static bool amd_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pm
 	return host_pmu->version >= 2;
 }
 
-static void amd_mediated_pmu_load(struct kvm_vcpu *vcpu)
+static void amd_mediated_pmu_load(struct kvm_vcpu *vcpu, u64 host_global_ctrl)
 {
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 	u64 global_status;
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index 9236bfa15c41..d0373de951d5 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -947,11 +947,22 @@ static u64 intel_fixed_ctrl_host_bits(struct kvm_pmu *pmu)
 	return fixed_ctl;
 }
 
-static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu)
+static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu, u64 host_global_ctrl)
 {
 	struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 	u64 global_status, toggle;
 
+	/*
+	 * Preserve host-owned bits: perf may schedule !exclude_guest events on
+	 * host-owned counters in non-root mode.
+	 * PerfMon masking requires VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, so the
+	 * MSR-store/load path does not apply here.
+	 */
+	if (kvm_vcpu_has_perfmon_mask(vcpu)) {
+		host_global_ctrl &= pmu->global_ctrl_rsvd;
+		intel_pmu_write_global_ctrl(pmu->global_ctrl | host_global_ctrl);
+	}
+
 	if (kvm_vcpu_has_perf_metrics(vcpu))
 		wrmsrq(MSR_PERF_METRICS, pmu->perf_metrics);
 
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 1b42c9d6f168..a241efb99b3d 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7467,6 +7467,10 @@ static void vmx_refresh_guest_perf_global_control(struct kvm_vcpu *vcpu)
 	}
 
 	pmu->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL);
+
+	/* Strip host-owned bits that were ORed into the VMCS on VMX entry. */
+	if (kvm_vcpu_has_perfmon_mask(vcpu))
+		pmu->global_ctrl &= ~pmu->global_ctrl_rsvd;
 }
 
 void noinstr vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp)
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.