Re: [PATCH] KVM: x86/pmu: Add Intel PMU supported fixed counters bit mask

From: Jim Mattson
Date: Fri Mar 24 2023 - 19:20:08 EST


On Tue, Mar 21, 2023 at 4:28 AM Like Xu <like.xu.linux@xxxxxxxxx> wrote:
>
> From: Like Xu <likexu@xxxxxxxxxxx>
>
> Per Intel SDM, fixed-function performance counter 'i' is supported if:
>
> FxCtr[i]_is_supported := ECX[i] || (EDX[4:0] > i);
>
> which means that the KVM user space can use EDX to limit the number of
> fixed counters and at the same time, using ECX to enable part of other
> KVM supported fixed counters.
>
> Add a bitmap (instead of always checking the vcpu's CPUIDs) to keep track
> of the guest available fixed counters and perform the semantic checks.
>
> Signed-off-by: Like Xu <likexu@xxxxxxxxxxx>
> ---
> arch/x86/include/asm/kvm_host.h | 2 ++
> arch/x86/kvm/pmu.h | 8 +++++
> arch/x86/kvm/vmx/pmu_intel.c | 53 +++++++++++++++++++++------------
> 3 files changed, 44 insertions(+), 19 deletions(-)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index a45de1118a42..14689e583127 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -565,6 +565,8 @@ struct kvm_pmu {
> */
> bool need_cleanup;
>
> + DECLARE_BITMAP(supported_fixed_pmc_idx, KVM_PMC_MAX_FIXED);
> +
> /*
> * The total number of programmed perf_events and it helps to avoid
> * redundant check before cleanup if guest don't use vPMU at all.
> diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
> index be62c16f2265..9f4504e5e9d5 100644
> --- a/arch/x86/kvm/pmu.h
> +++ b/arch/x86/kvm/pmu.h
> @@ -111,6 +111,11 @@ static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr,
> return NULL;
> }
>
> +static inline bool fixed_ctr_is_supported(struct kvm_pmu *pmu, unsigned int idx)
> +{
> + return test_bit(idx, pmu->supported_fixed_pmc_idx);
> +}
> +
> /* returns fixed PMC with the specified MSR */
> static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)
> {
> @@ -120,6 +125,9 @@ static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)
> u32 index = array_index_nospec(msr - base,
> pmu->nr_arch_fixed_counters);
>
> + if (!fixed_ctr_is_supported(pmu, index))
> + return NULL;
> +
> return &pmu->fixed_counters[index];
> }
>
> diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
> index e8a3be0b9df9..12f4b2fe7756 100644
> --- a/arch/x86/kvm/vmx/pmu_intel.c
> +++ b/arch/x86/kvm/vmx/pmu_intel.c
> @@ -43,13 +43,16 @@ static int fixed_pmc_events[] = {1, 0, 7};
> static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
> {
> struct kvm_pmc *pmc;
> - u8 old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl;
> + u8 new_ctrl, old_ctrl, old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl;
> int i;
>
> pmu->fixed_ctr_ctrl = data;
> for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
> - u8 new_ctrl = fixed_ctrl_field(data, i);
> - u8 old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i);
> + if (!fixed_ctr_is_supported(pmu, i))
> + continue;
> +
> + new_ctrl = fixed_ctrl_field(data, i);
> + old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i);
>
> if (old_ctrl == new_ctrl)
> continue;
> @@ -125,6 +128,9 @@ static bool intel_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
>
> idx &= ~(3u << 30);
>
> + if (fixed && !fixed_ctr_is_supported(pmu, idx))
> + return false;
> +
> return fixed ? idx < pmu->nr_arch_fixed_counters
> : idx < pmu->nr_arch_gp_counters;
> }
> @@ -145,7 +151,7 @@ static struct kvm_pmc *intel_rdpmc_ecx_to_pmc(struct kvm_vcpu *vcpu,
> counters = pmu->gp_counters;
> num_counters = pmu->nr_arch_gp_counters;
> }
> - if (idx >= num_counters)
> + if (idx >= num_counters || (fixed && !fixed_ctr_is_supported(pmu, idx)))
> return NULL;
> *mask &= pmu->counter_bitmask[fixed ? KVM_PMC_FIXED : KVM_PMC_GP];
> return &counters[array_index_nospec(idx, num_counters)];
> @@ -500,6 +506,9 @@ static void setup_fixed_pmc_eventsel(struct kvm_pmu *pmu)
> int i;
>
> for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
> + if (!fixed_ctr_is_supported(pmu, i))
> + continue;
> +
> pmc = &pmu->fixed_counters[i];
> event = fixed_pmc_events[array_index_nospec(i, size)];
> pmc->eventsel = (intel_arch_events[event].unit_mask << 8) |
> @@ -520,6 +529,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
>
> pmu->nr_arch_gp_counters = 0;
> pmu->nr_arch_fixed_counters = 0;
> + bitmap_zero(pmu->supported_fixed_pmc_idx, KVM_PMC_MAX_FIXED);
> pmu->counter_bitmask[KVM_PMC_GP] = 0;
> pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
> pmu->version = 0;
> @@ -551,13 +561,24 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
> pmu->available_event_types = ~entry->ebx &
> ((1ull << eax.split.mask_length) - 1);
>
> - if (pmu->version == 1) {
> - pmu->nr_arch_fixed_counters = 0;
> - } else {
> + counter_mask = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1);
> + bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters);
> +
> + if (pmu->version > 1) {
> pmu->nr_arch_fixed_counters =
> - min3(ARRAY_SIZE(fixed_pmc_events),
> - (size_t) edx.split.num_counters_fixed,
> - (size_t)kvm_pmu_cap.num_counters_fixed);
> + min_t(int, ARRAY_SIZE(fixed_pmc_events),
> + kvm_pmu_cap.num_counters_fixed);
> + for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
> + /* FxCtr[i]_is_supported := CPUID.0xA.ECX[i] || (EDX[4:0] > i) */

This is true only when pmu->version >= 5.

>From the SDM, volume 3, section 20.2.5 Architectural Performance
Monitoring Version 5:

With Architectural Performance Monitoring Version 5, register
CPUID.0AH.ECX indicates Fixed Counter enumeration. It is a bit mask
which enumerates the supported Fixed Counters in a processor. If bit
'i' is set, it implies that Fixed Counter 'i' is supported. Software
is recommended to use the following logic to check if a Fixed Counter
is supported on a given processor: FxCtr[i]_is_supported := ECX[i] ||
(EDX[4:0] > i);

Prior to PMU version 5, all fixed counters from 0 through <number of
fixed counters - 1> are supported.

> + if (!(entry->ecx & BIT_ULL(i) ||
> + edx.split.num_counters_fixed > i))
> + continue;
> +
> + set_bit(i, pmu->supported_fixed_pmc_idx);
> + set_bit(INTEL_PMC_MAX_GENERIC + i, pmu->all_valid_pmc_idx);
> + pmu->fixed_ctr_ctrl_mask &= ~(0xbull << (i * 4));
> + counter_mask &= ~BIT_ULL(INTEL_PMC_MAX_GENERIC + i);
> + }
> edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed,
> kvm_pmu_cap.bit_width_fixed);
> pmu->counter_bitmask[KVM_PMC_FIXED] =
> @@ -565,10 +586,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
> setup_fixed_pmc_eventsel(pmu);
> }
>
> - for (i = 0; i < pmu->nr_arch_fixed_counters; i++)
> - pmu->fixed_ctr_ctrl_mask &= ~(0xbull << (i * 4));
> - counter_mask = ~(((1ull << pmu->nr_arch_gp_counters) - 1) |
> - (((1ull << pmu->nr_arch_fixed_counters) - 1) << INTEL_PMC_IDX_FIXED));
> pmu->global_ctrl_mask = counter_mask;
> pmu->global_ovf_ctrl_mask = pmu->global_ctrl_mask
> & ~(MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF |
> @@ -585,11 +602,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
> pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED);
> }
>
> - bitmap_set(pmu->all_valid_pmc_idx,
> - 0, pmu->nr_arch_gp_counters);
> - bitmap_set(pmu->all_valid_pmc_idx,
> - INTEL_PMC_MAX_GENERIC, pmu->nr_arch_fixed_counters);
> -
> perf_capabilities = vcpu_get_perf_capabilities(vcpu);
> if (cpuid_model_is_consistent(vcpu) &&
> (perf_capabilities & PMU_CAP_LBR_FMT))
> @@ -605,6 +617,9 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
> pmu->pebs_enable_mask = counter_mask;
> pmu->reserved_bits &= ~ICL_EVENTSEL_ADAPTIVE;
> for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
> + if (!fixed_ctr_is_supported(pmu, i))
> + continue;
> +
> pmu->fixed_ctr_ctrl_mask &=
> ~(1ULL << (INTEL_PMC_IDX_FIXED + i * 4));
> }
>
> base-commit: d8708b80fa0e6e21bc0c9e7276ad0bccef73b6e7
> --
> 2.40.0
>