[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <9dd04b84f0d37a44e07add77f62c931cba7743a7.camel@redhat.com>
Date: Thu, 21 Dec 2023 19:38:40 +0200
From: Maxim Levitsky <mlevitsk@...hat.com>
To: Isaku Yamahata <isaku.yamahata@...el.com>, kvm@...r.kernel.org,
linux-kernel@...r.kernel.org, Paolo Bonzini <pbonzini@...hat.com>,
erdemaktas@...gle.com, Sean Christopherson <seanjc@...gle.com>, Vishal
Annapurve <vannapurve@...gle.com>, Jim Mattson <jmattson@...gle.com>,
Xiaoyao Li <xiaoyao.li@...el.com>
Cc: isaku.yamahata@...il.com
Subject: Re: [PATCH v3 4/4] KVM: selftests: Add test case for x86
apic_bus_clock_frequency
On Tue, 2023-12-19 at 00:34 -0800, Isaku Yamahata wrote:
> Test if the apic bus clock frequency is exptected to the configured value.
Typo.
> Set APIC TMICT to the maximum value and busy wait for 100 msec (any value
> is okay) with tsc value, and read TMCCT. Calculate apic bus clock frequency
> based on TSC frequency.
>
> Signed-off-by: Isaku Yamahata <isaku.yamahata@...el.com>
> ---
> Changes v3:
> - Use 1.5GHz instead of 1GHz as frequency.
>
> Changes v2:
> - Newly added.
> ---
> tools/testing/selftests/kvm/Makefile | 1 +
> .../selftests/kvm/include/x86_64/apic.h | 7 +
> .../kvm/x86_64/apic_bus_clock_test.c | 135 ++++++++++++++++++
> 3 files changed, 143 insertions(+)
> create mode 100644 tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c
>
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 963435959a92..e07ec9c1dbd1 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -116,6 +116,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..866a58d5fa11 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..e7896d703e7d
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c
> @@ -0,0 +1,135 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#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 up typical value. 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. */
> + 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. Because we don't fire the interrupt, the
> + * vector doesn't matter.
> + */
> + 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_vm *vm;
> + struct kvm_vcpu *vcpu;
> +
> + vm = __vm_create(VM_MODE_DEFAULT, 1, 0);
> + vm_ioctl(vm, KVM_SET_TSC_KHZ, (void *) (TSC_HZ / 1000));
> + /* KVM_CAP_X86_BUS_FREQUENCY_CONTROL requires that no vcpu is created. */
> + vm_enable_cap(vm, KVM_CAP_X86_BUS_FREQUENCY_CONTROL,
> + 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);
> +}
Reviewed-by: Maxim Levitsky <mlevitsk@...hat.com>
Best regards,
Maxim Levitsky
Powered by blists - more mailing lists