Commit d3ba32d1 authored by Dapeng Mi's avatar Dapeng Mi Committed by Sean Christopherson
Browse files

KVM: x86/pmu: Load/save GLOBAL_CTRL via entry/exit fields for mediated PMU



When running a guest with a mediated PMU, context switch PERF_GLOBAL_CTRL
via the dedicated VMCS fields for both host and guest.  For the host,
always zero GLOBAL_CTRL on exit as the guest's state will still be loaded
in hardware (KVM will context switch the bulk of PMU state outside of the
inner run loop).  For the guest, use the dedicated fields to atomically
load and save PERF_GLOBAL_CTRL on all entry/exits.

For now, require VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL support (introduced by
Sapphire Rapids).  KVM can support such CPUs by saving PERF_GLOBAL_CTRL
via the MSR save list, a.k.a. the MSR auto-store list, but defer that
support as it adds a small amount of complexity and is somewhat unique.

To minimize VM-Entry latency, propagate IA32_PERF_GLOBAL_CTRL to the VMCS
on-demand.  But to minimize complexity, read IA32_PERF_GLOBAL_CTRL out of
the VMCS on all non-failing VM-Exits.  I.e. partially cache the MSR.
KVM could track GLOBAL_CTRL as an EXREG and defer all reads, but writes
are rare, i.e. the dirty tracking for an EXREG is unnecessary, and it's
not obvious that shaving ~15-20 cycles per exit is meaningful given the
total overhead associated with mediated PMU context switches.

Suggested-by: default avatarSean Christopherson <seanjc@google.com>
Signed-off-by: default avatarDapeng Mi <dapeng1.mi@linux.intel.com>
Co-developed-by: default avatarMingwei Zhang <mizhang@google.com>
Signed-off-by: default avatarMingwei Zhang <mizhang@google.com>
Tested-by: default avatarXudong Hao <xudong.hao@intel.com>
Co-developed-by: default avatarSean Christopherson <seanjc@google.com>
Tested-by: default avatarManali Shukla <manali.shukla@amd.com>
Link: https://patch.msgid.link/20251206001720.468579-22-seanjc@google.com


Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
parent 80624272
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -23,5 +23,7 @@ KVM_X86_PMU_OP_OPTIONAL(reset)
KVM_X86_PMU_OP_OPTIONAL(deliver_pmi)
KVM_X86_PMU_OP_OPTIONAL(cleanup)

KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl)

#undef KVM_X86_PMU_OP
#undef KVM_X86_PMU_OP_OPTIONAL
+1 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@
#define VM_EXIT_PT_CONCEAL_PIP			0x01000000
#define VM_EXIT_CLEAR_IA32_RTIT_CTL		0x02000000
#define VM_EXIT_LOAD_CET_STATE                  0x10000000
#define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL	0x40000000

#define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR	0x00036dff

+11 −2
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops)
#undef __KVM_X86_PMU_OP
}

void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops)
void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops)
{
	bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL;
	int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS;
@@ -139,6 +139,9 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops)
	    !pmu_ops->is_mediated_pmu_supported(&kvm_host_pmu))
		enable_mediated_pmu = false;

	if (!enable_mediated_pmu)
		pmu_ops->write_global_ctrl = NULL;

	if (!enable_pmu) {
		memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap));
		return;
@@ -834,6 +837,9 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
			diff = pmu->global_ctrl ^ data;
			pmu->global_ctrl = data;
			reprogram_counters(pmu, diff);

			if (kvm_vcpu_has_mediated_pmu(vcpu))
				kvm_pmu_call(write_global_ctrl)(data);
		}
		break;
	case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
@@ -928,8 +934,11 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
	 * in the global controls).  Emulate that behavior when refreshing the
	 * PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL.
	 */
	if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters)
	if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) {
		pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0);
		if (kvm_vcpu_has_mediated_pmu(vcpu))
			kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl);
	}

	bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters);
	bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX,
+2 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ struct kvm_pmu_ops {
	void (*cleanup)(struct kvm_vcpu *vcpu);

	bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu);
	void (*write_global_ctrl)(u64 global_ctrl);

	const u64 EVENTSEL_EVENT;
	const int MAX_NR_GP_COUNTERS;
@@ -183,7 +184,7 @@ static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc)

extern struct x86_pmu_capability kvm_pmu_cap;

void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops);
void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops);

void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc);

+6 −0
Original line number Diff line number Diff line
@@ -109,6 +109,12 @@ static inline bool cpu_has_load_cet_ctrl(void)
{
	return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_CET_STATE);
}

static inline bool cpu_has_save_perf_global_ctrl(void)
{
	return vmcs_config.vmexit_ctrl & VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL;
}

static inline bool cpu_has_vmx_mpx(void)
{
	return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS;
Loading