The data plane thread isn't allowed to call virtio_irq() directly
because that function is not thread-safe.  Use the guest notifier just
like virtio-net to handle IRQs.

When MSI-X is in use and the vector is unmasked, the guest notifier
directly sets the IRQ inside the host kernel.  If the vector is masked,
then QEMU's iothread needs to take note of the IRQ.  If MSI-X is not in
use, then QEMU's iothread handles the IRQ and this will be slower than
synchronously calling notify_irq() from the data plane thread.
---
 hw/virtio-blk.c |   28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index d75c187..bdff68a 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -73,6 +73,18 @@ static int get_raw_posix_fd_hack(VirtIOBlock *s)
     return *(int*)s->bs->file->opaque;
 }
 
+/* Raise an interrupt to signal guest, if necessary */
+static void virtio_blk_notify_guest(VirtIOBlock *s)
+{
+    /* Always notify when queue is empty (when feature acknowledge) */
+       if ((s->vring.vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) &&
+           (s->vring.vr.avail->idx != s->vring.last_avail_idx ||
+        !(s->vdev.guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))))
+               return;
+
+    event_notifier_set(virtio_queue_get_guest_notifier(s->vq));
+}
+
 static void complete_request(struct iocb *iocb, ssize_t ret, void *opaque)
 {
     VirtIOBlock *s = opaque;
@@ -154,7 +166,7 @@ static void process_request(IOQueue *ioq, struct iovec 
iov[], unsigned int out_n
             fdatasync(get_raw_posix_fd_hack(s));
             inhdr->status = VIRTIO_BLK_S_OK;
             vring_push(&s->vring, head, sizeof *inhdr);
-            virtio_irq(s->vq);
+            virtio_blk_notify_guest(s);
         }
         return;
 
@@ -222,8 +234,7 @@ static bool handle_io(EventHandler *handler)
     VirtIOBlock *s = container_of(handler, VirtIOBlock, io_handler);
 
     if (ioq_run_completion(&s->ioqueue, complete_request, s) > 0) {
-        /* TODO is this thread-safe and can it be done faster? */
-        virtio_irq(s->vq);
+        virtio_blk_notify_guest(s);
     }
 
     /* If there were more requests than iovecs, the vring will not be empty yet
@@ -251,11 +262,17 @@ static void data_plane_start(VirtIOBlock *s)
 
     vring_setup(&s->vring, &s->vdev, 0);
 
+    /* Set up guest notifier (irq) */
+    if (s->vdev.binding->set_guest_notifier(s->vdev.binding_opaque, 0, true) 
!= 0) {
+        fprintf(stderr, "virtio-blk failed to set guest notifier, ensure 
-enable-kvm is set\n");
+        exit(1);
+    }
+
     event_poll_init(&s->event_poll);
 
     /* Set up virtqueue notify */
     if (s->vdev.binding->set_host_notifier(s->vdev.binding_opaque, 0, true) != 
0) {
-        fprintf(stderr, "virtio-blk failed to set host notifier, ensure 
-enable-kvm is set\n");
+        fprintf(stderr, "virtio-blk failed to set host notifier\n");
         exit(1);
     }
     event_poll_add(&s->event_poll, &s->notify_handler,
@@ -296,6 +313,9 @@ static void data_plane_stop(VirtIOBlock *s)
     s->vdev.binding->set_host_notifier(s->vdev.binding_opaque, 0, false);
 
     event_poll_cleanup(&s->event_poll);
+
+    /* Clean up guest notifier (irq) */
+    s->vdev.binding->set_guest_notifier(s->vdev.binding_opaque, 0, false);
 }
 
 static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t val)
-- 
1.7.10.4


Reply via email to