[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3 13/13] KVM: selftests: Add a KVM_IRQFD test to verify uniqueness requirements



Add a selftest to verify that eventfd+irqfd bindings are globally unique,
i.e. that KVM doesn't allow multiple irqfds to bind to a single eventfd,
even across VMs.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
 tools/testing/selftests/kvm/Makefile.kvm |   1 +
 tools/testing/selftests/kvm/irqfd_test.c | 130 +++++++++++++++++++++++
 2 files changed, 131 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/irqfd_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm 
b/tools/testing/selftests/kvm/Makefile.kvm
index f62b0a5aba35..318adf3ef6b6 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -54,6 +54,7 @@ TEST_PROGS_x86 += x86/nx_huge_pages_test.sh
 TEST_GEN_PROGS_COMMON = demand_paging_test
 TEST_GEN_PROGS_COMMON += dirty_log_test
 TEST_GEN_PROGS_COMMON += guest_print_test
+TEST_GEN_PROGS_COMMON += irqfd_test
 TEST_GEN_PROGS_COMMON += kvm_binary_stats_test
 TEST_GEN_PROGS_COMMON += kvm_create_max_vcpus
 TEST_GEN_PROGS_COMMON += kvm_page_table_test
diff --git a/tools/testing/selftests/kvm/irqfd_test.c 
b/tools/testing/selftests/kvm/irqfd_test.c
new file mode 100644
index 000000000000..286f2b15fde6
--- /dev/null
+++ b/tools/testing/selftests/kvm/irqfd_test.c
@@ -0,0 +1,130 @@
+// 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);
+
+       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);
+}
-- 
2.49.0.1151.ga128411c76-goog




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.