Message ID | a2f7013646a8a1e314744568b9baa439682cbf8a.1715017765.git.reinette.chatre@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: x86: Make bus clock frequency for vAPIC timer configurable | expand |
On 5/6/2024 11:35 AM, Reinette Chatre wrote: ... > diff --git a/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c > new file mode 100644 > index 000000000000..56eb686144c6 > --- /dev/null > +++ b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c > @@ -0,0 +1,166 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Test configure of APIC bus frequency. > + * > + * Copyright (c) 2024 Intel Corporation > + * > + * To verify if the APIC bus frequency can be configured this, test starts > + * by setting the TSC frequency in KVM, and then: > + * For every APIC timer frequency supported: > + * * In the guest: > + * * * Start the APIC timer by programming the APIC TMICT (initial count > + * register) to the largest value possible to guarantee that it will > + * not expire during the test, > + * * * Wait for a known duration based on previously set TSC frequency, > + * * * Stop the timer and read the APIC TMCCT (current count) register to > + * determine the count at that time (TMCCT is loaded from TMICT when > + * TMICT is programmed and then starts counting down). > + * * In the host: > + * * * Determine if the APIC counts close to configured APIC bus frequency > + * while taking into account how the APIC timer frequency was modified > + * using the APIC TDCR (divide configuration register). > + */ > +#define _GNU_SOURCE /* for program_invocation_short_name */ As reported in [1] this #define is no longer needed after commit 730cfa45b5f4 ("KVM: selftests: Define _GNU_SOURCE for all selftests code"). This will be fixed in next version of this series. Reinette [1] https://lore.kernel.org/oe-kbuild/202405181535.WFKy4EwQ-lkp@intel.com/
On Mon, May 20, 2024, Reinette Chatre wrote: > On 5/6/2024 11:35 AM, Reinette Chatre wrote: > > diff --git a/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c > > new file mode 100644 > > index 000000000000..56eb686144c6 > > --- /dev/null > > +++ b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c > > @@ -0,0 +1,166 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Test configure of APIC bus frequency. > > + * > > + * Copyright (c) 2024 Intel Corporation > > + * > > + * To verify if the APIC bus frequency can be configured this, test starts > > + * by setting the TSC frequency in KVM, and then: > > + * For every APIC timer frequency supported: > > + * * In the guest: > > + * * * Start the APIC timer by programming the APIC TMICT (initial count > > + * register) to the largest value possible to guarantee that it will > > + * not expire during the test, > > + * * * Wait for a known duration based on previously set TSC frequency, > > + * * * Stop the timer and read the APIC TMCCT (current count) register to > > + * determine the count at that time (TMCCT is loaded from TMICT when > > + * TMICT is programmed and then starts counting down). > > + * * In the host: > > + * * * Determine if the APIC counts close to configured APIC bus frequency > > + * while taking into account how the APIC timer frequency was modified > > + * using the APIC TDCR (divide configuration register). > > + */ > > +#define _GNU_SOURCE /* for program_invocation_short_name */ > > As reported in [1] this #define is no longer needed after commit 730cfa45b5f4 > ("KVM: selftests: Define _GNU_SOURCE for all selftests code"). This will be > fixed in next version of this series. Don't worry about sending another version just for this, I can clean this up when applying.
Hi Sean, On 5/20/2024 9:12 AM, Sean Christopherson wrote: > On Mon, May 20, 2024, Reinette Chatre wrote: >> On 5/6/2024 11:35 AM, Reinette Chatre wrote: >>> diff --git a/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c >>> new file mode 100644 >>> index 000000000000..56eb686144c6 >>> --- /dev/null >>> +++ b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c >>> @@ -0,0 +1,166 @@ >>> +// SPDX-License-Identifier: GPL-2.0-only >>> +/* >>> + * Test configure of APIC bus frequency. >>> + * >>> + * Copyright (c) 2024 Intel Corporation >>> + * >>> + * To verify if the APIC bus frequency can be configured this, test starts >>> + * by setting the TSC frequency in KVM, and then: >>> + * For every APIC timer frequency supported: >>> + * * In the guest: >>> + * * * Start the APIC timer by programming the APIC TMICT (initial count >>> + * register) to the largest value possible to guarantee that it will >>> + * not expire during the test, >>> + * * * Wait for a known duration based on previously set TSC frequency, >>> + * * * Stop the timer and read the APIC TMCCT (current count) register to >>> + * determine the count at that time (TMCCT is loaded from TMICT when >>> + * TMICT is programmed and then starts counting down). >>> + * * In the host: >>> + * * * Determine if the APIC counts close to configured APIC bus frequency >>> + * while taking into account how the APIC timer frequency was modified >>> + * using the APIC TDCR (divide configuration register). >>> + */ >>> +#define _GNU_SOURCE /* for program_invocation_short_name */ >> >> As reported in [1] this #define is no longer needed after commit 730cfa45b5f4 >> ("KVM: selftests: Define _GNU_SOURCE for all selftests code"). This will be >> fixed in next version of this series. > > Don't worry about sending another version just for this, I can clean this up when > applying. Thank you very much. At this time this is the only change planned for this series. I do not know the status of all completed and planned merges during the merge window, but I did a quick check and this series applies cleanly on top of today's tip of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git. The kernel parts passed a bisect test cleanly, while the selftest encountered just the warning related to _GNU_SOURCE. Reinette
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 6de9994971c9..52039d7c8dd5 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -111,6 +111,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_invalid_nested_guest_state TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_nested_tsc_scaling_test +TEST_GEN_PROGS_x86_64 += x86_64/apic_bus_clock_test TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test TEST_GEN_PROGS_x86_64 += x86_64/xapic_state_test TEST_GEN_PROGS_x86_64 += x86_64/xcr0_cpuid_test diff --git a/tools/testing/selftests/kvm/include/x86_64/apic.h b/tools/testing/selftests/kvm/include/x86_64/apic.h index bed316fdecd5..b0d2fc62e172 100644 --- a/tools/testing/selftests/kvm/include/x86_64/apic.h +++ b/tools/testing/selftests/kvm/include/x86_64/apic.h @@ -60,6 +60,13 @@ #define APIC_VECTOR_MASK 0x000FF #define APIC_ICR2 0x310 #define SET_APIC_DEST_FIELD(x) ((x) << 24) +#define APIC_LVT0 0x350 +#define APIC_LVT_TIMER_ONESHOT (0 << 17) +#define APIC_LVT_TIMER_PERIODIC (1 << 17) +#define APIC_LVT_TIMER_TSCDEADLINE (2 << 17) +#define APIC_TMICT 0x380 +#define APIC_TMCCT 0x390 +#define APIC_TDCR 0x3E0 void apic_disable(void); void xapic_enable(void); diff --git a/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c new file mode 100644 index 000000000000..56eb686144c6 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test configure of APIC bus frequency. + * + * Copyright (c) 2024 Intel Corporation + * + * To verify if the APIC bus frequency can be configured this, test starts + * by setting the TSC frequency in KVM, and then: + * For every APIC timer frequency supported: + * * In the guest: + * * * Start the APIC timer by programming the APIC TMICT (initial count + * register) to the largest value possible to guarantee that it will + * not expire during the test, + * * * Wait for a known duration based on previously set TSC frequency, + * * * Stop the timer and read the APIC TMCCT (current count) register to + * determine the count at that time (TMCCT is loaded from TMICT when + * TMICT is programmed and then starts counting down). + * * In the host: + * * * Determine if the APIC counts close to configured APIC bus frequency + * while taking into account how the APIC timer frequency was modified + * using the APIC TDCR (divide configuration register). + */ +#define _GNU_SOURCE /* for program_invocation_short_name */ + +#include "apic.h" +#include "test_util.h" + +/* + * Pick one convenient value, 1.5GHz. No special meaning and different from + * the default value, 1GHz. + */ +#define TSC_HZ (1500 * 1000 * 1000ULL) + +/* Wait for 100 msec, not too long, not too short value. */ +#define LOOP_MSEC 100ULL +#define TSC_WAIT_DELTA (TSC_HZ / 1000 * LOOP_MSEC) + +/* + * Pick a typical value, 25MHz. Different enough from the default value, 1GHz. + */ +#define APIC_BUS_CLOCK_FREQ (25 * 1000 * 1000ULL) + +static void guest_code(void) +{ + /* + * Possible TDCR values and its divide count. Used to modify APIC + * timer frequency. + */ + struct { + u32 tdcr; + u32 divide_count; + } tdcrs[] = { + {0x0, 2}, + {0x1, 4}, + {0x2, 8}, + {0x3, 16}, + {0x8, 32}, + {0x9, 64}, + {0xa, 128}, + {0xb, 1}, + }; + + u32 tmict, tmcct; + u64 tsc0, tsc1; + int i; + + asm volatile("cli"); + + xapic_enable(); + + /* + * Setup one-shot timer. The vector does not matter because the + * interrupt does not fire. + */ + xapic_write_reg(APIC_LVT0, APIC_LVT_TIMER_ONESHOT); + + for (i = 0; i < ARRAY_SIZE(tdcrs); i++) { + xapic_write_reg(APIC_TDCR, tdcrs[i].tdcr); + + /* Set the largest value to not trigger the interrupt. */ + tmict = ~0; + xapic_write_reg(APIC_TMICT, tmict); + + /* Busy wait for LOOP_MSEC */ + tsc0 = rdtsc(); + tsc1 = tsc0; + while (tsc1 - tsc0 < TSC_WAIT_DELTA) + tsc1 = rdtsc(); + + /* Read APIC timer and TSC */ + tmcct = xapic_read_reg(APIC_TMCCT); + tsc1 = rdtsc(); + + /* Stop timer */ + xapic_write_reg(APIC_TMICT, 0); + + /* Report it. */ + GUEST_SYNC_ARGS(tdcrs[i].divide_count, tmict - tmcct, + tsc1 - tsc0, 0, 0); + } + + GUEST_DONE(); +} + +void test_apic_bus_clock(struct kvm_vcpu *vcpu) +{ + bool done = false; + struct ucall uc; + + while (!done) { + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_DONE: + done = true; + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_SYNC: { + u32 divide_counter = uc.args[1]; + u32 apic_cycles = uc.args[2]; + u64 tsc_cycles = uc.args[3]; + u64 freq; + + TEST_ASSERT(tsc_cycles > 0, + "TSC cycles must not be zero."); + + /* Allow 1% slack. */ + freq = apic_cycles * divide_counter * TSC_HZ / tsc_cycles; + TEST_ASSERT(freq < APIC_BUS_CLOCK_FREQ * 101 / 100, + "APIC bus clock frequency is too large"); + TEST_ASSERT(freq > APIC_BUS_CLOCK_FREQ * 99 / 100, + "APIC bus clock frequency is too small"); + break; + } + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_APIC_BUS_CYCLES_NS)); + + vm = vm_create(1); + vm_ioctl(vm, KVM_SET_TSC_KHZ, (void *)(TSC_HZ / 1000)); + /* + * KVM_CAP_X86_APIC_BUS_CYCLES_NS expects APIC bus clock rate in + * nanoseconds and requires that no vCPU is created. + */ + vm_enable_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS, + NSEC_PER_SEC / APIC_BUS_CLOCK_FREQ); + vcpu = vm_vcpu_add(vm, 0, guest_code); + + virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); + + test_apic_bus_clock(vcpu); + kvm_vm_free(vm); +}