Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: selftests: Handle Intel Atom errata that leads to PMU event overcount

Add a PMU errata framework and use it to relax precise event counts on
Atom platforms that overcount "Instruction Retired" and "Branch Instruction
Retired" events, as the overcount issues on VM-Exit/VM-Entry are impossible
to prevent from userspace, e.g. the test can't prevent host IRQs.

Setup errata during early initialization and automatically sync the mask
to VMs so that tests can check for errata without having to manually
manage host=>guest variables.

For Intel Atom CPUs, the PMU events "Instruction Retired" or
"Branch Instruction Retired" may be overcounted for some certain
instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD
and complex SGX/SMX/CSTATE instructions/flows.

The detailed information can be found in the errata (section SRF7):
https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/

For the Atom platforms before Sierra Forest (including Sierra Forest),
Both 2 events "Instruction Retired" and "Branch Instruction Retired" would
be overcounted on these certain instructions, but for Clearwater Forest
only "Instruction Retired" event is overcounted on these instructions.

Signed-off-by: dongsheng <dongsheng.x.zhang@intel.com>
Co-developed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Signed-off-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Tested-by: Yi Lai <yi1.lai@intel.com>
Co-developed-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Tested-by: Dapeng Mi <dapeng1.mi@linux.intel.com>
Link: https://lore.kernel.org/r/20250919214648.1585683-6-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

authored by

dongsheng and committed by
Sean Christopherson
c435978e 2922b595

