Skip to content

Commit 7e9b231

Browse files
committed
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. Tested-by: K Prateek Nayak <kprateek.nayak@amd.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20250522235223.3178519-14-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 74e5e3f commit 7e9b231

2 files changed

Lines changed: 136 additions & 0 deletions

File tree

tools/testing/selftests/kvm/Makefile.kvm

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

0 commit comments

Comments
 (0)