Patchwork [05/12] aio: add Win32 implementation

login
register
mail settings
Submitter Paolo Bonzini
Date July 16, 2012, 10:42 a.m.
Message ID <1342435377-25897-6-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/171168/
State New
Headers show

Comments

Paolo Bonzini - July 16, 2012, 10:42 a.m.
The Win32 implementation only accepts EventNotifiers, thus a few
drivers are disabled under Windows.  It is possible to use the
same techniques in main-loop.c and reenable them; alternatively,
the drivers can be changed to use threads instead of non-blocking
I/O.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 Makefile.objs        |    6 +-
 aio.c => aio-posix.c |    0
 aio-win32.c          |  177 ++++++++++++++++++++++++++++++++++++++++++++++++++
 block/Makefile.objs  |    6 +-
 4 files changed, 185 insertions(+), 4 deletions(-)
 rename aio.c => aio-posix.c (100%)
 create mode 100644 aio-win32.c

Patch

diff --git a/Makefile.objs b/Makefile.objs
index 6ed1981..96d0e68 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -42,11 +42,11 @@  coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
 # block-obj-y is code used by both qemu system emulation and qemu-img
 
 block-obj-y = cutils.o cache-utils.o qemu-option.o module.o async.o
-block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o
+block-obj-y += nbd.o block.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o
 block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y)
 block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
-block-obj-$(CONFIG_POSIX) += event_notifier-posix.o
-block-obj-$(CONFIG_WIN32) += event_notifier-win32.o
+block-obj-$(CONFIG_POSIX) += event_notifier-posix.o aio-posix.o
+block-obj-$(CONFIG_WIN32) += event_notifier-win32.o aio-win32.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
 block-obj-y += block/
 
diff --git a/aio.c b/aio-posix.c
similarity index 100%
rename from aio.c
rename to aio-posix.c
diff --git a/aio-win32.c b/aio-win32.c
new file mode 100644
index 0000000..0936f7f
--- /dev/null
+++ b/aio-win32.c
@@ -0,0 +1,177 @@ 
+/*
+ * QEMU aio implementation for Win32
+ *
+ * Copyright IBM Corp., 2008
+ * Copyright Red Hat Inc., 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Paolo Bonzini     <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu-common.h"
+#include "block.h"
+#include "qemu-queue.h"
+#include "qemu_socket.h"
+
+typedef struct AioHandler AioHandler;
+
+/* The list of registered AIO handlers */
+static QLIST_HEAD(, AioHandler) aio_handlers;
+
+/* This is a simple lock used to protect the aio_handlers list.  Specifically,
+ * it's used to ensure that no callbacks are removed while we're walking and
+ * dispatching callbacks.
+ */
+static int walking_handlers;
+
+struct AioHandler
+{
+    EventNotifier *e;
+    EventNotifierHandler *io_notify;
+    AioFlushEventNotifierHandler *io_flush;
+    int deleted;
+    QLIST_ENTRY(AioHandler) node;
+};
+
+void qemu_aio_set_event_notifier(EventNotifier *e,
+                                 EventNotifierHandler *io_notify,
+                                 AioFlushEventNotifierHandler *io_flush)
+{
+    AioHandler *node;
+
+    QLIST_FOREACH(node, &aio_handlers, node) {
+        if (node->e == e && !node->deleted) {
+            break;
+        }
+    }
+
+    /* Are we deleting the fd handler? */
+    if (!io_notify) {
+        if (node) {
+            qemu_del_wait_object(event_notifier_get_handle(e),
+                                 (WaitObjectFunc *) node->io_notify, e);
+
+            /* If the lock is held, just mark the node as deleted */
+            if (walking_handlers) {
+                node->deleted = 1;
+            } else {
+                /* Otherwise, delete it for real.  We can't just mark it as
+                 * deleted because deleted nodes are only cleaned up after
+                 * releasing the walking_handlers lock.
+                 */
+                QLIST_REMOVE(node, node);
+                g_free(node);
+            }
+        }
+    } else {
+        if (node == NULL) {
+            /* Alloc and insert if it's not already there */
+            node = g_malloc0(sizeof(AioHandler));
+            node->e = e;
+            QLIST_INSERT_HEAD(&aio_handlers, node, node);
+        }
+        /* Update handler with latest information */
+        node->io_notify = io_notify;
+        node->io_flush = io_flush;
+        qemu_add_wait_object(event_notifier_get_handle(e),
+                             (WaitObjectFunc *) io_notify, e);
+    }
+}
+
+void qemu_aio_flush(void)
+{
+    while (qemu_aio_wait());
+}
+
+bool qemu_aio_wait(void)
+{
+    AioHandler *node;
+    HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
+    bool busy;
+    int count;
+    int ret;
+    int timeout;
+
+    /*
+     * If there are callbacks left that have been queued, we need to call then.
+     * Do not call select in this case, because it is possible that the caller
+     * does not need a complete flush (as is the case for qemu_aio_wait loops).
+     */
+    if (qemu_bh_poll()) {
+        return true;
+    }
+
+    walking_handlers = 1;
+
+    /* fill fd sets */
+    busy = false;
+    count = 0;
+    QLIST_FOREACH(node, &aio_handlers, node) {
+        /* If there aren't pending AIO operations, don't invoke callbacks.
+         * Otherwise, if there are no AIO requests, qemu_aio_wait() would
+         * wait indefinitely.
+         */
+        if (node->io_flush) {
+            if (node->io_flush(node->e) == 0) {
+                continue;
+            }
+            busy = true;
+        }
+        if (!node->deleted && node->io_notify) {
+            events[count++] = event_notifier_get_handle(node->e);
+        }
+    }
+
+    walking_handlers = 0;
+
+    /* No AIO operations?  Get us out of here */
+    if (!busy) {
+        return false;
+    }
+
+    /* wait until next event */
+    timeout = INFINITE;
+    for (;;) {
+        ret = WaitForMultipleObjects(count, events, FALSE, timeout);
+        if ((DWORD) (ret - WAIT_OBJECT_0) >= count) {
+            break;
+        }
+
+        timeout = 0;
+
+        /* if we have any signaled events, dispatch event */
+        walking_handlers = 1;
+
+        /* we have to walk very carefully in case
+         * qemu_aio_set_fd_handler is called while we're walking */
+        node = QLIST_FIRST(&aio_handlers);
+        while (node) {
+            AioHandler *tmp;
+
+            if (!node->deleted &&
+                event_notifier_get_handle(node->e) == events[ret - WAIT_OBJECT_0] &&
+                node->io_notify) {
+                node->io_notify(node->e);
+            }
+
+            tmp = node;
+            node = QLIST_NEXT(node, node);
+
+            if (tmp->deleted) {
+                QLIST_REMOVE(tmp, node);
+                g_free(tmp);
+            }
+        }
+
+        walking_handlers = 0;
+    }
+
+    return true;
+}
diff --git a/block/Makefile.objs b/block/Makefile.objs
index b5754d3..65d4dc6 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -2,10 +2,14 @@  block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat
 block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
 block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
 block-obj-y += qed-check.o
-block-obj-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
+block-obj-y += parallels.o blkdebug.o blkverify.o
 block-obj-y += stream.o
 block-obj-$(CONFIG_WIN32) += raw-win32.o
 block-obj-$(CONFIG_POSIX) += raw-posix.o
+
+ifeq ($(CONFIG_POSIX),y)
+block-obj-y += nbd.o sheepdog.o
 block-obj-$(CONFIG_LIBISCSI) += iscsi.o
 block-obj-$(CONFIG_CURL) += curl.o
 block-obj-$(CONFIG_RBD) += rbd.o
+endif