+77 -3
+16
tools/testing/selftests/kvm/include/x86/pmu.h
··· 5 5 #ifndef SELFTEST_KVM_PMU_H 6 6 #define SELFTEST_KVM_PMU_H 7 7 8 + #include <stdbool.h> 8 9 #include <stdint.h> 10 + 11 + #include <linux/bits.h> 9 12 10 13 #define KVM_PMU_EVENT_FILTER_MAX_EVENTS 300 11 14 ··· 106 103 107 104 extern const uint64_t intel_pmu_arch_events[]; 108 105 extern const uint64_t amd_pmu_zen_events[]; 106 + 107 + enum pmu_errata { 108 + INSTRUCTIONS_RETIRED_OVERCOUNT, 109 + BRANCHES_RETIRED_OVERCOUNT, 110 + }; 111 + extern uint64_t pmu_errata_mask; 112 + 113 + void kvm_init_pmu_errata(void); 114 + 115 + static inline bool this_pmu_has_errata(enum pmu_errata errata) 116 + { 117 + return pmu_errata_mask & BIT_ULL(errata); 118 + } 109 119 110 120 #endif /* SELFTEST_KVM_PMU_H */
+44
tools/testing/selftests/kvm/lib/x86/pmu.c
··· 8 8 #include <linux/kernel.h> 9 9 10 10 #include "kvm_util.h" 11 + #include "processor.h" 11 12 #include "pmu.h" 12 13 13 14 const uint64_t intel_pmu_arch_events[] = { ··· 35 34 AMD_ZEN_BRANCHES_MISPREDICTED, 36 35 }; 37 36 kvm_static_assert(ARRAY_SIZE(amd_pmu_zen_events) == NR_AMD_ZEN_EVENTS); 37 + 38 + /* 39 + * For Intel Atom CPUs, the PMU events "Instruction Retired" or 40 + * "Branch Instruction Retired" may be overcounted for some certain 41 + * instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD 42 + * and complex SGX/SMX/CSTATE instructions/flows. 43 + * 44 + * The detailed information can be found in the errata (section SRF7): 45 + * https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/ 46 + * 47 + * For the Atom platforms before Sierra Forest (including Sierra Forest), 48 + * Both 2 events "Instruction Retired" and "Branch Instruction Retired" would 49 + * be overcounted on these certain instructions, but for Clearwater Forest 50 + * only "Instruction Retired" event is overcounted on these instructions. 51 + */ 52 + static uint64_t get_pmu_errata(void) 53 + { 54 + if (!this_cpu_is_intel()) 55 + return 0; 56 + 57 + if (this_cpu_family() != 0x6) 58 + return 0; 59 + 60 + switch (this_cpu_model()) { 61 + case 0xDD: /* Clearwater Forest */ 62 + return BIT_ULL(INSTRUCTIONS_RETIRED_OVERCOUNT); 63 + case 0xAF: /* Sierra Forest */ 64 + case 0x4D: /* Avaton, Rangely */ 65 + case 0x5F: /* Denverton */ 66 + case 0x86: /* Jacobsville */ 67 + return BIT_ULL(INSTRUCTIONS_RETIRED_OVERCOUNT) | 68 + BIT_ULL(BRANCHES_RETIRED_OVERCOUNT); 69 + default: 70 + return 0; 71 + } 72 + } 73 + 74 + uint64_t pmu_errata_mask; 75 + 76 + void kvm_init_pmu_errata(void) 77 + { 78 + pmu_errata_mask = get_pmu_errata(); 79 + }
+4
tools/testing/selftests/kvm/lib/x86/processor.c
··· 6 6 #include "linux/bitmap.h" 7 7 #include "test_util.h" 8 8 #include "kvm_util.h" 9 + #include "pmu.h" 9 10 #include "processor.h" 10 11 #include "sev.h" 11 12 ··· 639 638 sync_global_to_guest(vm, host_cpu_is_intel); 640 639 sync_global_to_guest(vm, host_cpu_is_amd); 641 640 sync_global_to_guest(vm, is_forced_emulation_enabled); 641 + sync_global_to_guest(vm, pmu_errata_mask); 642 642 643 643 if (is_sev_vm(vm)) { 644 644 struct kvm_sev_init init = { 0 }; ··· 1271 1269 host_cpu_is_intel = this_cpu_is_intel(); 1272 1270 host_cpu_is_amd = this_cpu_is_amd(); 1273 1271 is_forced_emulation_enabled = kvm_is_forced_emulation_enabled(); 1272 + 1273 + kvm_init_pmu_errata(); 1274 1274 } 1275 1275 1276 1276 bool sys_clocksource_is_based_on_tsc(void)
+10 -2
tools/testing/selftests/kvm/x86/pmu_counters_test.c
··· 163 163 164 164 switch (idx) { 165 165 case INTEL_ARCH_INSTRUCTIONS_RETIRED_INDEX: 166 - GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); 166 + /* Relax precise count check due to VM-EXIT/VM-ENTRY overcount issue */ 167 + if (this_pmu_has_errata(INSTRUCTIONS_RETIRED_OVERCOUNT)) 168 + GUEST_ASSERT(count >= NUM_INSNS_RETIRED); 169 + else 170 + GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); 167 171 break; 168 172 case INTEL_ARCH_BRANCHES_RETIRED_INDEX: 169 - GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); 173 + /* Relax precise count check due to VM-EXIT/VM-ENTRY overcount issue */ 174 + if (this_pmu_has_errata(BRANCHES_RETIRED_OVERCOUNT)) 175 + GUEST_ASSERT(count >= NUM_BRANCH_INSNS_RETIRED); 176 + else 177 + GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); 170 178 break; 171 179 case INTEL_ARCH_LLC_REFERENCES_INDEX: 172 180 case INTEL_ARCH_LLC_MISSES_INDEX:
+3 -1
tools/testing/selftests/kvm/x86/pmu_event_filter_test.c
··· 214 214 do { \ 215 215 uint64_t br = pmc_results.branches_retired; \ 216 216 uint64_t ir = pmc_results.instructions_retired; \ 217 + bool br_matched = this_pmu_has_errata(BRANCHES_RETIRED_OVERCOUNT) ? \ 218 + br >= NUM_BRANCHES : br == NUM_BRANCHES; \ 217 219 \ 218 - if (br && br != NUM_BRANCHES) \ 220 + if (br && !br_matched) \ 219 221 pr_info("%s: Branch instructions retired = %lu (expected %u)\n", \ 220 222 __func__, br, NUM_BRANCHES); \ 221 223 TEST_ASSERT(br, "%s: Branch instructions retired = %lu (expected > 0)", \