Add an APIC sub-test to verify the darker corners of logical mode IPI delivery. Logical mode is rather bizarre, in that each "ID" is treated as a bitmask, e.g. an ID with multiple bits set can match multiple destinations. Verify that overlapping and/or superfluous destinations and IDs with multiple target vCPUs are handled correctly for both flat and cluster modes. Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> --- x86/apic.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 192 insertions(+), 9 deletions(-) diff --git a/x86/apic.c b/x86/apic.c index 5be44b8..1cc61d3 100644 --- a/x86/apic.c +++ b/x86/apic.c @@ -257,11 +257,11 @@ static void test_apic_id(void) on_cpu(1, __test_apic_id, NULL); } -static int ipi_count; +static atomic_t ipi_count; -static void self_ipi_isr(isr_regs_t *regs) +static void handle_ipi(isr_regs_t *regs) { - ++ipi_count; + atomic_inc(&ipi_count); eoi(); } @@ -270,13 +270,13 @@ static void __test_self_ipi(void) u64 start = rdtsc(); int vec = 0xf1; - handle_irq(vec, self_ipi_isr); + handle_irq(vec, handle_ipi); apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, id_map[0]); do { pause(); - } while (rdtsc() - start < 1000000000 && ipi_count == 0); + } while (rdtsc() - start < 1000000000 && atomic_read(&ipi_count) == 0); } static void test_self_ipi_xapic(void) @@ -287,9 +287,9 @@ static void test_self_ipi_xapic(void) reset_apic(); report(is_xapic_enabled(), "Local apic enabled in xAPIC mode"); - ipi_count = 0; + atomic_set(&ipi_count, 0); __test_self_ipi(); - report(ipi_count == 1, "self ipi"); + report(atomic_read(&ipi_count) == 1, "self ipi"); report_prefix_pop(); } @@ -301,9 +301,9 @@ static void test_self_ipi_x2apic(void) if (enable_x2apic()) { report(is_x2apic_enabled(), "Local apic enabled in x2APIC mode"); - ipi_count = 0; + atomic_set(&ipi_count, 0); __test_self_ipi(); - report(ipi_count == 1, "self ipi"); + report(atomic_read(&ipi_count) == 1, "self ipi"); } else { report_skip("x2apic not detected"); } @@ -665,6 +665,188 @@ static void test_pv_ipi(void) report(!ret, "PV IPIs testing"); } +#define APIC_LDR_CLUSTER_FLAG BIT(31) + +static void set_ldr(void *__ldr) +{ + u32 ldr = (unsigned long)__ldr; + + if (ldr & APIC_LDR_CLUSTER_FLAG) + apic_write(APIC_DFR, APIC_DFR_CLUSTER); + else + apic_write(APIC_DFR, APIC_DFR_FLAT); + + apic_write(APIC_LDR, ldr << 24); +} + +static int test_fixed_ipi(u32 dest_mode, u8 dest, u8 vector, + int nr_ipis_expected, const char *mode_name) +{ + u64 start = rdtsc(); + int got; + + atomic_set(&ipi_count, 0); + + /* + * Wait for vCPU1 to get back into HLT, i.e. into the host so that + * KVM must handle incomplete AVIC IPIs. + */ + do { + pause(); + } while (rdtsc() - start < 1000000); + + start = rdtsc(); + + apic_icr_write(dest_mode | APIC_DM_FIXED | vector, dest); + + do { + pause(); + } while (rdtsc() - start < 1000000000 && + atomic_read(&ipi_count) != nr_ipis_expected); + + /* Only report failures to cut down on the spam. */ + got = atomic_read(&ipi_count); + if (got != nr_ipis_expected) + report_fail("Want %d IPI(s) using %s mode, dest = %x, got %d IPI(s)", + nr_ipis_expected, mode_name, dest, got); + atomic_set(&ipi_count, 0); + + return got == nr_ipis_expected ? 0 : 1; +} + +static int test_logical_ipi_single_target(u8 logical_id, bool cluster, u8 dest, + u8 vector) +{ + /* Disallow broadcast, there are at least 2 vCPUs. */ + if (dest == 0xff) + return 0; + + set_ldr((void *)0); + on_cpu(1, set_ldr, + (void *)((u32)logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0))); + return test_fixed_ipi(APIC_DEST_LOGICAL, dest, vector, 1, + cluster ? "logical cluster" : "logical flat"); +} + +static int test_logical_ipi_multi_target(u8 vcpu0_logical_id, u8 vcpu1_logical_id, + bool cluster, u8 dest, u8 vector) +{ + /* Allow broadcast unless there are more than 2 vCPUs. */ + if (dest == 0xff && cpu_count() > 2) + return 0; + + set_ldr((void *)((u32)vcpu0_logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0))); + on_cpu(1, set_ldr, + (void *)((u32)vcpu1_logical_id | (cluster ? APIC_LDR_CLUSTER_FLAG : 0))); + return test_fixed_ipi(APIC_DEST_LOGICAL, dest, vector, 2, + cluster ? "logical cluster" : "logical flat"); +} + +static void test_logical_ipi_xapic(void) +{ + int c, i, j, k, f; + u8 vector = 0xf1; + + if (cpu_count() < 2) + return; + + /* + * All vCPUs must be in xAPIC mode, i.e. simply resetting this vCPUs + * APIC is not sufficient. + */ + if (is_x2apic_enabled()) + return; + + handle_irq(vector, handle_ipi); + + /* Flat mode. 8 bits for logical IDs (one per bit). */ + f = 0; + for (i = 0; i < 8; i++) { + /* + * Test all possible destination values. Non-existent targets + * should be ignored. vCPU is always targeted, i.e. should get + * an IPI. + */ + for (k = 0; k < 0xff; k++) { + /* + * Skip values that overlap the actual target the + * resulting combination will be covered by other + * numbers in the sequence. + */ + if (BIT(i) & k) + continue; + + f += test_logical_ipi_single_target(BIT(i), false, + BIT(i) | k, vector); + } + } + report(!f, "IPI to single target using logical flat mode"); + + /* Cluster mode. 4 bits for the cluster, 4 bits for logical IDs. */ + f = 0; + for (c = 0; c < 0xf; c++) { + for (i = 0; i < 4; i++) { + /* Same as above, just fewer bits... */ + for (k = 0; k < 0x10; k++) { + if (BIT(i) & k) + continue; + + test_logical_ipi_single_target(c << 4 | BIT(i), true, + c << 4 | BIT(i) | k, vector); + } + } + } + report(!f, "IPI to single target using logical cluster mode"); + + /* And now do it all over again targeting both vCPU0 and vCPU1. */ + f = 0; + for (i = 0; i < 8 && !f; i++) { + for (j = 0; j < 8 && !f; j++) { + if (i == j) + continue; + + for (k = 0; k < 0x100 && !f; k++) { + if ((BIT(i) | BIT(j)) & k) + continue; + + f += test_logical_ipi_multi_target(BIT(i), BIT(j), false, + BIT(i) | BIT(j) | k, vector); + if (f) + break; + f += test_logical_ipi_multi_target(BIT(i) | BIT(j), + BIT(i) | BIT(j), false, + BIT(i) | BIT(j) | k, vector); + } + } + } + report(!f, "IPI to multiple targets using logical flat mode"); + + f = 0; + for (c = 0; c < 0xf && !f; c++) { + for (i = 0; i < 4 && !f; i++) { + for (j = 0; j < 4 && !f; j++) { + if (i == j) + continue; + + for (k = 0; k < 0x10 && !f; k++) { + if ((BIT(i) | BIT(j)) & k) + continue; + + f += test_logical_ipi_multi_target(c << 4 | BIT(i), + c << 4 | BIT(j), true, + c << 4 | BIT(i) | BIT(j) | k, vector); + if (f) + break; + f += test_logical_ipi_multi_target(c << 4 | BIT(i) | BIT(j), + c << 4 | BIT(i) | BIT(j), true, + c << 4 | BIT(i) | BIT(j) | k, vector); + } + } + } + } + report(!f, "IPI to multiple targets using logical cluster mode"); +} + typedef void (*apic_test_fn)(void); int main(void) @@ -682,6 +864,7 @@ int main(void) test_self_ipi_xapic, test_self_ipi_x2apic, test_physical_broadcast, + test_logical_ipi_xapic, test_pv_ipi, -- 2.38.0.rc1.362.ged0d419d3c-goog