1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Test edge cases and race conditions in kvm_recalculate_apic_map().
4  */
5 
6 #include <sys/ioctl.h>
7 #include <pthread.h>
8 #include <time.h>
9 
10 #include "processor.h"
11 #include "test_util.h"
12 #include "kvm_util.h"
13 #include "apic.h"
14 
15 #define TIMEOUT		5	/* seconds */
16 
17 #define LAPIC_DISABLED	0
18 #define LAPIC_X2APIC	(MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE)
19 #define MAX_XAPIC_ID	0xff
20 
race(void * arg)21 static void *race(void *arg)
22 {
23 	struct kvm_lapic_state lapic = {};
24 	struct kvm_vcpu *vcpu = arg;
25 
26 	while (1) {
27 		/* Trigger kvm_recalculate_apic_map(). */
28 		vcpu_ioctl(vcpu, KVM_SET_LAPIC, &lapic);
29 		pthread_testcancel();
30 	}
31 
32 	return NULL;
33 }
34 
main(void)35 int main(void)
36 {
37 	struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
38 	struct kvm_vcpu *vcpuN;
39 	struct kvm_vm *vm;
40 	pthread_t thread;
41 	time_t t;
42 	int i;
43 
44 	kvm_static_assert(KVM_MAX_VCPUS > MAX_XAPIC_ID);
45 
46 	/*
47 	 * Create the max number of vCPUs supported by selftests so that KVM
48 	 * has decent amount of work to do when recalculating the map, i.e. to
49 	 * make the problematic window large enough to hit.
50 	 */
51 	vm = vm_create_with_vcpus(KVM_MAX_VCPUS, NULL, vcpus);
52 
53 	/*
54 	 * Enable x2APIC on all vCPUs so that KVM doesn't bail from the recalc
55 	 * due to vCPUs having aliased xAPIC IDs (truncated to 8 bits).
56 	 */
57 	for (i = 0; i < KVM_MAX_VCPUS; i++)
58 		vcpu_set_msr(vcpus[i], MSR_IA32_APICBASE, LAPIC_X2APIC);
59 
60 	TEST_ASSERT_EQ(pthread_create(&thread, NULL, race, vcpus[0]), 0);
61 
62 	vcpuN = vcpus[KVM_MAX_VCPUS - 1];
63 	for (t = time(NULL) + TIMEOUT; time(NULL) < t;) {
64 		vcpu_set_msr(vcpuN, MSR_IA32_APICBASE, LAPIC_X2APIC);
65 		vcpu_set_msr(vcpuN, MSR_IA32_APICBASE, LAPIC_DISABLED);
66 	}
67 
68 	TEST_ASSERT_EQ(pthread_cancel(thread), 0);
69 	TEST_ASSERT_EQ(pthread_join(thread, NULL), 0);
70 
71 	kvm_vm_free(vm);
72 
73 	return 0;
74 }
75