From dba295579d10ff88b596697c861a7c83f5e9d013 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Tue, 30 Nov 2021 13:56:57 +0200 Subject: [PATCH] add unit test for avic ipi Signed-off-by: Maxim Levitsky --- x86/Makefile.common | 3 +- x86/ipi_stress.c | 169 ++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 5 ++ 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 x86/ipi_stress.c diff --git a/x86/Makefile.common b/x86/Makefile.common index 461de51..372d6ec 100644 --- a/x86/Makefile.common +++ b/x86/Makefile.common @@ -60,7 +60,8 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \ $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \ $(TEST_DIR)/hyperv_connections.flat \ - $(TEST_DIR)/umip.flat $(TEST_DIR)/tsx-ctrl.flat + $(TEST_DIR)/umip.flat $(TEST_DIR)/tsx-ctrl.flat \ + $(TEST_DIR)/ipi_stress.flat test_cases: $(tests-common) $(tests) diff --git a/x86/ipi_stress.c b/x86/ipi_stress.c new file mode 100644 index 0000000..50cbf39 --- /dev/null +++ b/x86/ipi_stress.c @@ -0,0 +1,169 @@ +#include "libcflat.h" +#include "smp.h" +#include "alloc.h" +#include "apic.h" +#include "processor.h" +#include "isr.h" +#include "asm/barrier.h" +#include "delay.h" +#include "atomic.h" + +static atomic_t running_cpus; + +volatile u64 *isr_counts; + +volatile bool finish_init; + +u64 num_outer_iterations = 5; +#define NUM_INNER_ITERATIONS 10000 +#define TOTAL_NUMBER_ITERATIONS (num_outer_iterations * NUM_INNER_ITERATIONS) + +static void ipi_interrupt_handler(isr_regs_t *r) +{ + isr_counts[smp_id()]++; + eoi(); +} + +static void wait_for_ipi(volatile u64 *count) +{ + u64 old_count = *count; + while (1) { + if ((rdtsc() >> 4) % 10000 != 0 ) { + asm volatile( + /* short window for interrupts */ + "sti\n" + "nop\n" + "cli\n" + ); + if (old_count != *count) + break; + } + + else { + asm volatile( + /* short window for interrupts */ + "sti\n" + "hlt\n" + "cli\n" + ); + + if (old_count != *count) + break; + } + }; +} + +static void vcpu_init(void *data) +{ + int cpu = (long)data; + + /* To make it easier to see iteration number in the trace */ + handle_irq(0x40, ipi_interrupt_handler); + handle_irq(0x50, ipi_interrupt_handler); + + atomic_inc(&running_cpus); + + if (cpu != 0) + while (!finish_init); +} + +static void vcpu_code(void *data) +{ + int ncpus = cpu_count(); + int cpu = (long)data; + u64 i; + + assert(cpu != 0); + if (cpu != 1) + wait_for_ipi(&isr_counts[cpu]); + + for (i = 0; i < TOTAL_NUMBER_ITERATIONS; i++) { + + u8 physical_dst = cpu == ncpus -1 ? 1 : cpu + 1; + + // send IPI to a next vCPU in a circular fashion + apic_icr_write(APIC_INT_ASSERT | + APIC_DEST_PHYSICAL | + APIC_DM_FIXED | + (i % 2 ? 0x40 : 0x50), + physical_dst); + + // wait for the IPI interrupt chain to come back to us + if (i < (TOTAL_NUMBER_ITERATIONS - 1) || cpu == 1) + wait_for_ipi(&isr_counts[cpu]); + + if (cpu == 1 && (i % NUM_INNER_ITERATIONS == 0)) + printf("."); + } +} + + + +int main(int argc, void** argv) +{ + int cpu, ncpus = cpu_count(); + + assert(ncpus > 2); + + if (argc > 1) + num_outer_iterations = atol(argv[1]); + + isr_counts = (volatile u64 *)calloc(ncpus, sizeof(u64)); + + printf("found %d cpus\n", ncpus); + printf("running for %lld iterations\n", + (long long unsigned int)TOTAL_NUMBER_ITERATIONS); + + /* + * Ensure that we don't have interrupt window pending + * from PIT timer which inhibits the AVIC. + */ + + for (cpu = 0; cpu < ncpus; ++cpu) + on_cpu_async(cpu, vcpu_init, (void *)(long)cpu); + + /* Workaround for apic->irr_pending bug vs avic inhibittion: + * Ensure that all vCPUs are running before uninhibiting the AVIC + * */ + + while (atomic_read(&running_cpus) < ncpus) + pause(); + + asm volatile("sti;nop;cli\n"); + + /* now let all the vCPUs end the IPI function*/ + finish_init = true; + + while (cpus_active() > 1) + pause(); + + printf("starting test on all cpus but 0...\n"); + + for (cpu = ncpus-1; cpu > 0; cpu--) + on_cpu_async(cpu, vcpu_code, (void *)(long)cpu); + + printf("test started, waiting to end...\n"); + + while (cpus_active() > 1) { + + u64 isr_count1 = isr_counts[1]; + + delay(5ULL*1000*1000*1000); + + if (isr_count1 == isr_counts[1]) { + printf("\n"); + printf("hang detected!!\n"); + break; + } + } + + printf("\n"); + + for (cpu = 1; cpu < ncpus; ++cpu) + report(isr_counts[cpu] == TOTAL_NUMBER_ITERATIONS, + "Number of IPIs match (%lld)", + (long long unsigned int)isr_counts[cpu]); + + free((void*)isr_counts); + return report_summary(); +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 6244595..ff866b4 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -61,6 +61,11 @@ smp = 2 file = smptest.flat smp = 3 +[ipi_stress] +file = ipi_stress.flat +extra_params = -cpu host,-x2apic,-svm,-hypervisor -global kvm-pit.lost_tick_policy=discard -machine kernel-irqchip=on +smp = 4 + [vmexit_cpuid] file = vmexit.flat extra_params = -append 'cpuid' -- 2.26.3