Skip to content

Commit d374b89

Browse files
committed
KVM: VMX: Add mediated PMU support for CPUs without "save perf global ctrl"
Extend mediated PMU support for Intel CPUs without support for saving PERF_GLOBAL_CONTROL into the guest VMCS field on VM-Exit, e.g. for Skylake and its derivatives, as well as Icelake. While supporting CPUs without VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL isn't completely trivial, it's not that complex either. And not supporting such CPUs would mean not supporting 7+ years of Intel CPUs released in the past 10 years. On VM-Exit, immediately propagate the saved PERF_GLOBAL_CTRL to the VMCS as well as KVM's software cache so that KVM doesn't need to add full EXREG tracking of PERF_GLOBAL_CTRL. In practice, the vast majority of VM-Exits won't trigger software writes to guest PERF_GLOBAL_CTRL, so deferring the VMWRITE to the next VM-Enter would only delay the inevitable without batching/avoiding VMWRITEs. Note! Take care to refresh VM_EXIT_MSR_STORE_COUNT on nested VM-Exit, as it's unfortunately possible that KVM could recalculate MSR intercepts while L2 is active, e.g. if userspace loads nested state and _then_ sets PERF_CAPABILITIES. Eating the VMWRITE on every nested VM-Exit is unfortunate, but that's a pre-existing problem and can/should be solved separately, e.g. modifying the number of auto-load entries while L2 is active is also uncommon on modern CPUs. Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com> Tested-by: Dapeng Mi <dapeng1.mi@linux.intel.com> Tested-by: Manali Shukla <manali.shukla@amd.com> Link: https://patch.msgid.link/20251206001720.468579-45-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 9757a5a commit d374b89

File tree

3 files changed

+52
-13
lines changed

3 files changed

+52
-13
lines changed

arch/x86/kvm/vmx/nested.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5142,7 +5142,11 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
51425142

51435143
kvm_nested_vmexit_handle_ibrs(vcpu);
51445144

5145-
/* Update any VMCS fields that might have changed while L2 ran */
5145+
/*
5146+
* Update any VMCS fields that might have changed while vmcs02 was the
5147+
* active VMCS. The tracking is per-vCPU, not per-VMCS.
5148+
*/
5149+
vmcs_write32(VM_EXIT_MSR_STORE_COUNT, vmx->msr_autostore.nr);
51465150
vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr);
51475151
vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr);
51485152
vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset);

arch/x86/kvm/vmx/pmu_intel.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -777,13 +777,6 @@ static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_
777777
if (WARN_ON_ONCE(!cpu_has_load_perf_global_ctrl()))
778778
return false;
779779

780-
/*
781-
* KVM doesn't yet support mediated PMU on CPUs without support for
782-
* saving PERF_GLOBAL_CTRL via a dedicated VMCS field.
783-
*/
784-
if (!cpu_has_save_perf_global_ctrl())
785-
return false;
786-
787780
return true;
788781
}
789782

arch/x86/kvm/vmx/vmx.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,17 @@ static bool update_transition_efer(struct vcpu_vmx *vmx)
12041204
return true;
12051205
}
12061206

1207+
static void vmx_add_autostore_msr(struct vcpu_vmx *vmx, u32 msr)
1208+
{
1209+
vmx_add_auto_msr(&vmx->msr_autostore, msr, 0, VM_EXIT_MSR_STORE_COUNT,
1210+
vmx->vcpu.kvm);
1211+
}
1212+
1213+
static void vmx_remove_autostore_msr(struct vcpu_vmx *vmx, u32 msr)
1214+
{
1215+
vmx_remove_auto_msr(&vmx->msr_autostore, msr, VM_EXIT_MSR_STORE_COUNT);
1216+
}
1217+
12071218
#ifdef CONFIG_X86_32
12081219
/*
12091220
* On 32-bit kernels, VM exits still load the FS and GS bases from the
@@ -4225,6 +4236,8 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu)
42254236

42264237
static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
42274238
{
4239+
u64 vm_exit_controls_bits = VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL |
4240+
VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL;
42284241
bool has_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu);
42294242
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
42304243
struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -4234,12 +4247,19 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu)
42344247
if (!enable_mediated_pmu)
42354248
return;
42364249

4250+
if (!cpu_has_save_perf_global_ctrl()) {
4251+
vm_exit_controls_bits &= ~VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL;
4252+
4253+
if (has_mediated_pmu)
4254+
vmx_add_autostore_msr(vmx, MSR_CORE_PERF_GLOBAL_CTRL);
4255+
else
4256+
vmx_remove_autostore_msr(vmx, MSR_CORE_PERF_GLOBAL_CTRL);
4257+
}
4258+
42374259
vm_entry_controls_changebit(vmx, VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL,
42384260
has_mediated_pmu);
42394261

4240-
vm_exit_controls_changebit(vmx, VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL |
4241-
VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL,
4242-
has_mediated_pmu);
4262+
vm_exit_controls_changebit(vmx, vm_exit_controls_bits, has_mediated_pmu);
42434263

42444264
for (i = 0; i < pmu->nr_arch_gp_counters; i++) {
42454265
vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i,
@@ -7346,6 +7366,29 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx)
73467366
msrs[i].host);
73477367
}
73487368

7369+
static void vmx_refresh_guest_perf_global_control(struct kvm_vcpu *vcpu)
7370+
{
7371+
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
7372+
struct vcpu_vmx *vmx = to_vmx(vcpu);
7373+
7374+
if (msr_write_intercepted(vmx, MSR_CORE_PERF_GLOBAL_CTRL))
7375+
return;
7376+
7377+
if (!cpu_has_save_perf_global_ctrl()) {
7378+
int slot = vmx_find_loadstore_msr_slot(&vmx->msr_autostore,
7379+
MSR_CORE_PERF_GLOBAL_CTRL);
7380+
7381+
if (WARN_ON_ONCE(slot < 0))
7382+
return;
7383+
7384+
pmu->global_ctrl = vmx->msr_autostore.val[slot].value;
7385+
vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, pmu->global_ctrl);
7386+
return;
7387+
}
7388+
7389+
pmu->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL);
7390+
}
7391+
73497392
static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit)
73507393
{
73517394
struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -7631,8 +7674,7 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
76317674

76327675
vmx->loaded_vmcs->launched = 1;
76337676

7634-
if (!msr_write_intercepted(vmx, MSR_CORE_PERF_GLOBAL_CTRL))
7635-
vcpu_to_pmu(vcpu)->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL);
7677+
vmx_refresh_guest_perf_global_control(vcpu);
76367678

76377679
vmx_recover_nmi_blocking(vmx);
76387680
vmx_complete_interrupts(vmx);

0 commit comments

Comments
 (0)