diff mbox series

[V6,4/4] KVM: selftests: Add test for configure of x86 APIC bus frequency

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

Commit Message

Reinette Chatre May 6, 2024, 6:35 p.m. UTC
From: Isaku Yamahata <isaku.yamahata@intel.com>

Test if the APIC bus clock frequency is the expected configured value.

Set APIC timer's initial count to the maximum value and busy wait for 100
msec (any value is okay) with TSC value. Read the APIC timer's "current
count" to calculate the actual APIC bus clock frequency based on TSC
frequency.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Isaku Yamahata <isaku.yamahata@intel.com>
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Co-developed-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
---
Changes v6:
- Use vm_create() wrapper instead of open coding it. (Zide)
- Improve grammar of test description. (Zide)

Changes v5:
- Update to new name of capability KVM_CAP_X86_APIC_BUS_FREQUENCY ->
  KVM_CAP_X86_APIC_BUS_CYCLES_NS. (Xiaoyao Li)

Changes v4:
- Rework changelog.
- Add Sean's "Suggested-by" to acknowledge guidance received in
  https://lore.kernel.org/all/ZU0BASXWcck85r90@google.com/
- Add copyright.
- Add test description to file header.
- Consistent capitalization for acronyms.
- Rebase to kvm-x86/next.
- Update to v4 change of providing bus clock rate in nanoseconds.
- Add a "TEST_REQUIRE()" for the new capability so that the test can
  work on kernels that do not support the new capability.
- Address checkpatch warnings and use tabs instead of spaces in header
  file to match existing code.

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          | 166 ++++++++++++++++++
 3 files changed, 174 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86_64/apic_bus_clock_test.c

Comments

Reinette Chatre May 20, 2024, 3:47 p.m. UTC | #1
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/
Sean Christopherson May 20, 2024, 4:12 p.m. UTC | #2
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.
Reinette Chatre May 20, 2024, 4:52 p.m. UTC | #3
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 mbox series

Patch

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);
+}