When passing the guest's requested PEBS_ENABLE (or rather, KVM's version of PEBS_ENABLE on behalf of the guest), omit counters that are locally disable and/or don't have a perf event (due to contention), in addition to omitting counters that are cross-mapped in the host. In practice, this should be a nop as perf will already have disabled the associated counter, i.e. cpuc->pebs_enabled should have been cleared, but paranoia is cheap, and the existing code _looks_ wrong. Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/pmu_intel.c | 30 ++++++++++++++++-------------- arch/x86/kvm/vmx/vmx.c | 11 +---------- arch/x86/kvm/vmx/vmx.h | 15 ++++++++++++++- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 659fe097b904..1e420c8bca9d 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -736,34 +736,36 @@ static void intel_pmu_cleanup(struct kvm_vcpu *vcpu) intel_pmu_release_guest_lbr_event(vcpu); } -u64 intel_pmu_get_cross_mapped_mask(struct kvm_pmu *pmu) +u64 __intel_pmu_compute_pebs_enable(struct kvm_pmu *pmu) { - u64 host_cross_mapped_mask; + u64 guest_pebs_enable = pmu->pebs_enable & pmu->global_ctrl; + u64 pebs_enable = 0; struct kvm_pmc *pmc; int bit, hw_idx; /* - * Provide a mask of counters that are cross-mapped between the guest - * and the host, i.e. where a guest PMC is mapped to a host PMC with a - * different index. PEBS records hold a PERF_GLOBAL_STATUS snapshot, - * and so PEBS-enabled counters need to hold the correct index so as - * not to confuse the guest. + * Omit counters that are locally disabled, don't have a perf event, or + * ended up with a perf event that is using a different counter than + * the guest, i.e. where the guest PMC is different than the host PMC + * being used on behalf of the guest. PEBS records include + * PERF_GLOBAL_STATUS, and so using a counter with a different index + * means the guest will see overflow status for the wrong counter(s). */ - host_cross_mapped_mask = 0; - - kvm_for_each_pmc(pmu, pmc, bit, (unsigned long *)&pmu->global_ctrl) { + kvm_for_each_pmc(pmu, pmc, bit, (unsigned long *)&guest_pebs_enable) { if (!pmc_is_locally_enabled(pmc) || !pmc->perf_event) continue; /* - * A negative index indicates the event isn't mapped to a + * Note, a negative index indicates the event isn't mapped to a * physical counter in the host, e.g. due to contention. */ hw_idx = pmc->perf_event->hw.idx; - if (hw_idx != pmc->idx && hw_idx > -1) - host_cross_mapped_mask |= BIT_ULL(hw_idx); + if (hw_idx != pmc->idx) + continue; + + pebs_enable |= BIT_ULL(pmc->idx); } - return host_cross_mapped_mask; + return pebs_enable; } static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pmu) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index fbe3ce5f5a51..31675e5cf563 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7314,20 +7314,11 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx) return; struct x86_guest_pebs guest_pebs = { - .enable = pmu->pebs_enable, + .enable = intel_pmu_compute_pebs_enable(pmu), .ds_area = pmu->ds_area, .data_cfg = pmu->pebs_data_cfg, }; - /* - * Disable counters where the guest PMC is different than the host PMC - * being used on behalf of the guest, as the PEBS record includes - * PERF_GLOBAL_STATUS, i.e. the guest will see overflow status for the - * wrong counter(s). - */ - if (guest_pebs.enable & pmu->global_ctrl) - guest_pebs.enable &= ~intel_pmu_get_cross_mapped_mask(pmu); - /* Note, nr_msrs may be garbage if perf_guest_get_msrs() returns NULL. */ msrs = perf_guest_get_msrs(&nr_msrs, &guest_pebs); if (!msrs) diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 0c4563472940..b055731efd2d 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -659,7 +659,20 @@ static __always_inline struct vcpu_vmx *to_vmx(struct kvm_vcpu *vcpu) return container_of(vcpu, struct vcpu_vmx, vcpu); } -u64 intel_pmu_get_cross_mapped_mask(struct kvm_pmu *pmu); +u64 __intel_pmu_compute_pebs_enable(struct kvm_pmu *pmu); + +static inline u64 intel_pmu_compute_pebs_enable(struct kvm_pmu *pmu) +{ + /* + * Avoid the function call overhead in the common case that the guest + * isn't using PEBS. + */ + if (!(pmu->pebs_enable & pmu->global_ctrl)) + return 0; + + return __intel_pmu_compute_pebs_enable(pmu); +} + int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); void vmx_passthrough_lbr_msrs(struct kvm_vcpu *vcpu); -- 2.54.0.563.g4f69b47b94-goog