Keep switching between LAPIC_MODE_X2APIC and LAPIC_MODE_DISABLED during APIC map construction. Signed-off-by: Michal Luczaj <mhal@xxxxxxx> --- tools/testing/selftests/kvm/Makefile | 1 + .../kvm/x86_64/recalc_apic_map_race.c | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86_64/recalc_apic_map_race.c diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 7a5ff646e7e7..c9b8f16fb23f 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -116,6 +116,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/sev_migrate_tests TEST_GEN_PROGS_x86_64 += x86_64/amx_test TEST_GEN_PROGS_x86_64 += x86_64/max_vcpuid_cap_test TEST_GEN_PROGS_x86_64 += x86_64/triple_fault_event_test +TEST_GEN_PROGS_x86_64 += x86_64/recalc_apic_map_race TEST_GEN_PROGS_x86_64 += access_tracking_perf_test TEST_GEN_PROGS_x86_64 += demand_paging_test TEST_GEN_PROGS_x86_64 += dirty_log_test diff --git a/tools/testing/selftests/kvm/x86_64/recalc_apic_map_race.c b/tools/testing/selftests/kvm/x86_64/recalc_apic_map_race.c new file mode 100644 index 000000000000..1122df858623 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/recalc_apic_map_race.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * recalc_apic_map_race + * + * Test for a race condition in kvm_recalculate_apic_map(). + */ + +#include <sys/ioctl.h> +#include <pthread.h> +#include <time.h> + +#include "processor.h" +#include "test_util.h" +#include "kvm_util.h" +#include "apic.h" + +#define TIMEOUT 5 /* seconds */ +#define STUFFING 100 + +#define LAPIC_MODE_DISABLED 0 +#define LAPIC_MODE_X2APIC (MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE) +#define MAX_XAPIC_ID 0xff + +static int add_vcpu(struct kvm_vm *vm, long id) +{ + int vcpu = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)id); + + static struct { + struct kvm_cpuid2 head; + struct kvm_cpuid_entry2 entry; + } cpuid = { + .head.nent = 1, + /* X86_FEATURE_X2APIC */ + .entry = { + .function = 0x1, + .index = 0, + .ecx = 1UL << 21 + } + }; + + ASSERT_EQ(ioctl(vcpu, KVM_SET_CPUID2, &cpuid.head), 0); + + return vcpu; +} + +static void set_apicbase(int vcpu, u64 mode) +{ + struct { + struct kvm_msrs head; + struct kvm_msr_entry entry; + } msr = { + .head.nmsrs = 1, + .entry = { + .index = MSR_IA32_APICBASE, + .data = mode + } + }; + + ASSERT_EQ(ioctl(vcpu, KVM_SET_MSRS, &msr.head), msr.head.nmsrs); +} + +static void *race(void *arg) +{ + struct kvm_lapic_state state = {}; + int vcpu = *(int *)arg; + + while (1) { + /* Trigger kvm_recalculate_apic_map(). */ + ioctl(vcpu, KVM_SET_LAPIC, &state); + pthread_testcancel(); + } + + return NULL; +} + +int main(void) +{ + int vcpu0, vcpuN, i; + struct kvm_vm *vm; + pthread_t thread; + time_t t; + + vm = vm_create_barebones(); + vm_create_irqchip(vm); + + vcpu0 = add_vcpu(vm, 0); + vcpuN = add_vcpu(vm, KVM_MAX_VCPUS); + + static_assert(MAX_XAPIC_ID + STUFFING < KVM_MAX_VCPUS); + for (i = 0; i < STUFFING; ++i) + close(add_vcpu(vm, MAX_XAPIC_ID + i)); + + ASSERT_EQ(pthread_create(&thread, NULL, race, &vcpu0), 0); + + pr_info("Testing recalc_apic_map_race...\n"); + + for (t = time(NULL) + TIMEOUT; time(NULL) < t;) { + set_apicbase(vcpuN, LAPIC_MODE_X2APIC); + set_apicbase(vcpuN, LAPIC_MODE_DISABLED); + } + + ASSERT_EQ(pthread_cancel(thread), 0); + ASSERT_EQ(pthread_join(thread, NULL), 0); + + close(vcpu0); + close(vcpuN); + kvm_vm_release(vm); + + return 0; +} -- 2.40.1