@@ -17,6 +17,7 @@ tests += $(TEST_DIR)/syscall.flat
tests += $(TEST_DIR)/svm.flat
tests += $(TEST_DIR)/vmx.flat
tests += $(TEST_DIR)/tscdeadline_latency.flat
+tests += $(TEST_DIR)/tscdeadline_delay.flat
tests += $(TEST_DIR)/intel-iommu.flat
tests += $(TEST_DIR)/vmware_backdoors.flat
tests += $(TEST_DIR)/rdpru.flat
new file mode 100644
@@ -0,0 +1,105 @@
+/* Test regression for bug fixed by linux commit b606f189c7 */
+
+#include "libcflat.h"
+#include "apic.h"
+#include "vm.h"
+#include "smp.h"
+#include "desc.h"
+#include "isr.h"
+#include "msr.h"
+
+static u64 expire;
+
+static void test_lapic_existence(void)
+{
+ u32 lvr;
+
+ lvr = apic_read(APIC_LVR);
+ printf("apic version: %x\n", lvr);
+ report("apic existence", (u16)lvr == 0x14);
+}
+
+#define TSC_DEADLINE_TIMER_VECTOR 0xef
+
+static u64 expire;
+static u64 delta;
+
+static void tsc_deadline_timer_isr(isr_regs_t *regs)
+{
+ apic_write(APIC_EOI, 0);
+}
+
+static void start_tsc_deadline_timer(void)
+{
+ u64 tsc;
+
+ handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
+ irq_enable();
+
+ wrmsr(MSR_IA32_TSC, delta + 1);
+ tsc = rdmsr(MSR_IA32_TSC);
+ expire = tsc + delta;
+ wrmsr(MSR_IA32_TSCDEADLINE, expire);
+ asm volatile ("nop");
+ wrmsr(MSR_IA32_TSC, 1);
+ asm volatile ("nop");
+}
+
+static int enable_tsc_deadline_timer(void)
+{
+ uint32_t lvtt;
+
+ if (cpuid(1).c & (1 << 24)) {
+ lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR;
+ apic_write(APIC_LVTT, lvtt);
+ start_tsc_deadline_timer();
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void test_tsc_deadline_timer(void)
+{
+ if (enable_tsc_deadline_timer()) {
+ printf("tsc deadline timer enabled\n");
+ } else {
+ printf("tsc deadline timer not detected, aborting\n");
+ abort();
+ }
+}
+
+int main(int argc, char **argv)
+{
+ u64 now;
+ u64 then;
+ u64 max_delta = 0;
+
+ setup_vm();
+ smp_init();
+
+ test_lapic_existence();
+
+ mask_pic_interrupts();
+
+ delta = 1UL << 32;
+
+ test_tsc_deadline_timer();
+ irq_enable();
+
+ now = rdtsc();
+ do {
+ then = now;
+ now = rdtsc();
+ if (now - then > (delta / 2)) {
+ report("delta: %lu\n", false, now - then);
+ }
+ if (now - then > max_delta) {
+ max_delta = now - then;
+ }
+ } while (now < expire + delta);
+
+ printf("max delta: %lu\n", max_delta);
+
+ return report_summary();
+}
@@ -186,6 +186,10 @@ extra_params = -cpu kvm64,+rdtscp
file = tsc_adjust.flat
extra_params = -cpu host
+[tscdeadline_delay]
+file = tscdeadline_delay.flat
+extra_params = -cpu host,+tsc-deadline
+
[xsave]
file = xsave.flat
arch = x86_64
When the tsc deadline is used, the host might use a busy wait loop, which might sleep for up to the TSC offset/adjust, which is set when the guest sets the TSC MSR. Linux commit b606f189c7 ("KVM: LAPIC: cap __delay at lapic_timer_advance_ns") fixes the issue and this test check for its regression. On a kernel without that fix, the test fails with: FAIL: delta: 4296500469 On a kernel with the fix, the max_delta is usually reported as very low compared to that one: max delta: 12423150 Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com> --- x86/Makefile.x86_64 | 1 + x86/tscdeadline_delay.c | 105 ++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 4 ++ 3 files changed, 110 insertions(+) create mode 100644 x86/tscdeadline_delay.c