KVM: x86/pmu: Allow programming events that match unsupported arch events
authorSean Christopherson <seanjc@google.com>
Tue, 9 Jan 2024 23:02:22 +0000 (15:02 -0800)
committerSean Christopherson <seanjc@google.com>
Tue, 30 Jan 2024 23:28:02 +0000 (15:28 -0800)
Remove KVM's bogus restriction that the guest can't program an event whose
encoding matches an unsupported architectural event.  The enumeration of
an architectural event only says that if a CPU supports an architectural
event, then the event can be programmed using the architectural encoding.
The enumeration does NOT say anything about the encoding when the CPU
doesn't report support the architectural event.

Preventing the guest from counting events whose encoding happens to match
an architectural event breaks existing functionality whenever Intel adds
an architectural encoding that was *ever* used for a CPU that doesn't
enumerate support for the architectural event, even if the encoding is for
the exact same event!

E.g. the architectural encoding for Top-Down Slots is 0x01a4.  Broadwell
CPUs, which do not support the Top-Down Slots architectural event, 0x01a4
is a valid, model-specific event.  Denying guest usage of 0x01a4 if/when
KVM adds support for Top-Down slots would break any Broadwell-based guest.

Reported-by: Kan Liang <kan.liang@linux.intel.com>
Closes: https://lore.kernel.org/all/2004baa6-b494-462c-a11f-8104ea152c6a@linux.intel.com
Fixes: a21864486f7e ("KVM: x86/pmu: Fix available_event_types check for REF_CPU_CYCLES event")
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Reviewed-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Kan Liang <kan.liang@linux.intel.com>
Tested-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Link: https://lore.kernel.org/r/20240109230250.424295-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/include/asm/kvm-x86-pmu-ops.h
arch/x86/kvm/pmu.c
arch/x86/kvm/pmu.h
arch/x86/kvm/svm/pmu.c
arch/x86/kvm/vmx/pmu_intel.c

index 058bc636356a1133ad151457d8bf0b56528e7f39..d7eebee4450c6bf5f1507ab35ad34b1a1036f226 100644 (file)
@@ -12,7 +12,6 @@ BUILD_BUG_ON(1)
  * a NULL definition, for example if "static_call_cond()" will be used
  * at the call sites.
  */
-KVM_X86_PMU_OP(hw_event_available)
 KVM_X86_PMU_OP(pmc_idx_to_pmc)
 KVM_X86_PMU_OP(rdpmc_ecx_to_pmc)
 KVM_X86_PMU_OP(msr_idx_to_pmc)
index 87cc6c8809ad88898894bd0ea6199ab70e2a91ac..30945fea69883d408a02569122f6a28006970cfe 100644 (file)
@@ -441,7 +441,6 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc)
 static bool pmc_event_is_allowed(struct kvm_pmc *pmc)
 {
        return pmc_is_globally_enabled(pmc) && pmc_speculative_in_use(pmc) &&
-              static_call(kvm_x86_pmu_hw_event_available)(pmc) &&
               check_pmu_event_filter(pmc);
 }
 
index 7caeb3d8d4fd1739bba12b0d133185fda8a041df..87ecf22f5b250d5b9ecddc00d9fa2a8cd077722c 100644 (file)
@@ -19,7 +19,6 @@
 #define VMWARE_BACKDOOR_PMC_APPARENT_TIME      0x10002
 
 struct kvm_pmu_ops {
-       bool (*hw_event_available)(struct kvm_pmc *pmc);
        struct kvm_pmc *(*pmc_idx_to_pmc)(struct kvm_pmu *pmu, int pmc_idx);
        struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu,
                unsigned int idx, u64 *mask);
index b6a7ad4d69145096d55e610ef8d789b87c2a5fb0..1475d47c821c4e6e436009962d662efbd8e13dec 100644 (file)
@@ -73,11 +73,6 @@ static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr,
        return amd_pmc_idx_to_pmc(pmu, idx);
 }
 
-static bool amd_hw_event_available(struct kvm_pmc *pmc)
-{
-       return true;
-}
-
 static bool amd_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
 {
        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -233,7 +228,6 @@ static void amd_pmu_init(struct kvm_vcpu *vcpu)
 }
 
 struct kvm_pmu_ops amd_pmu_ops __initdata = {
-       .hw_event_available = amd_hw_event_available,
        .pmc_idx_to_pmc = amd_pmc_idx_to_pmc,
        .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc,
        .msr_idx_to_pmc = amd_msr_idx_to_pmc,
index 8207f8c03585378847c8f1bccef2f4deeae0b304..1a7d021a6c7bb11934b6ec6d1e718ac0b0935890 100644 (file)
@@ -101,43 +101,6 @@ static struct kvm_pmc *intel_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx)
        }
 }
 
-static bool intel_hw_event_available(struct kvm_pmc *pmc)
-{
-       struct kvm_pmu *pmu = pmc_to_pmu(pmc);
-       u8 event_select = pmc->eventsel & ARCH_PERFMON_EVENTSEL_EVENT;
-       u8 unit_mask = (pmc->eventsel & ARCH_PERFMON_EVENTSEL_UMASK) >> 8;
-       int i;
-
-       /*
-        * Fixed counters are always available if KVM reaches this point.  If a
-        * fixed counter is unsupported in hardware or guest CPUID, KVM doesn't
-        * allow the counter's corresponding MSR to be written.  KVM does use
-        * architectural events to program fixed counters, as the interface to
-        * perf doesn't allow requesting a specific fixed counter, e.g. perf
-        * may (sadly) back a guest fixed PMC with a general purposed counter.
-        * But if _hardware_ doesn't support the associated event, KVM simply
-        * doesn't enumerate support for the fixed counter.
-        */
-       if (pmc_is_fixed(pmc))
-               return true;
-
-       BUILD_BUG_ON(ARRAY_SIZE(intel_arch_events) != NR_INTEL_ARCH_EVENTS);
-
-       /*
-        * Disallow events reported as unavailable in guest CPUID.  Note, this
-        * doesn't apply to pseudo-architectural events (see above).
-        */
-       for (i = 0; i < NR_REAL_INTEL_ARCH_EVENTS; i++) {
-               if (intel_arch_events[i].eventsel != event_select ||
-                   intel_arch_events[i].unit_mask != unit_mask)
-                       continue;
-
-               return pmu->available_event_types & BIT(i);
-       }
-
-       return true;
-}
-
 static bool intel_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx)
 {
        struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
@@ -780,7 +743,6 @@ void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu)
 }
 
 struct kvm_pmu_ops intel_pmu_ops __initdata = {
-       .hw_event_available = intel_hw_event_available,
        .pmc_idx_to_pmc = intel_pmc_idx_to_pmc,
        .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc,
        .msr_idx_to_pmc = intel_msr_idx_to_pmc,