|
15 | 15 | #include <time.h>
|
16 | 16 | #include <sched.h>
|
17 | 17 | #include <signal.h>
|
| 18 | +#include <pthread.h> |
18 | 19 |
|
19 | 20 | #include <sys/eventfd.h>
|
20 | 21 |
|
| 22 | +/* Defined in include/linux/kvm_types.h */ |
| 23 | +#define GPA_INVALID (~(ulong)0) |
| 24 | + |
21 | 25 | #define SHINFO_REGION_GVA 0xc0000000ULL
|
22 | 26 | #define SHINFO_REGION_GPA 0xc0000000ULL
|
23 | 27 | #define SHINFO_REGION_SLOT 10
|
|
44 | 48 |
|
45 | 49 | #define MIN_STEAL_TIME 50000
|
46 | 50 |
|
| 51 | +#define SHINFO_RACE_TIMEOUT 2 /* seconds */ |
| 52 | + |
47 | 53 | #define __HYPERVISOR_set_timer_op 15
|
48 | 54 | #define __HYPERVISOR_sched_op 29
|
49 | 55 | #define __HYPERVISOR_event_channel_op 32
|
@@ -148,6 +154,7 @@ static void guest_wait_for_irq(void)
|
148 | 154 | static void guest_code(void)
|
149 | 155 | {
|
150 | 156 | struct vcpu_runstate_info *rs = (void *)RUNSTATE_VADDR;
|
| 157 | + int i; |
151 | 158 |
|
152 | 159 | __asm__ __volatile__(
|
153 | 160 | "sti\n"
|
@@ -325,6 +332,49 @@ static void guest_code(void)
|
325 | 332 | guest_wait_for_irq();
|
326 | 333 |
|
327 | 334 | GUEST_SYNC(21);
|
| 335 | + /* Racing host ioctls */ |
| 336 | + |
| 337 | + guest_wait_for_irq(); |
| 338 | + |
| 339 | + GUEST_SYNC(22); |
| 340 | + /* Racing vmcall against host ioctl */ |
| 341 | + |
| 342 | + ports[0] = 0; |
| 343 | + |
| 344 | + p = (struct sched_poll) { |
| 345 | + .ports = ports, |
| 346 | + .nr_ports = 1, |
| 347 | + .timeout = 0 |
| 348 | + }; |
| 349 | + |
| 350 | +wait_for_timer: |
| 351 | + /* |
| 352 | + * Poll for a timer wake event while the worker thread is mucking with |
| 353 | + * the shared info. KVM XEN drops timer IRQs if the shared info is |
| 354 | + * invalid when the timer expires. Arbitrarily poll 100 times before |
| 355 | + * giving up and asking the VMM to re-arm the timer. 100 polls should |
| 356 | + * consume enough time to beat on KVM without taking too long if the |
| 357 | + * timer IRQ is dropped due to an invalid event channel. |
| 358 | + */ |
| 359 | + for (i = 0; i < 100 && !guest_saw_irq; i++) |
| 360 | + asm volatile("vmcall" |
| 361 | + : "=a" (rax) |
| 362 | + : "a" (__HYPERVISOR_sched_op), |
| 363 | + "D" (SCHEDOP_poll), |
| 364 | + "S" (&p) |
| 365 | + : "memory"); |
| 366 | + |
| 367 | + /* |
| 368 | + * Re-send the timer IRQ if it was (likely) dropped due to the timer |
| 369 | + * expiring while the event channel was invalid. |
| 370 | + */ |
| 371 | + if (!guest_saw_irq) { |
| 372 | + GUEST_SYNC(23); |
| 373 | + goto wait_for_timer; |
| 374 | + } |
| 375 | + guest_saw_irq = false; |
| 376 | + |
| 377 | + GUEST_SYNC(24); |
328 | 378 | }
|
329 | 379 |
|
330 | 380 | static int cmp_timespec(struct timespec *a, struct timespec *b)
|
@@ -352,11 +402,36 @@ static void handle_alrm(int sig)
|
352 | 402 | TEST_FAIL("IRQ delivery timed out");
|
353 | 403 | }
|
354 | 404 |
|
| 405 | +static void *juggle_shinfo_state(void *arg) |
| 406 | +{ |
| 407 | + struct kvm_vm *vm = (struct kvm_vm *)arg; |
| 408 | + |
| 409 | + struct kvm_xen_hvm_attr cache_init = { |
| 410 | + .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, |
| 411 | + .u.shared_info.gfn = SHINFO_REGION_GPA / PAGE_SIZE |
| 412 | + }; |
| 413 | + |
| 414 | + struct kvm_xen_hvm_attr cache_destroy = { |
| 415 | + .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, |
| 416 | + .u.shared_info.gfn = GPA_INVALID |
| 417 | + }; |
| 418 | + |
| 419 | + for (;;) { |
| 420 | + __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_init); |
| 421 | + __vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &cache_destroy); |
| 422 | + pthread_testcancel(); |
| 423 | + }; |
| 424 | + |
| 425 | + return NULL; |
| 426 | +} |
| 427 | + |
355 | 428 | int main(int argc, char *argv[])
|
356 | 429 | {
|
357 | 430 | struct timespec min_ts, max_ts, vm_ts;
|
358 | 431 | struct kvm_vm *vm;
|
| 432 | + pthread_t thread; |
359 | 433 | bool verbose;
|
| 434 | + int ret; |
360 | 435 |
|
361 | 436 | verbose = argc > 1 && (!strncmp(argv[1], "-v", 3) ||
|
362 | 437 | !strncmp(argv[1], "--verbose", 10));
|
@@ -785,6 +860,71 @@ int main(int argc, char *argv[])
|
785 | 860 | case 21:
|
786 | 861 | TEST_ASSERT(!evtchn_irq_expected,
|
787 | 862 | "Expected event channel IRQ but it didn't happen");
|
| 863 | + alarm(0); |
| 864 | + |
| 865 | + if (verbose) |
| 866 | + printf("Testing shinfo lock corruption (KVM_XEN_HVM_EVTCHN_SEND)\n"); |
| 867 | + |
| 868 | + ret = pthread_create(&thread, NULL, &juggle_shinfo_state, (void *)vm); |
| 869 | + TEST_ASSERT(ret == 0, "pthread_create() failed: %s", strerror(ret)); |
| 870 | + |
| 871 | + struct kvm_irq_routing_xen_evtchn uxe = { |
| 872 | + .port = 1, |
| 873 | + .vcpu = vcpu->id, |
| 874 | + .priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL |
| 875 | + }; |
| 876 | + |
| 877 | + evtchn_irq_expected = true; |
| 878 | + for (time_t t = time(NULL) + SHINFO_RACE_TIMEOUT; time(NULL) < t;) |
| 879 | + __vm_ioctl(vm, KVM_XEN_HVM_EVTCHN_SEND, &uxe); |
| 880 | + break; |
| 881 | + |
| 882 | + case 22: |
| 883 | + TEST_ASSERT(!evtchn_irq_expected, |
| 884 | + "Expected event channel IRQ but it didn't happen"); |
| 885 | + |
| 886 | + if (verbose) |
| 887 | + printf("Testing shinfo lock corruption (SCHEDOP_poll)\n"); |
| 888 | + |
| 889 | + shinfo->evtchn_pending[0] = 1; |
| 890 | + |
| 891 | + evtchn_irq_expected = true; |
| 892 | + tmr.u.timer.expires_ns = rs->state_entry_time + |
| 893 | + SHINFO_RACE_TIMEOUT * 1000000000ULL; |
| 894 | + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); |
| 895 | + break; |
| 896 | + |
| 897 | + case 23: |
| 898 | + /* |
| 899 | + * Optional and possibly repeated sync point. |
| 900 | + * Injecting the timer IRQ may fail if the |
| 901 | + * shinfo is invalid when the timer expires. |
| 902 | + * If the timer has expired but the IRQ hasn't |
| 903 | + * been delivered, rearm the timer and retry. |
| 904 | + */ |
| 905 | + vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr); |
| 906 | + |
| 907 | + /* Resume the guest if the timer is still pending. */ |
| 908 | + if (tmr.u.timer.expires_ns) |
| 909 | + break; |
| 910 | + |
| 911 | + /* All done if the IRQ was delivered. */ |
| 912 | + if (!evtchn_irq_expected) |
| 913 | + break; |
| 914 | + |
| 915 | + tmr.u.timer.expires_ns = rs->state_entry_time + |
| 916 | + SHINFO_RACE_TIMEOUT * 1000000000ULL; |
| 917 | + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); |
| 918 | + break; |
| 919 | + case 24: |
| 920 | + TEST_ASSERT(!evtchn_irq_expected, |
| 921 | + "Expected event channel IRQ but it didn't happen"); |
| 922 | + |
| 923 | + ret = pthread_cancel(thread); |
| 924 | + TEST_ASSERT(ret == 0, "pthread_cancel() failed: %s", strerror(ret)); |
| 925 | + |
| 926 | + ret = pthread_join(thread, 0); |
| 927 | + TEST_ASSERT(ret == 0, "pthread_join() failed: %s", strerror(ret)); |
788 | 928 | goto done;
|
789 | 929 |
|
790 | 930 | case 0x20:
|
|
0 commit comments