/* Number of iterations of the loop for the guest measurement payload. */ #define NUM_LOOPS 10
/* Each iteration of the loop retires one branch instruction. */ #define NUM_BRANCH_INSNS_RETIRED (NUM_LOOPS)
/* * Number of instructions in each loop. 1 CLFLUSH/CLFLUSHOPT/NOP, 1 MFENCE, * 1 LOOP.
*/ #define NUM_INSNS_PER_LOOP 4
/* * Number of "extra" instructions that will be counted, i.e. the number of * instructions that are needed to set up the loop and then disable the * counter. 2 MOV, 2 XOR, 1 WRMSR.
*/ #define NUM_EXTRA_INSNS 5
/* Total number of instructions retired within the measured section. */ #define NUM_INSNS_RETIRED (NUM_LOOPS * NUM_INSNS_PER_LOOP + NUM_EXTRA_INSNS)
/* Track which architectural events are supported by hardware. */ static uint32_t hardware_pmu_arch_events;
/* * Wrap the array to appease the compiler, as the macros used to construct each * kvm_x86_pmu_feature use syntax that's only valid in function scope, and the * compiler often thinks the feature definitions aren't compile-time constants.
*/ staticstruct kvm_intel_pmu_event intel_event_to_feature(uint8_t idx)
{ conststruct kvm_intel_pmu_event __intel_event_to_feature[] = {
[INTEL_ARCH_CPU_CYCLES_INDEX] = { X86_PMU_FEATURE_CPU_CYCLES, X86_PMU_FEATURE_CPU_CYCLES_FIXED },
[INTEL_ARCH_INSTRUCTIONS_RETIRED_INDEX] = { X86_PMU_FEATURE_INSNS_RETIRED, X86_PMU_FEATURE_INSNS_RETIRED_FIXED }, /* * Note, the fixed counter for reference cycles is NOT the same as the * general purpose architectural event. The fixed counter explicitly * counts at the same frequency as the TSC, whereas the GP event counts * at a fixed, but uarch specific, frequency. Bundle them here for * simplicity.
*/
[INTEL_ARCH_REFERENCE_CYCLES_INDEX] = { X86_PMU_FEATURE_REFERENCE_CYCLES, X86_PMU_FEATURE_REFERENCE_TSC_CYCLES_FIXED },
[INTEL_ARCH_LLC_REFERENCES_INDEX] = { X86_PMU_FEATURE_LLC_REFERENCES, X86_PMU_FEATURE_NULL },
[INTEL_ARCH_LLC_MISSES_INDEX] = { X86_PMU_FEATURE_LLC_MISSES, X86_PMU_FEATURE_NULL },
[INTEL_ARCH_BRANCHES_RETIRED_INDEX] = { X86_PMU_FEATURE_BRANCH_INSNS_RETIRED, X86_PMU_FEATURE_NULL },
[INTEL_ARCH_BRANCHES_MISPREDICTED_INDEX] = { X86_PMU_FEATURE_BRANCHES_MISPREDICTED, X86_PMU_FEATURE_NULL },
[INTEL_ARCH_TOPDOWN_SLOTS_INDEX] = { X86_PMU_FEATURE_TOPDOWN_SLOTS, X86_PMU_FEATURE_TOPDOWN_SLOTS_FIXED },
};
vm = vm_create_with_one_vcpu(vcpu, guest_code);
sync_global_to_guest(vm, kvm_pmu_version);
sync_global_to_guest(vm, hardware_pmu_arch_events);
/* * Set PERF_CAPABILITIES before PMU version as KVM disallows enabling * features via PERF_CAPABILITIES if the guest doesn't have a vPMU.
*/ if (kvm_has_perf_caps)
vcpu_set_msr(*vcpu, MSR_IA32_PERF_CAPABILITIES, perf_capabilities);
do {
vcpu_run(vcpu); switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: break; case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc); break; case UCALL_PRINTF:
pr_info("%s", uc.buffer); break; case UCALL_DONE: break; default:
TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
}
} while (uc.cmd != UCALL_DONE);
}
static uint8_t guest_get_pmu_version(void)
{ /* * Return the effective PMU version, i.e. the minimum between what KVM * supports and what is enumerated to the guest. The host deliberately * advertises a PMU version to the guest beyond what is actually * supported by KVM to verify KVM doesn't freak out and do something * bizarre with an architecturally valid, but unsupported, version.
*/ return min_t(uint8_t, kvm_pmu_version, this_cpu_property(X86_PROPERTY_PMU_VERSION));
}
/* * If an architectural event is supported and guaranteed to generate at least * one "hit, assert that its count is non-zero. If an event isn't supported or * the test can't guarantee the associated action will occur, then all bets are * off regarding the count, i.e. no checks can be done. * * Sanity check that in all cases, the event doesn't count when it's disabled, * and that KVM correctly emulates the write of an arbitrary value.
*/ staticvoid guest_assert_event_count(uint8_t idx, uint32_t pmc, uint32_t pmc_msr)
{
uint64_t count;
count = _rdpmc(pmc); if (!(hardware_pmu_arch_events & BIT(idx))) goto sanity_checks;
switch (idx) { case INTEL_ARCH_INSTRUCTIONS_RETIRED_INDEX:
GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); break; case INTEL_ARCH_BRANCHES_RETIRED_INDEX:
GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); break; case INTEL_ARCH_LLC_REFERENCES_INDEX: case INTEL_ARCH_LLC_MISSES_INDEX: if (!this_cpu_has(X86_FEATURE_CLFLUSHOPT) &&
!this_cpu_has(X86_FEATURE_CLFLUSH)) break;
fallthrough; case INTEL_ARCH_CPU_CYCLES_INDEX: case INTEL_ARCH_REFERENCE_CYCLES_INDEX:
GUEST_ASSERT_NE(count, 0); break; case INTEL_ARCH_TOPDOWN_SLOTS_INDEX:
__GUEST_ASSERT(count >= NUM_INSNS_RETIRED, "Expected top-down slots >= %u, got count = %lu",
NUM_INSNS_RETIRED, count); break; default: break;
}
/* * Enable and disable the PMC in a monolithic asm blob to ensure that the * compiler can't insert _any_ code into the measured sequence. Note, ECX * doesn't need to be clobbered as the input value, @pmc_msr, is restored * before the end of the sequence. * * If CLFUSH{,OPT} is supported, flush the cacheline containing (at least) the * CLFUSH{,OPT} instruction on each loop iteration to force LLC references and * misses, i.e. to allow testing that those events actually count. * * If forced emulation is enabled (and specified), force emulation on a subset * of the measured code to verify that KVM correctly emulates instructions and * branches retired events in conjunction with hardware also counting said * events.
*/ #define GUEST_MEASURE_EVENT(_msr, _value, clflush, FEP) \ do { \
__asm__ __volatile__("wrmsr\n\t" \ " mov $" __stringify(NUM_LOOPS) ", %%ecx\n\t" \ "1:\n\t" \
clflush "\n\t" \ "mfence\n\t" \ "mov %[m], %%eax\n\t" \
FEP "loop 1b\n\t" \
FEP "mov %%edi, %%ecx\n\t" \
FEP "xor %%eax, %%eax\n\t" \
FEP "xor %%edx, %%edx\n\t" \ "wrmsr\n\t" \
:: "a"((uint32_t)_value), "d"(_value >> 32), \ "c"(_msr), "D"(_msr), [m]"m"(kvm_pmu_version) \
); \
} while (0)
/* * Limit testing to MSRs that are actually defined by Intel (in the SDM). MSRs * that aren't defined counter MSRs *probably* don't exist, but there's no * guarantee that currently undefined MSR indices won't be used for something * other than PMCs in the future.
*/ #define MAX_NR_GP_COUNTERS 8 #define MAX_NR_FIXED_COUNTERS 3
for (i = 0; i < nr_possible_counters; i++) { /* * TODO: Test a value that validates full-width writes and the * width of the counters.
*/ const uint64_t test_val = 0xffff; const uint32_t msr = base_msr + i;
/* * Fixed counters are supported if the counter is less than the * number of enumerated contiguous counters *or* the counter is * explicitly enumerated in the supported counters mask.
*/ constbool expect_success = i < nr_counters || (or_mask & BIT(i));
/* * KVM drops writes to MSR_P6_PERFCTR[0|1] if the counters are * unsupported, i.e. doesn't #GP and reads back '0'.
*/ const uint64_t expected_val = expect_success ? test_val : 0; constbool expect_gp = !expect_success && msr != MSR_P6_PERFCTR0 &&
msr != MSR_P6_PERFCTR1;
uint32_t rdpmc_idx;
uint8_t vector;
uint64_t val;
/* On #GP, the result of RDMSR is undefined. */ if (!expect_gp)
GUEST_ASSERT_PMC_VALUE(RDMSR, msr, val, expected_val);
/* * Redo the read tests with RDPMC, which has different indexing * semantics and additional capabilities.
*/
rdpmc_idx = i; if (base_msr == MSR_CORE_PERF_FIXED_CTR0)
rdpmc_idx |= INTEL_RDPMC_FIXED;
/* * KVM doesn't support non-architectural PMUs, i.e. it should * impossible to have fast mode RDPMC. Verify that attempting * to use fast RDPMC always #GPs.
*/
GUEST_ASSERT(!expect_success || !pmu_has_fast_mode);
rdpmc_idx |= INTEL_RDPMC_FAST;
guest_test_rdpmc(rdpmc_idx, false, -1ull);
if (pmu_version)
nr_gp_counters = this_cpu_property(X86_PROPERTY_PMU_NR_GP_COUNTERS);
/* * For v2+ PMUs, PERF_GLOBAL_CTRL's architectural post-RESET value is * "Sets bits n-1:0 and clears the upper bits", where 'n' is the number * of GP counters. If there are no GP counters, require KVM to leave * PERF_GLOBAL_CTRL '0'. This edge case isn't covered by the SDM, but * follow the spirit of the architecture and only globally enable GP * counters, of which there are none.
*/ if (pmu_version > 1) {
uint64_t global_ctrl = rdmsr(MSR_CORE_PERF_GLOBAL_CTRL);
/* Fixed counters require Architectural vPMU Version 2+. */ if (guest_get_pmu_version() >= 2)
nr_fixed_counters = this_cpu_property(X86_PROPERTY_PMU_NR_FIXED_COUNTERS);
/* * The supported bitmask for fixed counters was introduced in PMU * version 5.
*/ if (guest_get_pmu_version() >= 5)
supported_bitmask = this_cpu_property(X86_PROPERTY_PMU_FIXED_COUNTERS_BITMASK);
/* * Test up to PMU v5, which is the current maximum version defined by * Intel, i.e. is the last version that is guaranteed to be backwards * compatible with KVM's existing behavior.
*/
uint8_t max_pmu_version = max_t(typeof(pmu_version), pmu_version, 5);
/* * Detect the existence of events that aren't supported by selftests. * This will (obviously) fail any time hardware adds support for a new * event, but it's worth paying that price to keep the test fresh.
*/
TEST_ASSERT(this_cpu_property(X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH) <= NR_INTEL_ARCH_EVENTS, "New architectural event(s) detected; please update this test (length = %u, mask = %x)",
this_cpu_property(X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH),
this_cpu_property(X86_PROPERTY_PMU_EVENTS_MASK));
/* * Iterate over known arch events irrespective of KVM/hardware support * to verify that KVM doesn't reject programming of events just because * the *architectural* encoding is unsupported. Track which events are * supported in hardware; the guest side will validate supported events * count correctly, even if *enumeration* of the event is unsupported * by KVM and/or isn't exposed to the guest.
*/ for (i = 0; i < NR_INTEL_ARCH_EVENTS; i++) { if (this_pmu_has(intel_event_to_feature(i).gp_event))
hardware_pmu_arch_events |= BIT(i);
}
for (v = 0; v <= max_pmu_version; v++) { for (i = 0; i < ARRAY_SIZE(perf_caps); i++) { if (!kvm_has_perf_caps && perf_caps[i]) continue;
pr_info("Testing arch events, PMU version %u, perf_caps = %lx\n",
v, perf_caps[i]); /* * To keep the total runtime reasonable, test every * possible non-zero, non-reserved bitmap combination * only with the native PMU version and the full bit * vector length.
*/ if (v == pmu_version) { for (k = 1; k < (BIT(NR_INTEL_ARCH_EVENTS) - 1); k++)
test_arch_events(v, perf_caps[i], NR_INTEL_ARCH_EVENTS, k);
} /* * Test single bits for all PMU version and lengths up * the number of events +1 (to verify KVM doesn't do * weird things if the guest length is greater than the * host length). Explicitly test a mask of '0' and all * ones i.e. all events being available and unavailable.
*/ for (j = 0; j <= NR_INTEL_ARCH_EVENTS + 1; j++) {
test_arch_events(v, perf_caps[i], j, 0);
test_arch_events(v, perf_caps[i], j, 0xff);
for (k = 0; k < NR_INTEL_ARCH_EVENTS; k++)
test_arch_events(v, perf_caps[i], j, BIT(k));
}
pr_info("Testing GP counters, PMU version %u, perf_caps = %lx\n",
v, perf_caps[i]); for (j = 0; j <= nr_gp_counters; j++)
test_gp_counters(v, perf_caps[i], j);
pr_info("Testing fixed counters, PMU version %u, perf_caps = %lx\n",
v, perf_caps[i]); for (j = 0; j <= nr_fixed_counters; j++) { for (k = 0; k <= (BIT(nr_fixed_counters) - 1); k++)
test_fixed_counters(v, perf_caps[i], j, k);
}
}
}
}
int main(int argc, char *argv[])
{
TEST_REQUIRE(kvm_is_pmu_enabled());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.