Message ID | 20230814115108.45741-4-cloudliang@tencent.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: selftests: Test the consistency of the PMU's CPUID and its features | expand |
On Mon, Aug 14, 2023, Jinrong Liang wrote: > +static void test_arch_events_cpuid(struct kvm_vcpu *vcpu, > + uint8_t arch_events_bitmap_size, > + uint8_t arch_events_unavailable_mask, > + uint8_t idx) > +{ > + uint64_t counter_val = 0; > + bool is_supported; > + > + vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH, > + arch_events_bitmap_size); > + vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EVENTS_MASK, > + arch_events_unavailable_mask); > + > + is_supported = arch_event_is_supported(vcpu, idx); > + vcpu_args_set(vcpu, 1, intel_arch_events[idx]); > + > + while (run_vcpu(vcpu, &counter_val) != UCALL_DONE) > + TEST_ASSERT_EQ(is_supported, !!counter_val); > +} > + > +static void intel_check_arch_event_is_unavl(uint8_t idx) > +{ > + uint8_t eax_evt_vec, ebx_unavl_mask, i, j; > + struct kvm_vcpu *vcpu; > + struct kvm_vm *vm; > + > + /* > + * A brute force iteration of all combinations of values is likely to > + * exhaust the limit of the single-threaded thread fd nums, so it's > + * tested here by iterating through all valid values on a single bit. > + */ > + for (i = 0; i < ARRAY_SIZE(intel_arch_events); i++) { > + eax_evt_vec = BIT_ULL(i); > + for (j = 0; j < ARRAY_SIZE(intel_arch_events); j++) { > + ebx_unavl_mask = BIT_ULL(j); > + vm = pmu_vm_create_with_one_vcpu(&vcpu, > + guest_measure_loop); > + test_arch_events_cpuid(vcpu, eax_evt_vec, > + ebx_unavl_mask, idx); > + > + kvm_vm_free(vm); This is messy. If you're going to use a helper, then use the helper. If not, then open code everything. Half and half just makes everything unnecessarily hard to follow. E.g. if you reorganize things, and move even more checks into the guest, I think you can end up with: static void test_arch_events_cpuid(uint8_t i, uint8_t j, uint8_t idx) { uint8_t eax_evt_vec = BIT_ULL(i); uint8_t ebx_unavl_mask = BIT_ULL(j); struct kvm_vcpu *vcpu; struct kvm_vm *vm; vm = pmu_vm_create_with_one_vcpu(&vcpu, guest_measure_loop); vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH, arch_events_bitmap_size); vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EVENTS_MASK, arch_events_unavailable_mask); vcpu_args_set(vcpu, 1, idx); run_vcpu(vcpu, &counter_val) kvm_vm_free(vm); } static void intel_check_arch_event_is_unavl(uint8_t idx) { /* * A brute force iteration of all combinations of values is likely to * exhaust the limit of the single-threaded thread fd nums, so it's * tested here by iterating through all valid values on a single bit. */ for (i = 0; i < ARRAY_SIZE(intel_arch_events); i++) { eax_evt_vec = BIT_ULL(i); for (j = 0; j < ARRAY_SIZE(intel_arch_events); j++) test_arch_events_cpuid(i, j, idx); } }
On Mon, Aug 14, 2023, Jinrong Liang wrote: > Add test case for AMD Guest PerfMonV2. Also test Intel > MSR_CORE_PERF_GLOBAL_STATUS and MSR_CORE_PERF_GLOBAL_OVF_CTRL. > > Signed-off-by: Jinrong Liang <cloudliang@tencent.com> > --- > .../kvm/x86_64/pmu_basic_functionality_test.c | 48 ++++++++++++++++++- > 1 file changed, 46 insertions(+), 2 deletions(-) > > diff --git a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c > index cb2a7ad5c504..02bd1fe3900b 100644 > --- a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c > +++ b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c > @@ -58,7 +58,9 @@ static uint64_t run_vcpu(struct kvm_vcpu *vcpu, uint64_t *ucall_arg) > > static void guest_measure_loop(uint64_t event_code) > { > + uint64_t global_ovf_ctrl_msr, global_status_msr, global_ctrl_msr; > uint8_t nr_gp_counters, pmu_version = 1; > + uint8_t gp_counter_bit_width = 48; > uint64_t event_sel_msr; > uint32_t counter_msr; > unsigned int i; > @@ -68,6 +70,12 @@ static void guest_measure_loop(uint64_t event_code) > pmu_version = this_cpu_property(X86_PROPERTY_PMU_VERSION); > event_sel_msr = MSR_P6_EVNTSEL0; > > + if (pmu_version > 1) { > + global_ovf_ctrl_msr = MSR_CORE_PERF_GLOBAL_OVF_CTRL; > + global_status_msr = MSR_CORE_PERF_GLOBAL_STATUS; > + global_ctrl_msr = MSR_CORE_PERF_GLOBAL_CTRL; > + } > + > if (rdmsr(MSR_IA32_PERF_CAPABILITIES) & PMU_CAP_FW_WRITES) > counter_msr = MSR_IA32_PMC0; > else > @@ -76,6 +84,17 @@ static void guest_measure_loop(uint64_t event_code) > nr_gp_counters = AMD64_NR_COUNTERS; > event_sel_msr = MSR_K7_EVNTSEL0; > counter_msr = MSR_K7_PERFCTR0; > + > + if (this_cpu_has(X86_FEATURE_AMD_PMU_EXT_CORE) && > + this_cpu_has(X86_FEATURE_AMD_PERFMON_V2)) { > + nr_gp_counters = this_cpu_property(X86_PROPERTY_AMD_PMU_NR_CORE_COUNTERS); > + global_ovf_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR; > + global_status_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS; > + global_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_CTL; > + event_sel_msr = MSR_F15H_PERF_CTL0; > + counter_msr = MSR_F15H_PERF_CTR0; > + pmu_version = 2; > + } Please use an if-else when the two things are completely exclusive, i.e. don't set "defaults" and then override them. > } > > for (i = 0; i < nr_gp_counters; i++) { > @@ -84,14 +103,39 @@ static void guest_measure_loop(uint64_t event_code) > ARCH_PERFMON_EVENTSEL_ENABLE | event_code); > > if (pmu_version > 1) { > - wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, BIT_ULL(i)); > + wrmsr(global_ctrl_msr, BIT_ULL(i)); > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > - wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); > + wrmsr(global_ctrl_msr, 0); > GUEST_SYNC(_rdpmc(i)); > } else { > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > GUEST_SYNC(_rdpmc(i)); > } This is extremely difficult to follow. I think the same thing to do is to split this up into helpers, e.g. send pmu_version > 1 into one path, and pmu_version <= 1 into an entirely different path. E.g. something like this? static void guest_measure_loop(uint64_t event_code) { uint64_t global_ovf_ctrl_msr, global_status_msr, global_ctrl_msr; uint8_t nr_gp_counters, pmu_version; uint8_t gp_counter_bit_width; uint64_t event_sel_msr; uint32_t counter_msr; unsigned int i; if (host_cpu_is_intel) pmu_version = this_cpu_property(X86_PROPERTY_PMU_VERSION); else if (this_cpu_has(X86_FEATURE_PERFCTR_CORE) && this_cpu_has(X86_FEATURE_PERFMON_V2)) { pmu_version = 2; } else { pmu_version = 1; } if (pmu_version <= 1) { guest_measure_pmu_legacy(...); return; } if (host_cpu_is_intel) { nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); global_ovf_ctrl_msr = MSR_CORE_PERF_GLOBAL_OVF_CTRL; global_status_msr = MSR_CORE_PERF_GLOBAL_STATUS; global_ctrl_msr = MSR_CORE_PERF_GLOBAL_CTRL; gp_counter_bit_width = this_cpu_property(X86_PROPERTY_PMU_GP_COUNTERS_BIT_WIDTH); if (rdmsr(MSR_IA32_PERF_CAPABILITIES) & PMU_CAP_FW_WRITES) counter_msr = MSR_IA32_PMC0; else counter_msr = MSR_IA32_PERFCTR0; } else { nr_gp_counters = this_cpu_property(X86_PROPERTY_AMD_PMU_NR_CORE_COUNTERS); global_ovf_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR; global_status_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS; global_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_CTL; event_sel_msr = MSR_F15H_PERF_CTL0; counter_msr = MSR_F15H_PERF_CTR0; gp_counter_bit_width = 48; } for (i = 0; i < nr_gp_counters; i++) { wrmsr(counter_msr + i, 0); wrmsr(event_sel_msr + i, ARCH_PERFMON_EVENTSEL_OS | ARCH_PERFMON_EVENTSEL_ENABLE | event_code); wrmsr(global_ctrl_msr, BIT_ULL(i)); __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); wrmsr(global_ctrl_msr, 0); counter = _rdpmc(i); GUEST_ASSERT_EQ(this_pmu_has(...), !!counter); if ( _rdpmc(i)) { wrmsr(global_ctrl_msr, 0); wrmsr(counter_msr + i, 0); __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); GUEST_ASSERT(!_rdpmc(i)); wrmsr(global_ctrl_msr, BIT_ULL(i)); __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); GUEST_ASSERT(_rdpmc(i)); wrmsr(global_ctrl_msr, 0); wrmsr(counter_msr + i, (1ULL << gp_counter_bit_width) - 2); wrmsr(global_ctrl_msr, BIT_ULL(i)); __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); GUEST_ASSERT(rdmsr(global_status_msr) & BIT_ULL(i)); wrmsr(global_ctrl_msr, 0); wrmsr(global_ovf_ctrl_msr, BIT_ULL(i)); GUEST_ASSERT(!(rdmsr(global_status_msr) & BIT_ULL(i))); } }
Sean Christopherson <seanjc@google.com> 于2023年8月18日周五 06:54写道: > > On Mon, Aug 14, 2023, Jinrong Liang wrote: > > Add test case for AMD Guest PerfMonV2. Also test Intel > > MSR_CORE_PERF_GLOBAL_STATUS and MSR_CORE_PERF_GLOBAL_OVF_CTRL. > > > > Signed-off-by: Jinrong Liang <cloudliang@tencent.com> > > --- > > .../kvm/x86_64/pmu_basic_functionality_test.c | 48 ++++++++++++++++++- > > 1 file changed, 46 insertions(+), 2 deletions(-) > > > > diff --git a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c > > index cb2a7ad5c504..02bd1fe3900b 100644 > > --- a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c > > +++ b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c > > @@ -58,7 +58,9 @@ static uint64_t run_vcpu(struct kvm_vcpu *vcpu, uint64_t *ucall_arg) > > > > static void guest_measure_loop(uint64_t event_code) > > { > > + uint64_t global_ovf_ctrl_msr, global_status_msr, global_ctrl_msr; > > uint8_t nr_gp_counters, pmu_version = 1; > > + uint8_t gp_counter_bit_width = 48; > > uint64_t event_sel_msr; > > uint32_t counter_msr; > > unsigned int i; > > @@ -68,6 +70,12 @@ static void guest_measure_loop(uint64_t event_code) > > pmu_version = this_cpu_property(X86_PROPERTY_PMU_VERSION); > > event_sel_msr = MSR_P6_EVNTSEL0; > > > > + if (pmu_version > 1) { > > + global_ovf_ctrl_msr = MSR_CORE_PERF_GLOBAL_OVF_CTRL; > > + global_status_msr = MSR_CORE_PERF_GLOBAL_STATUS; > > + global_ctrl_msr = MSR_CORE_PERF_GLOBAL_CTRL; > > + } > > + > > if (rdmsr(MSR_IA32_PERF_CAPABILITIES) & PMU_CAP_FW_WRITES) > > counter_msr = MSR_IA32_PMC0; > > else > > @@ -76,6 +84,17 @@ static void guest_measure_loop(uint64_t event_code) > > nr_gp_counters = AMD64_NR_COUNTERS; > > event_sel_msr = MSR_K7_EVNTSEL0; > > counter_msr = MSR_K7_PERFCTR0; > > + > > + if (this_cpu_has(X86_FEATURE_AMD_PMU_EXT_CORE) && > > + this_cpu_has(X86_FEATURE_AMD_PERFMON_V2)) { > > + nr_gp_counters = this_cpu_property(X86_PROPERTY_AMD_PMU_NR_CORE_COUNTERS); > > + global_ovf_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR; > > + global_status_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS; > > + global_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_CTL; > > + event_sel_msr = MSR_F15H_PERF_CTL0; > > + counter_msr = MSR_F15H_PERF_CTR0; > > + pmu_version = 2; > > + } > > Please use an if-else when the two things are completely exclusive, i.e. don't > set "defaults" and then override them. > > > } > > > > for (i = 0; i < nr_gp_counters; i++) { > > @@ -84,14 +103,39 @@ static void guest_measure_loop(uint64_t event_code) > > ARCH_PERFMON_EVENTSEL_ENABLE | event_code); > > > > if (pmu_version > 1) { > > - wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, BIT_ULL(i)); > > + wrmsr(global_ctrl_msr, BIT_ULL(i)); > > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > > - wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); > > + wrmsr(global_ctrl_msr, 0); > > GUEST_SYNC(_rdpmc(i)); > > } else { > > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > > GUEST_SYNC(_rdpmc(i)); > > } > > This is extremely difficult to follow. I think the same thing to do is to split > this up into helpers, e.g. send pmu_version > 1 into one path, and pmu_version <= 1 > into an entirely different path. > > E.g. something like this? I agree with all the proposed code changes you have provided. Your comments have been incredibly helpful in making the necessary improvements to the code. I will diligently follow your suggestions and modify the code accordingly. > > static void guest_measure_loop(uint64_t event_code) > { > uint64_t global_ovf_ctrl_msr, global_status_msr, global_ctrl_msr; > uint8_t nr_gp_counters, pmu_version; > uint8_t gp_counter_bit_width; > uint64_t event_sel_msr; > uint32_t counter_msr; > unsigned int i; > > if (host_cpu_is_intel) > pmu_version = this_cpu_property(X86_PROPERTY_PMU_VERSION); > else if (this_cpu_has(X86_FEATURE_PERFCTR_CORE) && > this_cpu_has(X86_FEATURE_PERFMON_V2)) { > pmu_version = 2; > } else { > pmu_version = 1; > } > > if (pmu_version <= 1) { > guest_measure_pmu_legacy(...); > return; > } > > if (host_cpu_is_intel) { > nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); > global_ovf_ctrl_msr = MSR_CORE_PERF_GLOBAL_OVF_CTRL; > global_status_msr = MSR_CORE_PERF_GLOBAL_STATUS; > global_ctrl_msr = MSR_CORE_PERF_GLOBAL_CTRL; > gp_counter_bit_width = this_cpu_property(X86_PROPERTY_PMU_GP_COUNTERS_BIT_WIDTH); > > if (rdmsr(MSR_IA32_PERF_CAPABILITIES) & PMU_CAP_FW_WRITES) > counter_msr = MSR_IA32_PMC0; > else > counter_msr = MSR_IA32_PERFCTR0; > } else { > nr_gp_counters = this_cpu_property(X86_PROPERTY_AMD_PMU_NR_CORE_COUNTERS); > global_ovf_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR; > global_status_msr = MSR_AMD64_PERF_CNTR_GLOBAL_STATUS; > global_ctrl_msr = MSR_AMD64_PERF_CNTR_GLOBAL_CTL; > event_sel_msr = MSR_F15H_PERF_CTL0; > counter_msr = MSR_F15H_PERF_CTR0; > gp_counter_bit_width = 48; > } > > for (i = 0; i < nr_gp_counters; i++) { > wrmsr(counter_msr + i, 0); > wrmsr(event_sel_msr + i, ARCH_PERFMON_EVENTSEL_OS | > ARCH_PERFMON_EVENTSEL_ENABLE | event_code); > > wrmsr(global_ctrl_msr, BIT_ULL(i)); > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > wrmsr(global_ctrl_msr, 0); > counter = _rdpmc(i); > GUEST_ASSERT_EQ(this_pmu_has(...), !!counter); > > if ( _rdpmc(i)) { > wrmsr(global_ctrl_msr, 0); > wrmsr(counter_msr + i, 0); > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > GUEST_ASSERT(!_rdpmc(i)); > > wrmsr(global_ctrl_msr, BIT_ULL(i)); > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > GUEST_ASSERT(_rdpmc(i)); > > wrmsr(global_ctrl_msr, 0); > wrmsr(counter_msr + i, (1ULL << gp_counter_bit_width) - 2); > wrmsr(global_ctrl_msr, BIT_ULL(i)); > __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); > GUEST_ASSERT(rdmsr(global_status_msr) & BIT_ULL(i)); > > wrmsr(global_ctrl_msr, 0); > wrmsr(global_ovf_ctrl_msr, BIT_ULL(i)); > GUEST_ASSERT(!(rdmsr(global_status_msr) & BIT_ULL(i))); > } > } > I truly appreciate your time and effort in reviewing the code and providing such valuable feedback. Please feel free to share any further suggestions or ideas in the future.
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 77026907968f..965a36562ef8 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -80,6 +80,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test TEST_GEN_PROGS_x86_64 += x86_64/monitor_mwait_test TEST_GEN_PROGS_x86_64 += x86_64/nested_exceptions_test TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test +TEST_GEN_PROGS_x86_64 += x86_64/pmu_basic_functionality_test TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test diff --git a/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c new file mode 100644 index 000000000000..c04eb0bdf69f --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/pmu_basic_functionality_test.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test the consistency of the PMU's CPUID and its features + * + * Copyright (C) 2023, Tencent, Inc. + * + * Check that the VM's PMU behaviour is consistent with the + * VM CPUID definition. + */ + +#define _GNU_SOURCE /* for program_invocation_short_name */ +#include <x86intrin.h> + +#include "pmu.h" + +/* Guest payload for any performance counter counting */ +#define NUM_BRANCHES 10 + +static struct kvm_vm *pmu_vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, + void *guest_code) +{ + struct kvm_vm *vm; + + vm = vm_create_with_one_vcpu(vcpu, guest_code); + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(*vcpu); + + return vm; +} + +static uint64_t run_vcpu(struct kvm_vcpu *vcpu, uint64_t *ucall_arg) +{ + struct ucall uc; + + vcpu_run(vcpu); + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + *ucall_arg = uc.args[1]; + break; + case UCALL_DONE: + break; + default: + TEST_FAIL("Unexpected ucall: %lu", uc.cmd); + } + return uc.cmd; +} + +static void guest_measure_loop(uint64_t event_code) +{ + uint32_t nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS); + uint32_t pmu_version = this_cpu_property(X86_PROPERTY_PMU_VERSION); + uint32_t counter_msr; + unsigned int i; + + if (rdmsr(MSR_IA32_PERF_CAPABILITIES) & PMU_CAP_FW_WRITES) + counter_msr = MSR_IA32_PMC0; + else + counter_msr = MSR_IA32_PERFCTR0; + + for (i = 0; i < nr_gp_counters; i++) { + wrmsr(counter_msr + i, 0); + wrmsr(MSR_P6_EVNTSEL0 + i, ARCH_PERFMON_EVENTSEL_OS | + ARCH_PERFMON_EVENTSEL_ENABLE | event_code); + + if (pmu_version > 1) { + wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, BIT_ULL(i)); + __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); + wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0); + GUEST_SYNC(_rdpmc(i)); + } else { + __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES})); + GUEST_SYNC(_rdpmc(i)); + } + } + + GUEST_DONE(); +} + +static void test_arch_events_cpuid(struct kvm_vcpu *vcpu, + uint8_t arch_events_bitmap_size, + uint8_t arch_events_unavailable_mask, + uint8_t idx) +{ + uint64_t counter_val = 0; + bool is_supported; + + vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH, + arch_events_bitmap_size); + vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EVENTS_MASK, + arch_events_unavailable_mask); + + is_supported = arch_event_is_supported(vcpu, idx); + vcpu_args_set(vcpu, 1, intel_arch_events[idx]); + + while (run_vcpu(vcpu, &counter_val) != UCALL_DONE) + TEST_ASSERT_EQ(is_supported, !!counter_val); +} + +static void intel_check_arch_event_is_unavl(uint8_t idx) +{ + uint8_t eax_evt_vec, ebx_unavl_mask, i, j; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + /* + * A brute force iteration of all combinations of values is likely to + * exhaust the limit of the single-threaded thread fd nums, so it's + * tested here by iterating through all valid values on a single bit. + */ + for (i = 0; i < ARRAY_SIZE(intel_arch_events); i++) { + eax_evt_vec = BIT_ULL(i); + for (j = 0; j < ARRAY_SIZE(intel_arch_events); j++) { + ebx_unavl_mask = BIT_ULL(j); + vm = pmu_vm_create_with_one_vcpu(&vcpu, + guest_measure_loop); + test_arch_events_cpuid(vcpu, eax_evt_vec, + ebx_unavl_mask, idx); + + kvm_vm_free(vm); + } + } +} + +static void intel_test_arch_events(void) +{ + uint8_t idx; + + for (idx = 0; idx < ARRAY_SIZE(intel_arch_events); idx++) { + /* + * Given the stability of performance event recurrence, + * only these arch events are currently being tested: + * + * - Core cycle event (idx = 0) + * - Instruction retired event (idx = 1) + * - Reference cycles event (idx = 2) + * - Branch instruction retired event (idx = 5) + */ + if (idx > INTEL_ARCH_INSTRUCTIONS_RETIRED && + idx != INTEL_ARCH_BRANCHES_RETIRED) + continue; + + intel_check_arch_event_is_unavl(idx); + } +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(get_kvm_param_bool("enable_pmu")); + + TEST_REQUIRE(host_cpu_is_intel); + TEST_REQUIRE(kvm_cpu_has_p(X86_PROPERTY_PMU_VERSION)); + TEST_REQUIRE(kvm_cpu_property(X86_PROPERTY_PMU_VERSION) > 0); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM)); + + intel_test_arch_events(); + + return 0; +}