virtio: move common irqfd handling out of virtio-pci
All transports can use the same event handler for the irqfd, though the
exact mechanics of the assignment will be specific. Note that there
are three states: handled by the kernel, handled in userspace, disabled.
This also lets virtio use event_notifier_set_handler.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 36770fd..a66c946 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -496,25 +496,15 @@
return proxy->host_features;
}
-static void virtio_pci_guest_notifier_read(void *opaque)
-{
- VirtQueue *vq = opaque;
- EventNotifier *n = virtio_queue_get_guest_notifier(vq);
- if (event_notifier_test_and_clear(n)) {
- virtio_irq(vq);
- }
-}
-
static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
unsigned int queue_no,
unsigned int vector,
MSIMessage msg)
{
VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- int fd, ret;
-
- fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vq));
+ int ret;
if (irqfd->users == 0) {
ret = kvm_irqchip_add_msi_route(kvm_state, msg);
@@ -525,7 +515,7 @@
}
irqfd->users++;
- ret = kvm_irqchip_add_irqfd(kvm_state, fd, irqfd->virq);
+ ret = kvm_irqchip_add_irq_notifier(kvm_state, n, irqfd->virq);
if (ret < 0) {
if (--irqfd->users == 0) {
kvm_irqchip_release_virq(kvm_state, irqfd->virq);
@@ -533,8 +523,7 @@
return ret;
}
- qemu_set_fd_handler(fd, NULL, NULL, NULL);
-
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, true);
return 0;
}
@@ -543,19 +532,18 @@
unsigned int vector)
{
VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+ EventNotifier *n = virtio_queue_get_guest_notifier(vq);
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
- int fd, ret;
+ int ret;
- fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vq));
-
- ret = kvm_irqchip_remove_irqfd(kvm_state, fd, irqfd->virq);
+ ret = kvm_irqchip_remove_irq_notifier(kvm_state, n, irqfd->virq);
assert(ret == 0);
if (--irqfd->users == 0) {
kvm_irqchip_release_virq(kvm_state, irqfd->virq);
}
- qemu_set_fd_handler(fd, virtio_pci_guest_notifier_read, NULL, vq);
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, false);
}
static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector,
@@ -617,14 +605,9 @@
if (r < 0) {
return r;
}
- qemu_set_fd_handler(event_notifier_get_fd(notifier),
- virtio_pci_guest_notifier_read, NULL, vq);
+ virtio_queue_set_guest_notifier_fd_handler(vq, true, false);
} else {
- qemu_set_fd_handler(event_notifier_get_fd(notifier),
- NULL, NULL, NULL);
- /* Test and clear notifier before closing it,
- * in case poll callback didn't have time to run. */
- virtio_pci_guest_notifier_read(vq);
+ virtio_queue_set_guest_notifier_fd_handler(vq, false, false);
event_notifier_cleanup(notifier);
}
diff --git a/hw/virtio.c b/hw/virtio.c
index 197edf0..d146f86 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -984,6 +984,30 @@
return vdev->vq + n;
}
+static void virtio_queue_guest_notifier_read(EventNotifier *n)
+{
+ VirtQueue *vq = container_of(n, VirtQueue, guest_notifier);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_irq(vq);
+ }
+}
+
+void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
+ bool with_irqfd)
+{
+ if (assign && !with_irqfd) {
+ event_notifier_set_handler(&vq->guest_notifier,
+ virtio_queue_guest_notifier_read);
+ } else {
+ event_notifier_set_handler(&vq->guest_notifier, NULL);
+ }
+ if (!assign) {
+ /* Test and clear notifier before closing it,
+ * in case poll callback didn't have time to run. */
+ virtio_queue_guest_notifier_read(&vq->guest_notifier);
+ }
+}
+
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq)
{
return &vq->guest_notifier;
diff --git a/hw/virtio.h b/hw/virtio.h
index 2949485..96f4dbb 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -231,6 +231,8 @@
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
int virtio_queue_get_id(VirtQueue *vq);
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
+void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign,
+ bool with_irqfd);
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign);
void virtio_queue_notify_vq(VirtQueue *vq);
diff --git a/kvm-all.c b/kvm-all.c
index 5a386b4..add24a1 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1163,11 +1163,21 @@
return kvm_irqchip_assign_irqfd(s, fd, virq, true);
}
+int kvm_irqchip_add_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return kvm_irqchip_add_irqfd(s, event_notifier_get_fd(n), virq);
+}
+
int kvm_irqchip_remove_irqfd(KVMState *s, int fd, int virq)
{
return kvm_irqchip_assign_irqfd(s, fd, virq, false);
}
+int kvm_irqchip_remove_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return kvm_irqchip_remove_irqfd(s, event_notifier_get_fd(n), virq);
+}
+
static int kvm_irqchip_create(KVMState *s)
{
QemuOptsList *list = qemu_find_opts("machine");
diff --git a/kvm-stub.c b/kvm-stub.c
index ec9a364..d23b11c 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -147,7 +147,17 @@
return -ENOSYS;
}
+int kvm_irqchip_add_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return -ENOSYS;
+}
+
int kvm_irqchip_remove_irqfd(KVMState *s, int fd, int virq)
{
return -ENOSYS;
}
+
+int kvm_irqchip_remove_irq_notifier(KVMState *s, EventNotifier *n, int virq)
+{
+ return -ENOSYS;
+}
diff --git a/kvm.h b/kvm.h
index 9c7b0ea..99003f4 100644
--- a/kvm.h
+++ b/kvm.h
@@ -218,4 +218,6 @@
int kvm_irqchip_add_irqfd(KVMState *s, int fd, int virq);
int kvm_irqchip_remove_irqfd(KVMState *s, int fd, int virq);
+int kvm_irqchip_add_irq_notifier(KVMState *s, EventNotifier *n, int virq);
+int kvm_irqchip_remove_irq_notifier(KVMState *s, EventNotifier *n, int virq);
#endif