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@xxxxxxxxxxxxx> --- 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 diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 index 010102b600f9..ac0e4858a29c 100644 --- a/x86/Makefile.x86_64 +++ b/x86/Makefile.x86_64 @@ -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 diff --git a/x86/tscdeadline_delay.c b/x86/tscdeadline_delay.c new file mode 100644 index 000000000000..01498da2d0ce --- /dev/null +++ b/x86/tscdeadline_delay.c @@ -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(); +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 5ecb9bba535b..b26202d32240 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -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 -- 2.20.1