PerfMon v4 streamlined the Freeze-LBR-on-PMI mechanism. When DEBUGCTLMSR_FREEZE_LBRS_ON_PMI is set and a PMI fires, hardware sets IA32_PERF_GLOBAL_STATUS.LBR_Frz instead of clearing DEBUGCTLMSR_LBR. Guest PerfMon v4+ is supported under mediated vPMU only. When KVM relays a guest-induced PMI, the mediated vPMU framework already propagates IA32_PERF_GLOBAL_STATUS.LBR_Frz, and no additional handling is required. For PMIs generated by KVM-emulated PMU events, however, KVM must emulate IA32_PERF_GLOBAL_STATUS.LBR_Frz so that guest LBR recording is frozen as required by the architectural behavior. Signed-off-by: Zide Chen --- arch/x86/kvm/vmx/pmu_intel.c | 47 ++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index cb6f9c272e03..556c119d5e91 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -660,6 +660,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) pmu->global_status_rsvd = pmu->global_ctrl_rsvd & ~(GLOBAL_STATUS_BUFFER_OVF | GLOBAL_STATUS_COND_CHG); + if (pmu->version > 3) + pmu->global_status_rsvd &= ~GLOBAL_STATUS_LBRS_FROZEN; if (vmx_pt_mode_is_host_guest()) pmu->global_status_rsvd &= ~GLOBAL_STATUS_TRACE_TOPAPMI; @@ -718,32 +720,41 @@ static void intel_pmu_reset(struct kvm_vcpu *vcpu) } /* - * Emulate LBR_On_PMI behavior for 1 < pmu.version < 4. - * - * If Freeze_LBR_On_PMI = 1, the LBR is frozen on PMI and - * the KVM emulates to clear the LBR bit (bit 0) in IA32_DEBUGCTL. - * - * Guest needs to re-enable LBR to resume branches recording. + * Emulate legacy and streamlined Freeze_LBR_On_PMI behavior. + * In either case, guest needs to re-enable LBR to resume branches recording. */ -static void intel_pmu_legacy_freezing_lbrs_on_pmi(struct kvm_vcpu *vcpu) +static void intel_pmu_freeze_lbr_on_pmi(struct kvm_vcpu *vcpu) { - u64 data = vmx_guest_debugctl_read(); + u8 version; + u64 data; - if (data & DEBUGCTLMSR_FREEZE_LBRS_ON_PMI) { - data &= ~DEBUGCTLMSR_LBR; - vmx_guest_debugctl_write(vcpu, data); + if (!intel_pmu_lbr_is_enabled(vcpu)) + return; + + data = vmx_guest_debugctl_read(); + version = vcpu_to_pmu(vcpu)->version; + + if (!(data & DEBUGCTLMSR_FREEZE_LBRS_ON_PMI)) + return; + + if (version > 1 && version < 4) { + if (data & DEBUGCTLMSR_LBR) { + data &= ~DEBUGCTLMSR_LBR; + vmx_guest_debugctl_write(vcpu, data); + } + } else if (vcpu_to_lbr_desc(vcpu)->msr_passthrough && + kvm_vcpu_has_mediated_pmu(vcpu)) { + /* + * This will be restored to guest before VM-Entry, setting + * LBR_Frz to freeze LBR recording until the guest clears it. + */ + vcpu_to_pmu(vcpu)->global_status |= GLOBAL_STATUS_LBRS_FROZEN; } } static void intel_pmu_deliver_pmi(struct kvm_vcpu *vcpu) { - u8 version = vcpu_to_pmu(vcpu)->version; - - if (!intel_pmu_lbr_is_enabled(vcpu)) - return; - - if (version > 1 && version < 4) - intel_pmu_legacy_freezing_lbrs_on_pmi(vcpu); + intel_pmu_freeze_lbr_on_pmi(vcpu); } static void vmx_update_intercept_for_lbr_msrs(struct kvm_vcpu *vcpu, bool set) -- 2.54.0