Skip to content

Commit 5ed12ae

Browse files
suomilewissean-jc
authored andcommitted
selftests: kvm/x86: Test masked events
Add testing to show that a pmu event can be filtered with a generalized match on it's unit mask. These tests set up test cases to demonstrate various ways of filtering a pmu event that has multiple unit mask values. It does this by setting up the filter in KVM with the masked events provided, then enabling three pmu counters in the guest. The test then verifies that the pmu counters agree with which counters should be counting and which counters should be filtered for both a sparse filter list and a dense filter list. Signed-off-by: Aaron Lewis <aaronlewis@google.com> Link: https://lore.kernel.org/r/20221220161236.555143-8-aaronlewis@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent b1a8657 commit 5ed12ae

File tree

1 file changed

+336
-2
lines changed

1 file changed

+336
-2
lines changed

tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c

Lines changed: 336 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,331 @@ static bool use_amd_pmu(void)
404404
is_zen3(family, model));
405405
}
406406

407+
/*
408+
* "MEM_INST_RETIRED.ALL_LOADS", "MEM_INST_RETIRED.ALL_STORES", and
409+
* "MEM_INST_RETIRED.ANY" from https://perfmon-events.intel.com/
410+
* supported on Intel Xeon processors:
411+
* - Sapphire Rapids, Ice Lake, Cascade Lake, Skylake.
412+
*/
413+
#define MEM_INST_RETIRED 0xD0
414+
#define MEM_INST_RETIRED_LOAD EVENT(MEM_INST_RETIRED, 0x81)
415+
#define MEM_INST_RETIRED_STORE EVENT(MEM_INST_RETIRED, 0x82)
416+
#define MEM_INST_RETIRED_LOAD_STORE EVENT(MEM_INST_RETIRED, 0x83)
417+
418+
static bool supports_event_mem_inst_retired(void)
419+
{
420+
uint32_t eax, ebx, ecx, edx;
421+
422+
cpuid(1, &eax, &ebx, &ecx, &edx);
423+
if (x86_family(eax) == 0x6) {
424+
switch (x86_model(eax)) {
425+
/* Sapphire Rapids */
426+
case 0x8F:
427+
/* Ice Lake */
428+
case 0x6A:
429+
/* Skylake */
430+
/* Cascade Lake */
431+
case 0x55:
432+
return true;
433+
}
434+
}
435+
436+
return false;
437+
}
438+
439+
/*
440+
* "LS Dispatch", from Processor Programming Reference
441+
* (PPR) for AMD Family 17h Model 01h, Revision B1 Processors,
442+
* Preliminary Processor Programming Reference (PPR) for AMD Family
443+
* 17h Model 31h, Revision B0 Processors, and Preliminary Processor
444+
* Programming Reference (PPR) for AMD Family 19h Model 01h, Revision
445+
* B1 Processors Volume 1 of 2.
446+
*/
447+
#define LS_DISPATCH 0x29
448+
#define LS_DISPATCH_LOAD EVENT(LS_DISPATCH, BIT(0))
449+
#define LS_DISPATCH_STORE EVENT(LS_DISPATCH, BIT(1))
450+
#define LS_DISPATCH_LOAD_STORE EVENT(LS_DISPATCH, BIT(2))
451+
452+
#define INCLUDE_MASKED_ENTRY(event_select, mask, match) \
453+
KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, false)
454+
#define EXCLUDE_MASKED_ENTRY(event_select, mask, match) \
455+
KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, true)
456+
457+
struct perf_counter {
458+
union {
459+
uint64_t raw;
460+
struct {
461+
uint64_t loads:22;
462+
uint64_t stores:22;
463+
uint64_t loads_stores:20;
464+
};
465+
};
466+
};
467+
468+
static uint64_t masked_events_guest_test(uint32_t msr_base)
469+
{
470+
uint64_t ld0, ld1, st0, st1, ls0, ls1;
471+
struct perf_counter c;
472+
int val;
473+
474+
/*
475+
* The acutal value of the counters don't determine the outcome of
476+
* the test. Only that they are zero or non-zero.
477+
*/
478+
ld0 = rdmsr(msr_base + 0);
479+
st0 = rdmsr(msr_base + 1);
480+
ls0 = rdmsr(msr_base + 2);
481+
482+
__asm__ __volatile__("movl $0, %[v];"
483+
"movl %[v], %%eax;"
484+
"incl %[v];"
485+
: [v]"+m"(val) :: "eax");
486+
487+
ld1 = rdmsr(msr_base + 0);
488+
st1 = rdmsr(msr_base + 1);
489+
ls1 = rdmsr(msr_base + 2);
490+
491+
c.loads = ld1 - ld0;
492+
c.stores = st1 - st0;
493+
c.loads_stores = ls1 - ls0;
494+
495+
return c.raw;
496+
}
497+
498+
static void intel_masked_events_guest_code(void)
499+
{
500+
uint64_t r;
501+
502+
for (;;) {
503+
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
504+
505+
wrmsr(MSR_P6_EVNTSEL0 + 0, ARCH_PERFMON_EVENTSEL_ENABLE |
506+
ARCH_PERFMON_EVENTSEL_OS | MEM_INST_RETIRED_LOAD);
507+
wrmsr(MSR_P6_EVNTSEL0 + 1, ARCH_PERFMON_EVENTSEL_ENABLE |
508+
ARCH_PERFMON_EVENTSEL_OS | MEM_INST_RETIRED_STORE);
509+
wrmsr(MSR_P6_EVNTSEL0 + 2, ARCH_PERFMON_EVENTSEL_ENABLE |
510+
ARCH_PERFMON_EVENTSEL_OS | MEM_INST_RETIRED_LOAD_STORE);
511+
512+
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0x7);
513+
514+
r = masked_events_guest_test(MSR_IA32_PMC0);
515+
516+
GUEST_SYNC(r);
517+
}
518+
}
519+
520+
static void amd_masked_events_guest_code(void)
521+
{
522+
uint64_t r;
523+
524+
for (;;) {
525+
wrmsr(MSR_K7_EVNTSEL0, 0);
526+
wrmsr(MSR_K7_EVNTSEL1, 0);
527+
wrmsr(MSR_K7_EVNTSEL2, 0);
528+
529+
wrmsr(MSR_K7_EVNTSEL0, ARCH_PERFMON_EVENTSEL_ENABLE |
530+
ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_LOAD);
531+
wrmsr(MSR_K7_EVNTSEL1, ARCH_PERFMON_EVENTSEL_ENABLE |
532+
ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_STORE);
533+
wrmsr(MSR_K7_EVNTSEL2, ARCH_PERFMON_EVENTSEL_ENABLE |
534+
ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_LOAD_STORE);
535+
536+
r = masked_events_guest_test(MSR_K7_PERFCTR0);
537+
538+
GUEST_SYNC(r);
539+
}
540+
}
541+
542+
static struct perf_counter run_masked_events_test(struct kvm_vcpu *vcpu,
543+
const uint64_t masked_events[],
544+
const int nmasked_events)
545+
{
546+
struct kvm_pmu_event_filter *f;
547+
struct perf_counter r;
548+
549+
f = create_pmu_event_filter(masked_events, nmasked_events,
550+
KVM_PMU_EVENT_ALLOW,
551+
KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
552+
r.raw = test_with_filter(vcpu, f);
553+
free(f);
554+
555+
return r;
556+
}
557+
558+
/* Matches KVM_PMU_EVENT_FILTER_MAX_EVENTS in pmu.c */
559+
#define MAX_FILTER_EVENTS 300
560+
#define MAX_TEST_EVENTS 10
561+
562+
#define ALLOW_LOADS BIT(0)
563+
#define ALLOW_STORES BIT(1)
564+
#define ALLOW_LOADS_STORES BIT(2)
565+
566+
struct masked_events_test {
567+
uint64_t intel_events[MAX_TEST_EVENTS];
568+
uint64_t intel_event_end;
569+
uint64_t amd_events[MAX_TEST_EVENTS];
570+
uint64_t amd_event_end;
571+
const char *msg;
572+
uint32_t flags;
573+
};
574+
575+
/*
576+
* These are the test cases for the masked events tests.
577+
*
578+
* For each test, the guest enables 3 PMU counters (loads, stores,
579+
* loads + stores). The filter is then set in KVM with the masked events
580+
* provided. The test then verifies that the counters agree with which
581+
* ones should be counting and which ones should be filtered.
582+
*/
583+
const struct masked_events_test test_cases[] = {
584+
{
585+
.intel_events = {
586+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x81),
587+
},
588+
.amd_events = {
589+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(0)),
590+
},
591+
.msg = "Only allow loads.",
592+
.flags = ALLOW_LOADS,
593+
}, {
594+
.intel_events = {
595+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x82),
596+
},
597+
.amd_events = {
598+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(1)),
599+
},
600+
.msg = "Only allow stores.",
601+
.flags = ALLOW_STORES,
602+
}, {
603+
.intel_events = {
604+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x83),
605+
},
606+
.amd_events = {
607+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(2)),
608+
},
609+
.msg = "Only allow loads + stores.",
610+
.flags = ALLOW_LOADS_STORES,
611+
}, {
612+
.intel_events = {
613+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0x7C, 0),
614+
EXCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x83),
615+
},
616+
.amd_events = {
617+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, ~(BIT(0) | BIT(1)), 0),
618+
},
619+
.msg = "Only allow loads and stores.",
620+
.flags = ALLOW_LOADS | ALLOW_STORES,
621+
}, {
622+
.intel_events = {
623+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0x7C, 0),
624+
EXCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFF, 0x82),
625+
},
626+
.amd_events = {
627+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xF8, 0),
628+
EXCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(1)),
629+
},
630+
.msg = "Only allow loads and loads + stores.",
631+
.flags = ALLOW_LOADS | ALLOW_LOADS_STORES
632+
}, {
633+
.intel_events = {
634+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0xFE, 0x82),
635+
},
636+
.amd_events = {
637+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xF8, 0),
638+
EXCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xFF, BIT(0)),
639+
},
640+
.msg = "Only allow stores and loads + stores.",
641+
.flags = ALLOW_STORES | ALLOW_LOADS_STORES
642+
}, {
643+
.intel_events = {
644+
INCLUDE_MASKED_ENTRY(MEM_INST_RETIRED, 0x7C, 0),
645+
},
646+
.amd_events = {
647+
INCLUDE_MASKED_ENTRY(LS_DISPATCH, 0xF8, 0),
648+
},
649+
.msg = "Only allow loads, stores, and loads + stores.",
650+
.flags = ALLOW_LOADS | ALLOW_STORES | ALLOW_LOADS_STORES
651+
},
652+
};
653+
654+
static int append_test_events(const struct masked_events_test *test,
655+
uint64_t *events, int nevents)
656+
{
657+
const uint64_t *evts;
658+
int i;
659+
660+
evts = use_intel_pmu() ? test->intel_events : test->amd_events;
661+
for (i = 0; i < MAX_TEST_EVENTS; i++) {
662+
if (evts[i] == 0)
663+
break;
664+
665+
events[nevents + i] = evts[i];
666+
}
667+
668+
return nevents + i;
669+
}
670+
671+
static bool bool_eq(bool a, bool b)
672+
{
673+
return a == b;
674+
}
675+
676+
static void run_masked_events_tests(struct kvm_vcpu *vcpu, uint64_t *events,
677+
int nevents)
678+
{
679+
int ntests = ARRAY_SIZE(test_cases);
680+
struct perf_counter c;
681+
int i, n;
682+
683+
for (i = 0; i < ntests; i++) {
684+
const struct masked_events_test *test = &test_cases[i];
685+
686+
/* Do any test case events overflow MAX_TEST_EVENTS? */
687+
assert(test->intel_event_end == 0);
688+
assert(test->amd_event_end == 0);
689+
690+
n = append_test_events(test, events, nevents);
691+
692+
c = run_masked_events_test(vcpu, events, n);
693+
TEST_ASSERT(bool_eq(c.loads, test->flags & ALLOW_LOADS) &&
694+
bool_eq(c.stores, test->flags & ALLOW_STORES) &&
695+
bool_eq(c.loads_stores,
696+
test->flags & ALLOW_LOADS_STORES),
697+
"%s loads: %u, stores: %u, loads + stores: %u",
698+
test->msg, c.loads, c.stores, c.loads_stores);
699+
}
700+
}
701+
702+
static void add_dummy_events(uint64_t *events, int nevents)
703+
{
704+
int i;
705+
706+
for (i = 0; i < nevents; i++) {
707+
int event_select = i % 0xFF;
708+
bool exclude = ((i % 4) == 0);
709+
710+
if (event_select == MEM_INST_RETIRED ||
711+
event_select == LS_DISPATCH)
712+
event_select++;
713+
714+
events[i] = KVM_PMU_ENCODE_MASKED_ENTRY(event_select, 0,
715+
0, exclude);
716+
}
717+
}
718+
719+
static void test_masked_events(struct kvm_vcpu *vcpu)
720+
{
721+
int nevents = MAX_FILTER_EVENTS - MAX_TEST_EVENTS;
722+
uint64_t events[MAX_FILTER_EVENTS];
723+
724+
/* Run the test cases against a sparse PMU event filter. */
725+
run_masked_events_tests(vcpu, events, 0);
726+
727+
/* Run the test cases against a dense PMU event filter. */
728+
add_dummy_events(events, MAX_FILTER_EVENTS);
729+
run_masked_events_tests(vcpu, events, nevents);
730+
}
731+
407732
static int run_filter_test(struct kvm_vcpu *vcpu, const uint64_t *events,
408733
int nevents, uint32_t flags)
409734
{
@@ -432,15 +757,15 @@ static void test_filter_ioctl(struct kvm_vcpu *vcpu)
432757
r = run_filter_test(vcpu, &e, 1, KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
433758
TEST_ASSERT(r != 0, "Invalid PMU Event Filter is expected to fail");
434759

435-
e = KVM_PMU_EVENT_ENCODE_MASKED_ENTRY(0xff, 0xff, 0xff, 0xf);
760+
e = KVM_PMU_ENCODE_MASKED_ENTRY(0xff, 0xff, 0xff, 0xf);
436761
r = run_filter_test(vcpu, &e, 1, KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
437762
TEST_ASSERT(r == 0, "Valid PMU Event Filter is failing");
438763
}
439764

440765
int main(int argc, char *argv[])
441766
{
442767
void (*guest_code)(void);
443-
struct kvm_vcpu *vcpu;
768+
struct kvm_vcpu *vcpu, *vcpu2 = NULL;
444769
struct kvm_vm *vm;
445770

446771
TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_FILTER));
@@ -465,6 +790,15 @@ int main(int argc, char *argv[])
465790
test_not_member_deny_list(vcpu);
466791
test_not_member_allow_list(vcpu);
467792

793+
if (use_intel_pmu() &&
794+
supports_event_mem_inst_retired() &&
795+
kvm_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS) >= 3)
796+
vcpu2 = vm_vcpu_add(vm, 2, intel_masked_events_guest_code);
797+
else if (use_amd_pmu())
798+
vcpu2 = vm_vcpu_add(vm, 2, amd_masked_events_guest_code);
799+
800+
if (vcpu2)
801+
test_masked_events(vcpu2);
468802
test_filter_ioctl(vcpu);
469803

470804
kvm_vm_free(vm);

0 commit comments

Comments
 (0)