1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
// SPDX-License-Identifier: GPL-2.0-only
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdint.h>
#include <sys/sysinfo.h>
#include "kvm_util.h"
static struct kvm_vm *vm1;
static struct kvm_vm *vm2;
static int __eventfd;
static bool done;
/*
* KVM de-assigns based on eventfd *and* GSI, but requires unique eventfds when
* assigning (the API isn't symmetrical). Abuse the oddity and use a per-task
* GSI base to avoid false failures due to cross-task de-assign, i.e. so that
* the secondary doesn't de-assign the primary's eventfd and cause assign to
* unexpectedly succeed on the primary.
*/
#define GSI_BASE_PRIMARY 0x20
#define GSI_BASE_SECONDARY 0x30
static void juggle_eventfd_secondary(struct kvm_vm *vm, int eventfd)
{
int r, i;
/*
* The secondary task can encounter EBADF since the primary can close
* the eventfd at any time. And because the primary can recreate the
* eventfd, at the safe fd in the file table, the secondary can also
* encounter "unexpected" success, e.g. if the close+recreate happens
* between the first and second assignments. The secondary's role is
* mostly to antagonize KVM, not to detect bugs.
*/
for (i = 0; i < 2; i++) {
r = __kvm_irqfd(vm, GSI_BASE_SECONDARY, eventfd, 0);
TEST_ASSERT(!r || errno == EBUSY || errno == EBADF,
"Wanted success, EBUSY, or EBADF, r = %d, errno = %d",
r, errno);
/* De-assign should succeed unless the eventfd was closed. */
r = __kvm_irqfd(vm, GSI_BASE_SECONDARY + i, eventfd, KVM_IRQFD_FLAG_DEASSIGN);
TEST_ASSERT(!r || errno == EBADF,
"De-assign should succeed unless the fd was closed");
}
}
static void *secondary_irqfd_juggler(void *ign)
{
while (!READ_ONCE(done)) {
juggle_eventfd_secondary(vm1, READ_ONCE(__eventfd));
juggle_eventfd_secondary(vm2, READ_ONCE(__eventfd));
}
return NULL;
}
static void juggle_eventfd_primary(struct kvm_vm *vm, int eventfd)
{
int r1, r2;
/*
* At least one of the assigns should fail. KVM disallows assigning a
* single eventfd to multiple GSIs (or VMs), so it's possible that both
* assignments can fail, too.
*/
r1 = __kvm_irqfd(vm, GSI_BASE_PRIMARY, eventfd, 0);
TEST_ASSERT(!r1 || errno == EBUSY,
"Wanted success or EBUSY, r = %d, errno = %d", r1, errno);
r2 = __kvm_irqfd(vm, GSI_BASE_PRIMARY + 1, eventfd, 0);
TEST_ASSERT(r1 || (r2 && errno == EBUSY),
"Wanted failure (EBUSY), r1 = %d, r2 = %d, errno = %d",
r1, r2, errno);
/*
* De-assign should always succeed, even if the corresponding assign
* failed.
*/
kvm_irqfd(vm, GSI_BASE_PRIMARY, eventfd, KVM_IRQFD_FLAG_DEASSIGN);
kvm_irqfd(vm, GSI_BASE_PRIMARY + 1, eventfd, KVM_IRQFD_FLAG_DEASSIGN);
}
int main(int argc, char *argv[])
{
pthread_t racing_thread;
int r, i;
/* Create "full" VMs, as KVM_IRQFD requires an in-kernel IRQ chip. */
vm1 = vm_create(1);
vm2 = vm_create(1);
WRITE_ONCE(__eventfd, kvm_new_eventfd());
kvm_irqfd(vm1, 10, __eventfd, 0);
r = __kvm_irqfd(vm1, 11, __eventfd, 0);
TEST_ASSERT(r && errno == EBUSY,
"Wanted EBUSY, r = %d, errno = %d", r, errno);
r = __kvm_irqfd(vm2, 12, __eventfd, 0);
TEST_ASSERT(r && errno == EBUSY,
"Wanted EBUSY, r = %d, errno = %d", r, errno);
/*
* De-assign all eventfds, along with multiple eventfds that were never
* assigned. KVM's ABI is that de-assign is allowed so long as the
* eventfd itself is valid.
*/
kvm_irqfd(vm1, 11, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN);
kvm_irqfd(vm1, 12, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN);
kvm_irqfd(vm1, 13, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN);
kvm_irqfd(vm1, 14, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN);
kvm_irqfd(vm1, 10, READ_ONCE(__eventfd), KVM_IRQFD_FLAG_DEASSIGN);
close(__eventfd);
pthread_create(&racing_thread, NULL, secondary_irqfd_juggler, vm2);
for (i = 0; i < 10000; i++) {
WRITE_ONCE(__eventfd, kvm_new_eventfd());
juggle_eventfd_primary(vm1, __eventfd);
juggle_eventfd_primary(vm2, __eventfd);
close(__eventfd);
}
WRITE_ONCE(done, true);
pthread_join(racing_thread, NULL);
}
|