[PATCH kvm/queue v2 1/3] KVM: x86/pmu: Replace pmu->available_event_types with a new BITMAP

From: Like Xu
Date: Mon Jan 17 2022 - 03:53:34 EST


From: Like Xu <likexu@xxxxxxxxxxx>

Currently, KVM refuses to create a perf_event for a counter if its
requested hw event is prompted as unavailable according to the
Intel CPUID CPUID 0x0A.EBX bit vector. We replace the basis for
this validation with the kernel generic and common enum perf_hw_id{}.
This helps to remove the use of static {intel,amd}_arch_events[] later on,
as it is not constant across platforms.

Signed-off-by: Like Xu <likexu@xxxxxxxxxxx>
---
arch/x86/include/asm/kvm_host.h | 2 +-
arch/x86/kvm/vmx/pmu_intel.c | 40 +++++++++++++++++++++++++++------
2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 5d97f4adc1cb..03fabf22e167 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -501,7 +501,6 @@ struct kvm_pmc {
struct kvm_pmu {
unsigned nr_arch_gp_counters;
unsigned nr_arch_fixed_counters;
- unsigned available_event_types;
u64 fixed_ctr_ctrl;
u64 global_ctrl;
u64 global_status;
@@ -516,6 +515,7 @@ struct kvm_pmu {
DECLARE_BITMAP(reprogram_pmi, X86_PMC_IDX_MAX);
DECLARE_BITMAP(all_valid_pmc_idx, X86_PMC_IDX_MAX);
DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX);
+ DECLARE_BITMAP(avail_perf_hw_ids, PERF_COUNT_HW_MAX);

/*
* The gate to release perf_events not marked in
diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c
index ffccfd9823c0..1ba8f0f0098b 100644
--- a/arch/x86/kvm/vmx/pmu_intel.c
+++ b/arch/x86/kvm/vmx/pmu_intel.c
@@ -74,6 +74,7 @@ static unsigned int intel_pmc_perf_hw_id(struct kvm_pmc *pmc)
u8 event_select = pmc->eventsel & ARCH_PERFMON_EVENTSEL_EVENT;
u8 unit_mask = (pmc->eventsel & ARCH_PERFMON_EVENTSEL_UMASK) >> 8;
int i;
+ unsigned int event_type = PERF_COUNT_HW_MAX;

for (i = 0; i < ARRAY_SIZE(intel_arch_events); i++) {
if (intel_arch_events[i].eventsel != event_select ||
@@ -81,16 +82,14 @@ static unsigned int intel_pmc_perf_hw_id(struct kvm_pmc *pmc)
continue;

/* disable event that reported as not present by cpuid */
- if ((i < 7) && !(pmu->available_event_types & (1 << i)))
+ event_type = intel_arch_events[i].event_type;
+ if (!test_bit(event_type, pmu->avail_perf_hw_ids))
return PERF_COUNT_HW_MAX + 1;

break;
}

- if (i == ARRAY_SIZE(intel_arch_events))
- return PERF_COUNT_HW_MAX;
-
- return intel_arch_events[i].event_type;
+ return event_type;
}

/* check if a PMC is enabled by comparing it with globl_ctrl bits. */
@@ -469,6 +468,25 @@ static void setup_fixed_pmc_eventsel(struct kvm_pmu *pmu)
}
}

+/* Mapping between CPUID 0x0A.EBX bit vector and enum perf_hw_id. */
+static inline int map_unavail_bit_to_perf_hw_id(int bit)
+{
+ switch (bit) {
+ case 0:
+ case 1:
+ return bit;
+ case 2:
+ return PERF_COUNT_HW_BUS_CYCLES;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ return --bit;
+ }
+
+ return PERF_COUNT_HW_MAX;
+}
+
static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
{
struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -478,6 +496,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
struct kvm_cpuid_entry2 *entry;
union cpuid10_eax eax;
union cpuid10_edx edx;
+ unsigned long available_cpuid_events;
+ int bit;

pmu->nr_arch_gp_counters = 0;
pmu->nr_arch_fixed_counters = 0;
@@ -503,8 +523,14 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
eax.split.bit_width = min_t(int, eax.split.bit_width, x86_pmu.bit_width_gp);
pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1;
eax.split.mask_length = min_t(int, eax.split.mask_length, x86_pmu.events_mask_len);
- pmu->available_event_types = ~entry->ebx &
- ((1ull << eax.split.mask_length) - 1);
+ /*
+ * The number of valid EBX bits should be less than the number of valid perf_hw_ids.
+ * Otherwise, we need to additionally determine if the event is rejected by KVM.
+ */
+ available_cpuid_events = ~entry->ebx & ((1ull << eax.split.mask_length) - 1);
+ bitmap_fill(pmu->avail_perf_hw_ids, PERF_COUNT_HW_MAX);
+ for_each_clear_bit(bit, (unsigned long *)&available_cpuid_events, eax.split.mask_length)
+ __clear_bit(map_unavail_bit_to_perf_hw_id(bit), pmu->avail_perf_hw_ids);

if (pmu->version == 1) {
pmu->nr_arch_fixed_counters = 0;
--
2.33.1