Skip to contentSkip to conversation

Portable thread-pool/AIO, Win32 emulated AIO

PATCH Superseded Paolo Bonzini 4 participants 28 messages Jul 16, 2012 – Jul 24, 2012

Subsystems (2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover Portable thread-pool/AIO, Win32 emulated AIO

This patch series is part 2 in my EventNotifier/AIO improvements for QEMU 1.2. It extends use of EventNotifier to the main loop and AIO subsystems. A new API using EventNotifier is added to aio.c and a new portable thread pool is introduced (based on code from posix-aio-compat.c, mostly) that uses this API. raw-posix.c is converted to use the new thread pool, and support for asynchronous I/O is finally added to Win32 as well.

The network drivers (curl, libiscsi, nbd) have to be disabled under Windows.

I tested this under Wine, with a RHEL virtual machine booting just as glacially as before. However, "info blockstats" does show a slightly higher overhead, so I would like this to be tested on real Windows hosts. However, even if the result is negative, I would prefer to keep the early parts (i.e. drop only the last patch) since they are a prerequisite for more improvements to block/raw-posix.c (such as asynchronous discard support).

Paolo Bonzini (12):
event_notifier: enable it to use pipes
event_notifier: add Win32 implementation
main-loop: use event notifiers
aio: provide platform-independent API
aio: add Win32 implementation
linux-aio: use event notifiers
qemu-thread: add QemuSemaphore
aio: add generic thread-pool facility
block: switch posix-aio-compat to threadpool

  raw: merge posix-aio-compat.c into block/raw-posix.c
  raw-posix: rename raw-posix-aio.h, hide unavailable prototypes
  raw-win32: add emulated AIO support
 Makefile.objs                        |   13 +-
 aio.c => aio-posix.c                 |    9 +
 aio-win32.c                          |  177 +++++++++
 block/Makefile.objs                  |    6 +-
 block/{raw-posix-aio.h => raw-aio.h} |   19 +-
 block/raw-posix.c                    |  301 ++++++++++++++-
 block/raw-win32.c                    |  189 +++++++---
 event_notifier-posix.c               |  118 ++++++
 event_notifier-win32.c               |   59 +++
 event_notifier.c                     |   67 ----
 event_notifier.h                     |   20 +-
 linux-aio.c                          |   51 +--
 main-loop.c                          |  106 +-----
 oslib-posix.c                        |   31 --
 posix-aio-compat.c                   |  681 ----------------------------------
 qemu-aio.h                           |   19 +-
 qemu-common.h                        |    1 -
 qemu-thread-posix.c                  |   74 ++++
 qemu-thread-posix.h                  |    5 +
 qemu-thread-win32.c                  |   35 ++
 qemu-thread-win32.h                  |    4 +
 qemu-thread.h                        |    7 +
 thread-pool.c                        |  279 ++++++++++++++
 thread-pool.h                        |   34 ++
 trace-events                         |    5 +
 25 files changed, 1329 insertions(+), 981 deletions(-)
 rename aio.c => aio-posix.c (92%)
 create mode 100644 aio-win32.c
 rename block/{raw-posix-aio.h => raw-aio.h} (62%)
 create mode 100644 event_notifier-posix.c
 create mode 100644 event_notifier-win32.c
 delete mode 100644 event_notifier.c
 delete mode 100644 posix-aio-compat.c
 create mode 100644 thread-pool.c
 create mode 100644 thread-pool.h
Signature · 1 line
1.7.10.4
#1 lore raw

PATCH 1/12 event_notifier: enable it to use pipes

This takes the eventfd emulation code from the main loop and adds it to EventNotifier. When the EventNotifier is used for the main loop too, we need this compatibility code.

Without CONFIG_EVENTFD, event_notifier_get_fd is only usable for the "read" side of the notifier, for example to set a select() handler.

---
 event_notifier.c |   83 +++++++++++++++++++++++++++++++++++++++++++-----------
 event_notifier.h |    3 +-
 2 files changed, 69 insertions(+), 17 deletions(-)
event_notifier.c +67 -16
index 2c207e1..dde2d32 100644
@@ -20,48 +20,99 @@
 
 void event_notifier_init_fd(EventNotifier *e, int fd)
 {
-    e->fd = fd;
+    e->rfd = fd;
+    e->wfd = fd;
 }
 
 int event_notifier_init(EventNotifier *e, int active)
 {
+    int fds[2];
+    int ret;
+
 #ifdef CONFIG_EVENTFD
-    int fd = eventfd(!!active, EFD_NONBLOCK | EFD_CLOEXEC);
-    if (fd < 0)
-        return -errno;
-    e->fd = fd;
-    return 0;
+    ret = eventfd(0, O_NONBLOCK);
 #else
-    return -ENOSYS;
+    ret = -1;
+    errno = ENOSYS;
 #endif
+    if (ret >= 0) {
+        e->rfd = e->wfd = ret;
+        qemu_set_cloexec(ret);
+    } else {
+        if (errno != ENOSYS) {
+            return -errno;
+        }
+        if (qemu_pipe(fds) < 0) {
+            return -errno;
+        }
+        ret = fcntl_setfl(fds[0], O_NONBLOCK);
+        if (ret < 0) {
+            goto fail;
+        }
+        ret = fcntl_setfl(fds[1], O_NONBLOCK);
+        if (ret < 0) {
+            goto fail;
+        }
+        e->rfd = fds[0];
+        e->wfd = fds[1];
+    }
+    if (active)
+        event_notifier_set(e);
+    return 0;
+
+fail:
+    close(fds[0]);
+    close(fds[1]);
+    return ret;
 }
 
 void event_notifier_cleanup(EventNotifier *e)
 {
-    close(e->fd);
+    if (e->rfd != e->wfd) {
+        close(e->rfd);
+    }
+    close(e->wfd);
 }
 
 int event_notifier_get_fd(EventNotifier *e)
 {
-    return e->fd;
+    return e->rfd;
 }
 
 int event_notifier_set_handler(EventNotifier *e,
                                EventNotifierHandler *handler)
 {
-    return qemu_set_fd_handler(e->fd, (IOHandler *)handler, NULL, e);
+    return qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e);
 }
 
 int event_notifier_set(EventNotifier *e)
 {
-    uint64_t value = 1;
-    int r = write(e->fd, &value, sizeof(value));
-    return r == sizeof(value);
+    static const uint64_t value = 1;
+    ssize_t ret;
+
+    do {
+        ret = write(e->wfd, &value, sizeof(value));
+    } while (ret < 0 && errno == EINTR);
+
+    /* EAGAIN is fine, a read must be pending.  */
+    if (ret < 0 && errno != EAGAIN) {
+        return -1;
+    }
+    return 0;
 }
 
 int event_notifier_test_and_clear(EventNotifier *e)
 {
-    uint64_t value;
-    int r = read(e->fd, &value, sizeof(value));
-    return r == sizeof(value);
+    int value;
+    ssize_t len;
+    char buffer[512];
+
+    /* Drain the notify pipe.  For eventfd, only 8 bytes will be read.  */
+    value = 0;
+    do {
+        len = read(e->rfd, buffer, sizeof(buffer));
+        value |= (len > 0);
+    } while ((len == -1 && errno == EINTR) || len == sizeof(buffer));
+
+    return value;
 }
event_notifier.h +2 -1
index f0ec2f2..f04d12d 100644
@@ -16,7 +16,8 @@
 #include "qemu-common.h"
 
 struct EventNotifier {
-    int fd;
+    int rfd;
+    int wfd;
 };
 
 typedef void EventNotifierHandler(EventNotifier *);
Signature · 1 line
1.7.10.4
#2 lore raw

Re: [Qemu-devel] [PATCH 01/12] event_notifier: enable it to use pipes

Quoted 36 lines · Paolo Bonzini

This takes the eventfd emulation code from the main loop and adds it to EventNotifier. When the EventNotifier is used for the main loop too, we need this compatibility code.

Without CONFIG_EVENTFD, event_notifier_get_fd is only usable for the "read" side of the notifier, for example to set a select() handler.

---
 event_notifier.c |   83 +++++++++++++++++++++++++++++++++++++++++++-----------
 event_notifier.h |    3 +-
 2 files changed, 69 insertions(+), 17 deletions(-)
event_notifier.c +5 -5
index 2c207e1..dde2d32 100644
@@ -20,48 +20,99 @@
 
 void event_notifier_init_fd(EventNotifier *e, int fd)
 {
-    e->fd = fd;
+    e->rfd = fd;
+    e->wfd = fd;
 }
 
 int event_notifier_init(EventNotifier *e, int active)
 {
+    int fds[2];
+    int ret;
+
 #ifdef CONFIG_EVENTFD
-    int fd = eventfd(!!active, EFD_NONBLOCK | EFD_CLOEXEC);
-    if (fd < 0)
-        return -errno;
-    e->fd = fd;

event_notifier.c line 40

Quoted 10 lines
event_notifier.c +6 -2
-    return 0;
+    ret = eventfd(0, O_NONBLOCK);
 #else
-    return -ENOSYS;
+    ret = -1;
+    errno = ENOSYS;
 #endif
+    if (ret >= 0) {
+        e->rfd = e->wfd = ret;
+        qemu_set_cloexec(ret);

This is kind of redundant with EFD_CLOEXEC, no?

Quoted 10 lines
event_notifier.c +10 -0
+    } else {
+        if (errno != ENOSYS) {
+            return -errno;
+        }
+        if (qemu_pipe(fds) < 0) {
+            return -errno;
+        }
+        ret = fcntl_setfl(fds[0], O_NONBLOCK);
+        if (ret < 0) {
+            goto fail;

event_notifier.c line 60

Quoted 10 lines
event_notifier.c +10 -0
+        }
+        ret = fcntl_setfl(fds[1], O_NONBLOCK);
+        if (ret < 0) {
+            goto fail;
+        }
+        e->rfd = fds[0];
+        e->wfd = fds[1];
+    }
+    if (active)
+        event_notifier_set(e);

Missing a curly..

The rest looks good.

Regards,

Anthony Liguori

Quoted 83 lines
event_notifier.c +36 -9
+    return 0;
+
+fail:
+    close(fds[0]);
+    close(fds[1]);
+    return ret;
 }
 
 void event_notifier_cleanup(EventNotifier *e)
 {
-    close(e->fd);
+    if (e->rfd != e->wfd) {
+        close(e->rfd);
+    }
+    close(e->wfd);
 }
 
 int event_notifier_get_fd(EventNotifier *e)
 {
-    return e->fd;
+    return e->rfd;
 }
 
 int event_notifier_set_handler(EventNotifier *e,
                                EventNotifierHandler *handler)
 {
-    return qemu_set_fd_handler(e->fd, (IOHandler *)handler, NULL, e);
+    return qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e);
 }
 
 int event_notifier_set(EventNotifier *e)
 {
-    uint64_t value = 1;
-    int r = write(e->fd, &value, sizeof(value));
-    return r == sizeof(value);
+    static const uint64_t value = 1;
+    ssize_t ret;
+
+    do {
+        ret = write(e->wfd, &value, sizeof(value));
+    } while (ret < 0 && errno == EINTR);
+
+    /* EAGAIN is fine, a read must be pending.  */
+    if (ret < 0 && errno != EAGAIN) {
+        return -1;
+    }
+    return 0;
 }
 
 int event_notifier_test_and_clear(EventNotifier *e)
 {
-    uint64_t value;
-    int r = read(e->fd, &value, sizeof(value));
-    return r == sizeof(value);
+    int value;
+    ssize_t len;
+    char buffer[512];
+
+    /* Drain the notify pipe.  For eventfd, only 8 bytes will be read.  */
+    value = 0;
+    do {
+        len = read(e->rfd, buffer, sizeof(buffer));
+        value |= (len > 0);
+    } while ((len == -1 && errno == EINTR) || len == sizeof(buffer));
+
+    return value;
 }
event_notifier.h +2 -1
index f0ec2f2..f04d12d 100644
@@ -16,7 +16,8 @@
 #include "qemu-common.h"
 
 struct EventNotifier {
-    int fd;
+    int rfd;
+    int wfd;
 };
 
 typedef void EventNotifierHandler(EventNotifier *);
Signature · 1 line
1.7.10.4
#3 lore raw

PATCH 2/12 event_notifier: add Win32 implementation

In reply to Paolo Bonzini

The Win32 implementation of EventNotifier is a trivial wrapper around manual-reset events.

---
 Makefile.objs                              |    4 +-
 event_notifier.c => event_notifier-posix.c |    0
 event_notifier-win32.c                     |   59 ++++++++++++++++++++++++++++
 event_notifier.h                           |   17 +++++++-
 4 files changed, 77 insertions(+), 3 deletions(-)
 rename event_notifier.c => event_notifier-posix.c (100%)
 create mode 100644 event_notifier-win32.c
Makefile.objs +3 -1
index 625c4d5..ecdfaf9 100644
@@ -45,6 +45,8 @@ 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 += $(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_LINUX_AIO) += linux-aio.o
 block-obj-y += block/
 
@@ -90,7 +92,7 @@ common-obj-y += bt-host.o bt-vhci.o
 
 common-obj-y += iov.o acl.o
 common-obj-$(CONFIG_POSIX) += compatfd.o
-common-obj-y += notify.o event_notifier.o
+common-obj-y += notify.o
 common-obj-y += qemu-timer.o qemu-timer-common.o
 
 common-obj-$(CONFIG_SLIRP) += slirp/
event_notifier-posix.c renamed from event_notifier.c +0 -0
similarity index 100%
rename from event_notifier.c
rename to event_notifier-posix.c
event_notifier-win32.c new file +59 -0

59 lines not on this page · raw message

event_notifier.h +15 -2
index f04d12d..88b57af 100644
@@ -15,19 +15,32 @@
 
 #include "qemu-common.h"
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 struct EventNotifier {
+#ifdef _WIN32
+    HANDLE event;
+#else
     int rfd;
     int wfd;
+#endif
 };
 
 typedef void EventNotifierHandler(EventNotifier *);
 
-void event_notifier_init_fd(EventNotifier *, int fd);
 int event_notifier_init(EventNotifier *, int active);
 void event_notifier_cleanup(EventNotifier *);
-int event_notifier_get_fd(EventNotifier *);
 int event_notifier_set(EventNotifier *);
 int event_notifier_test_and_clear(EventNotifier *);
 int event_notifier_set_handler(EventNotifier *, EventNotifierHandler *);
 
+#ifdef CONFIG_POSIX
+void event_notifier_init_fd(EventNotifier *, int fd);
+int event_notifier_get_fd(EventNotifier *);
+#else
+HANDLE event_notifier_get_handle(EventNotifier *);
+#endif
+
 #endif
Signature · 1 line
1.7.10.4
#4 lore raw

PATCH 3/12 main-loop: use event notifiers

In reply to Paolo Bonzini
---
 Makefile.objs |    4 +--
 main-loop.c   |  106 ++++++++-------------------------------------------------
 oslib-posix.c |   31 -----------------
 qemu-common.h |    1 -
 4 files changed, 17 insertions(+), 125 deletions(-)
Makefile.objs +2 -2
index ecdfaf9..6ed1981 100644
@@ -20,8 +20,8 @@ universal-obj-y += $(qom-obj-y)
 #######################################################################
 # oslib-obj-y is code depending on the OS (win32 vs posix)
 oslib-obj-y = osdep.o
-oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
-oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
+oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o event_notifier-win32.o
+oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o event_notifier-posix.o
 
 #######################################################################
 # coroutines
main-loop.c +15 -91

148 lines not on this page · raw message

oslib-posix.c +0 -31
index 6b7ba64..2c6b044 100644
@@ -58,9 +58,6 @@ static int running_on_valgrind = -1;
 #ifdef CONFIG_LINUX
 #include <sys/syscall.h>
 #endif
-#ifdef CONFIG_EVENTFD
-#include <sys/eventfd.h>
-#endif
 
 int qemu_get_thread_id(void)
 {
@@ -180,34 +177,6 @@ int qemu_pipe(int pipefd[2])
     return ret;
 }
 
-/*
- * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set.
- */
-int qemu_eventfd(int fds[2])
-{
-#ifdef CONFIG_EVENTFD
-    int ret;
-
-    ret = eventfd(0, 0);
-    if (ret >= 0) {
-        fds[0] = ret;
-        fds[1] = dup(ret);
-        if (fds[1] == -1) {
-            close(ret);
-            return -1;
-        }
-        qemu_set_cloexec(ret);
-        qemu_set_cloexec(fds[1]);
-        return 0;
-    }
-    if (errno != ENOSYS) {
-        return -1;
-    }
-#endif
-
-    return qemu_pipe(fds);
-}
-
 int qemu_utimens(const char *path, const struct timespec *times)
 {
     struct timeval tv[2], tv_now;
qemu-common.h +0 -1
index 9d9e603..036d254 100644
@@ -195,7 +195,6 @@ ssize_t qemu_recv_full(int fd, void *buf, size_t count, int flags)
     QEMU_WARN_UNUSED_RESULT;
 
 #ifndef _WIN32
-int qemu_eventfd(int pipefd[2]);
 int qemu_pipe(int pipefd[2]);
 #endif
 
Signature · 1 line
1.7.10.4
Reviewed-by on v1 3/12
#5 lore raw

Re: [Qemu-devel] [PATCH 03/12] main-loop: use event notifiers

Paolo Bonzini <[email protected]> writes:

Regards,

Anthony Liguori

Quoted 242 lines
---
 Makefile.objs |    4 +--
 main-loop.c   |  106 ++++++++-------------------------------------------------
 oslib-posix.c |   31 -----------------
 qemu-common.h |    1 -
 4 files changed, 17 insertions(+), 125 deletions(-)
Makefile.objs +2 -2
index ecdfaf9..6ed1981 100644
@@ -20,8 +20,8 @@ universal-obj-y += $(qom-obj-y)
 #######################################################################
 # oslib-obj-y is code depending on the OS (win32 vs posix)
 oslib-obj-y = osdep.o
-oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
-oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
+oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o event_notifier-win32.o
+oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o event_notifier-posix.o
 
 #######################################################################
 # coroutines
main-loop.c +15 -91
index eb3b6e6..81f49b3 100644
@@ -26,75 +26,12 @@
 #include "qemu-timer.h"
 #include "slirp/slirp.h"
 #include "main-loop.h"
+#include "event_notifier.h"
 
 #ifndef _WIN32
 
 #include "compatfd.h"
 
-static int io_thread_fd = -1;
-
-void qemu_notify_event(void)
-{
-    /* Write 8 bytes to be compatible with eventfd.  */
-    static const uint64_t val = 1;
-    ssize_t ret;
-
-    if (io_thread_fd == -1) {
-        return;
-    }
-    do {
-        ret = write(io_thread_fd, &val, sizeof(val));
-    } while (ret < 0 && errno == EINTR);
-
-    /* EAGAIN is fine, a read must be pending.  */
-    if (ret < 0 && errno != EAGAIN) {
-        fprintf(stderr, "qemu_notify_event: write() failed: %s\n",
-                strerror(errno));
-        exit(1);
-    }
-}
-
-static void qemu_event_read(void *opaque)
-{
-    int fd = (intptr_t)opaque;
-    ssize_t len;
-    char buffer[512];
-
-    /* Drain the notify pipe.  For eventfd, only 8 bytes will be read.  */
-    do {
-        len = read(fd, buffer, sizeof(buffer));
-    } while ((len == -1 && errno == EINTR) || len == sizeof(buffer));
-}
-
-static int qemu_event_init(void)
-{
-    int err;
-    int fds[2];
-
-    err = qemu_eventfd(fds);
-    if (err == -1) {
-        return -errno;
-    }
-    err = fcntl_setfl(fds[0], O_NONBLOCK);
-    if (err < 0) {
-        goto fail;
-    }
-    err = fcntl_setfl(fds[1], O_NONBLOCK);
-    if (err < 0) {
-        goto fail;
-    }
-    qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL,
-                         (void *)(intptr_t)fds[0]);
-
-    io_thread_fd = fds[1];
-    return 0;
-
-fail:
-    close(fds[0]);
-    close(fds[1]);
-    return err;
-}
-
 /* If we have signalfd, we mask out the signals we want to handle and then
  * use signalfd to listen for them.  We rely on whatever the current signal
  * handler is to dispatch the signals when we receive them.
@@ -164,40 +101,22 @@ static int qemu_signal_init(void)
 
 #else /* _WIN32 */
 
-static HANDLE qemu_event_handle = NULL;
-
-static void dummy_event_handler(void *opaque)
-{
-}
-
-static int qemu_event_init(void)
+static int qemu_signal_init(void)
 {
-    qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (!qemu_event_handle) {
-        fprintf(stderr, "Failed CreateEvent: %ld\n", GetLastError());
-        return -1;
-    }
-    qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL);
     return 0;
 }
+#endif
+
+static EventNotifier io_thread_notifier;
+static int io_thread_initialized;
 
 void qemu_notify_event(void)
 {
-    if (!qemu_event_handle) {
+    if (!io_thread_initialized) {
         return;
     }
-    if (!SetEvent(qemu_event_handle)) {
-        fprintf(stderr, "qemu_notify_event: SetEvent failed: %ld\n",
-                GetLastError());
-        exit(1);
-    }
-}
-
-static int qemu_signal_init(void)
-{
-    return 0;
+    event_notifier_set(&io_thread_notifier);
 }
-#endif
 
 int main_loop_init(void)
 {
@@ -210,11 +129,15 @@ int main_loop_init(void)
     }
 
     /* Note eventfd must be drained before signalfd handlers run */
-    ret = qemu_event_init();
+    ret = event_notifier_init(&io_thread_notifier, 0);
     if (ret) {
         return ret;
     }
 
+    io_thread_initialized = true;
+    event_notifier_set_handler(&io_thread_notifier,
+                               (EventNotifierHandler *)
+                               event_notifier_test_and_clear);
     return 0;
 }
 
@@ -400,7 +323,8 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
 
 void qemu_fd_register(int fd)
 {
-    WSAEventSelect(fd, qemu_event_handle, FD_READ | FD_ACCEPT | FD_CLOSE |
+    WSAEventSelect(fd, event_notifier_get_handle(&io_thread_notifier),
+                   FD_READ | FD_ACCEPT | FD_CLOSE |
                    FD_CONNECT | FD_WRITE | FD_OOB);
 }
 
oslib-posix.c +0 -31
index 6b7ba64..2c6b044 100644
@@ -58,9 +58,6 @@ static int running_on_valgrind = -1;
 #ifdef CONFIG_LINUX
 #include <sys/syscall.h>
 #endif
-#ifdef CONFIG_EVENTFD
-#include <sys/eventfd.h>
-#endif
 
 int qemu_get_thread_id(void)
 {
@@ -180,34 +177,6 @@ int qemu_pipe(int pipefd[2])
     return ret;
 }
 
-/*
- * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set.
- */
-int qemu_eventfd(int fds[2])
-{
-#ifdef CONFIG_EVENTFD
-    int ret;
-
-    ret = eventfd(0, 0);
-    if (ret >= 0) {
-        fds[0] = ret;
-        fds[1] = dup(ret);
-        if (fds[1] == -1) {
-            close(ret);
-            return -1;
-        }
-        qemu_set_cloexec(ret);
-        qemu_set_cloexec(fds[1]);
-        return 0;
-    }
-    if (errno != ENOSYS) {
-        return -1;
-    }
-#endif
-
-    return qemu_pipe(fds);
-}
-
 int qemu_utimens(const char *path, const struct timespec *times)
 {
     struct timeval tv[2], tv_now;
qemu-common.h +0 -1
index 9d9e603..036d254 100644
@@ -195,7 +195,6 @@ ssize_t qemu_recv_full(int fd, void *buf, size_t count, int flags)
     QEMU_WARN_UNUSED_RESULT;
 
 #ifndef _WIN32
-int qemu_eventfd(int pipefd[2]);
 int qemu_pipe(int pipefd[2]);
 #endif
 
Signature · 1 line
1.7.10.4
#6 lore raw

PATCH 4/12 aio: provide platform-independent API

In reply to Paolo Bonzini

This adds to aio.c a platform-independent API based on EventNotifiers, that can be used by the portable thread pool.

---
 aio.c      |    9 +++++++++
 qemu-aio.h |   19 ++++++++++++++++++-
 2 files changed, 27 insertions(+), 1 deletion(-)
aio.c +9 -0
index 0a9eb10..417ceb4 100644
@@ -97,6 +97,15 @@ int qemu_aio_set_fd_handler(int fd,
     return 0;
 }
 
+void qemu_aio_set_event_notifier(EventNotifier *notifier,
+                                 EventNotifierHandler *io_read,
+                                 AioFlushEventNotifierHandler *io_flush)
+{
+    qemu_aio_set_fd_handler(event_notifier_get_fd(notifier),
+                            (IOHandler *)io_read, NULL,
+                            (AioFlushHandler *)io_flush, notifier);
+}
+
 void qemu_aio_flush(void)
 {
     while (qemu_aio_wait());
qemu-aio.h +18 -1
index bfdd35f..89c766c 100644
@@ -16,6 +16,7 @@
 
 #include "qemu-common.h"
 #include "qemu-char.h"
+#include "event_notifier.h"
 
 typedef struct BlockDriverAIOCB BlockDriverAIOCB;
 typedef void BlockDriverCompletionFunc(void *opaque, int ret);
@@ -39,7 +40,7 @@ void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
 void qemu_aio_release(void *p);
 
 /* Returns 1 if there are still outstanding AIO requests; 0 otherwise */
-typedef int (AioFlushHandler)(void *opaque);
+typedef int (AioFlushEventNotifierHandler)(EventNotifier *e);
 
 /* Flush any pending AIO operation. This function will block until all
  * outstanding AIO operations have been completed or cancelled. */
@@ -53,6 +54,10 @@ void qemu_aio_flush(void);
  * Return whether there is still any pending AIO operation.  */
 bool qemu_aio_wait(void);
 
+#ifdef CONFIG_POSIX
+/* Returns 1 if there are still outstanding AIO requests; 0 otherwise */
+typedef int (AioFlushHandler)(void *opaque);
+
 /* Register a file descriptor and associated callbacks.  Behaves very similarly
  * to qemu_set_fd_handler2.  Unlike qemu_set_fd_handler2, these callbacks will
  * be invoked when using either qemu_aio_wait() or qemu_aio_flush().
@@ -65,5 +70,17 @@ int qemu_aio_set_fd_handler(int fd,
                             IOHandler *io_write,
                             AioFlushHandler *io_flush,
                             void *opaque);
+#endif
+
+/* Register an event notifier and associated callbacks.  Behaves very similarly
+ * to event_notifier_set_handler.  Unlike event_notifier_set_handler, these callbacks
+ * will be invoked when using either qemu_aio_wait() or qemu_aio_flush().
+ *
+ * Code that invokes AIO completion functions should rely on this function
+ * instead of event_notifier_set_handler.
+ */
+void qemu_aio_set_event_notifier(EventNotifier *notifier,
+                                 EventNotifierHandler *io_read,
+                                 AioFlushEventNotifierHandler *io_flush);
 
 #endif
Signature · 1 line
1.7.10.4
#7 lore raw

PATCH 5/12 aio: add Win32 implementation

In reply to Paolo Bonzini

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.

---
 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
Makefile.objs +3 -3
index 6ed1981..96d0e68 100644
@@ -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/
 
aio-posix.c renamed from aio.c +0 -0
similarity index 100%
rename from aio.c
rename to aio-posix.c
aio-win32.c new file +177 -0

177 lines not on this page · raw message

block/Makefile.objs +5 -1
index b5754d3..65d4dc6 100644
@@ -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
Signature · 1 line
1.7.10.4
#8 lore raw

PATCH 6/12 linux-aio: use event notifiers

In reply to Paolo Bonzini

Since linux-aio already uses an eventfd, converting it to use the EventNotifier-based API simplifies the code even though it is not meant to be portable.

---
 linux-aio.c |   49 +++++++++++++++++++------------------------------
 1 file changed, 19 insertions(+), 30 deletions(-)
linux-aio.c +19 -30
index fa0fbf3..779f793 100644
@@ -10,8 +10,8 @@
 #include "qemu-common.h"
 #include "qemu-aio.h"
 #include "block/raw-posix-aio.h"
+#include "event_notifier.h"
 
-#include <sys/eventfd.h>
 #include <libaio.h>
 
 /*
@@ -37,7 +37,7 @@ struct qemu_laiocb {
 
 struct qemu_laio_state {
     io_context_t ctx;
-    int efd;
+    EventNotifier e;
     int count;
 };
 
@@ -76,29 +76,17 @@ static void qemu_laio_process_completion(struct qemu_laio_state *s,
     qemu_aio_release(laiocb);
 }
 
-static void qemu_laio_completion_cb(void *opaque)
+static void qemu_laio_completion_cb(EventNotifier *e)
 {
-    struct qemu_laio_state *s = opaque;
+    struct qemu_laio_state *s = container_of(e, struct qemu_laio_state, e);
 
-    while (1) {
+    while (event_notifier_test_and_clear(&s->e)) {
         struct io_event events[MAX_EVENTS];
-        uint64_t val;
-        ssize_t ret;
         struct timespec ts = { 0 };
         int nevents, i;
 
         do {
-            ret = read(s->efd, &val, sizeof(val));
-        } while (ret == -1 && errno == EINTR);
-
-        if (ret == -1 && errno == EAGAIN)
-            break;
-
-        if (ret != 8)
-            break;
-
-        do {
-            nevents = io_getevents(s->ctx, val, MAX_EVENTS, events, &ts);
+            nevents = io_getevents(s->ctx, MAX_EVENTS, MAX_EVENTS, events, &ts);
         } while (nevents == -EINTR);
 
         for (i = 0; i < nevents; i++) {
@@ -112,9 +100,9 @@ static void qemu_laio_completion_cb(void *opaque)
     }
 }
 
-static int qemu_laio_flush_cb(void *opaque)
+static int qemu_laio_flush_cb(EventNotifier *e)
 {
-    struct qemu_laio_state *s = opaque;
+    struct qemu_laio_state *s = container_of(e, struct qemu_laio_state, e);
 
     return (s->count > 0) ? 1 : 0;
 }
@@ -146,8 +134,9 @@ static void laio_cancel(BlockDriverAIOCB *blockacb)
      * We might be able to do this slightly more optimal by removing the
      * O_NONBLOCK flag.
      */
-    while (laiocb->ret == -EINPROGRESS)
-        qemu_laio_completion_cb(laiocb->ctx);
+    while (laiocb->ret == -EINPROGRESS) {
+        qemu_laio_completion_cb(&laiocb->ctx->e);
+    }
 }
 
 static AIOPool laio_pool = {
@@ -186,7 +175,7 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
                         __func__, type);
         goto out_free_aiocb;
     }
-    io_set_eventfd(&laiocb->iocb, s->efd);
+    io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e));
     s->count++;
 
     if (io_submit(s->ctx, 1, &iocbs) < 0)
@@ -205,21 +194,21 @@ void *laio_init(void)
     struct qemu_laio_state *s;
 
     s = g_malloc0(sizeof(*s));
-    s->efd = eventfd(0, 0);
-    if (s->efd == -1)
+    if (event_notifier_init(&s->e, false) < 0) {
         goto out_free_state;
-    fcntl(s->efd, F_SETFL, O_NONBLOCK);
+    }
 
-    if (io_setup(MAX_EVENTS, &s->ctx) != 0)
+    if (io_setup(MAX_EVENTS, &s->ctx) != 0) {
         goto out_close_efd;
+    }
 
-    qemu_aio_set_fd_handler(s->efd, qemu_laio_completion_cb, NULL,
-        qemu_laio_flush_cb, s);
+    qemu_aio_set_event_notifier(&s->e, qemu_laio_completion_cb,
+                                qemu_laio_flush_cb);
 
     return s;
 
 out_close_efd:
-    close(s->efd);
+    event_notifier_cleanup(&s->e);
 out_free_state:
     g_free(s);
     return NULL;
Signature · 1 line
1.7.10.4
Reviewed-by on v1 6/12
#9 lore raw

Re: [Qemu-devel] [PATCH 06/12] linux-aio: use event notifiers

Quoted 4 lines · Paolo Bonzini

Since linux-aio already uses an eventfd, converting it to use the EventNotifier-based API simplifies the code even though it is not meant to be portable.

Regards,

Anthony Liguori

Quoted 126 lines
---
 linux-aio.c |   49 +++++++++++++++++++------------------------------
 1 file changed, 19 insertions(+), 30 deletions(-)
linux-aio.c +19 -30
index fa0fbf3..779f793 100644
@@ -10,8 +10,8 @@
 #include "qemu-common.h"
 #include "qemu-aio.h"
 #include "block/raw-posix-aio.h"
+#include "event_notifier.h"
 
-#include <sys/eventfd.h>
 #include <libaio.h>
 
 /*
@@ -37,7 +37,7 @@ struct qemu_laiocb {
 
 struct qemu_laio_state {
     io_context_t ctx;
-    int efd;
+    EventNotifier e;
     int count;
 };
 
@@ -76,29 +76,17 @@ static void qemu_laio_process_completion(struct qemu_laio_state *s,
     qemu_aio_release(laiocb);
 }
 
-static void qemu_laio_completion_cb(void *opaque)
+static void qemu_laio_completion_cb(EventNotifier *e)
 {
-    struct qemu_laio_state *s = opaque;
+    struct qemu_laio_state *s = container_of(e, struct qemu_laio_state, e);
 
-    while (1) {
+    while (event_notifier_test_and_clear(&s->e)) {
         struct io_event events[MAX_EVENTS];
-        uint64_t val;
-        ssize_t ret;
         struct timespec ts = { 0 };
         int nevents, i;
 
         do {
-            ret = read(s->efd, &val, sizeof(val));
-        } while (ret == -1 && errno == EINTR);
-
-        if (ret == -1 && errno == EAGAIN)
-            break;
-
-        if (ret != 8)
-            break;
-
-        do {
-            nevents = io_getevents(s->ctx, val, MAX_EVENTS, events, &ts);
+            nevents = io_getevents(s->ctx, MAX_EVENTS, MAX_EVENTS, events, &ts);
         } while (nevents == -EINTR);
 
         for (i = 0; i < nevents; i++) {
@@ -112,9 +100,9 @@ static void qemu_laio_completion_cb(void *opaque)
     }
 }
 
-static int qemu_laio_flush_cb(void *opaque)
+static int qemu_laio_flush_cb(EventNotifier *e)
 {
-    struct qemu_laio_state *s = opaque;
+    struct qemu_laio_state *s = container_of(e, struct qemu_laio_state, e);
 
     return (s->count > 0) ? 1 : 0;
 }
@@ -146,8 +134,9 @@ static void laio_cancel(BlockDriverAIOCB *blockacb)
      * We might be able to do this slightly more optimal by removing the
      * O_NONBLOCK flag.
      */
-    while (laiocb->ret == -EINPROGRESS)
-        qemu_laio_completion_cb(laiocb->ctx);
+    while (laiocb->ret == -EINPROGRESS) {
+        qemu_laio_completion_cb(&laiocb->ctx->e);
+    }
 }
 
 static AIOPool laio_pool = {
@@ -186,7 +175,7 @@ BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
                         __func__, type);
         goto out_free_aiocb;
     }
-    io_set_eventfd(&laiocb->iocb, s->efd);
+    io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e));
     s->count++;
 
     if (io_submit(s->ctx, 1, &iocbs) < 0)
@@ -205,21 +194,21 @@ void *laio_init(void)
     struct qemu_laio_state *s;
 
     s = g_malloc0(sizeof(*s));
-    s->efd = eventfd(0, 0);
-    if (s->efd == -1)
+    if (event_notifier_init(&s->e, false) < 0) {
         goto out_free_state;
-    fcntl(s->efd, F_SETFL, O_NONBLOCK);
+    }
 
-    if (io_setup(MAX_EVENTS, &s->ctx) != 0)
+    if (io_setup(MAX_EVENTS, &s->ctx) != 0) {
         goto out_close_efd;
+    }
 
-    qemu_aio_set_fd_handler(s->efd, qemu_laio_completion_cb, NULL,
-        qemu_laio_flush_cb, s);
+    qemu_aio_set_event_notifier(&s->e, qemu_laio_completion_cb,
+                                qemu_laio_flush_cb);
 
     return s;
 
 out_close_efd:
-    close(s->efd);
+    event_notifier_cleanup(&s->e);
 out_free_state:
     g_free(s);
     return NULL;
Signature · 1 line
1.7.10.4
#10 lore raw

PATCH 7/12 qemu-thread: add QemuSemaphore

In reply to Paolo Bonzini

The new thread pool will use semaphores instead of condition variables, because QemuCond does not have qemu_cond_timedwait. (I also like it more this way, since semaphores model well the producer-consumer problem).

---
 qemu-thread-posix.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-thread-posix.h |    5 ++++
 qemu-thread-win32.c |   35 ++++++++++++++++++++++++
 qemu-thread-win32.h |    4 +++
 qemu-thread.h       |    7 +++++
 5 files changed, 125 insertions(+)
qemu-thread-posix.c +74 -0
index 9e1b5fb..251fef0 100644
@@ -17,6 +17,9 @@
 #include <signal.h>
 #include <stdint.h>
 #include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/time.h>
 #include "qemu-thread.h"
 
 static void error_exit(int err, const char *msg)
@@ -115,6 +118,77 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
         error_exit(err, __func__);
 }
 
+void qemu_sem_init(QemuSemaphore *sem, int init)
+{
+    int rc;
+
+    rc = sem_init(&sem->sem, 0, init);
+    if (rc < 0) {
+        error_exit(errno, __func__);
+    }
+}
+
+void qemu_sem_destroy(QemuSemaphore *sem)
+{
+    int rc;
+
+    rc = sem_destroy(&sem->sem);
+    if (rc < 0) {
+        error_exit(errno, __func__);
+    }
+}
+
+void qemu_sem_post(QemuSemaphore *sem)
+{
+    int rc;
+
+    rc = sem_post(&sem->sem);
+    if (rc < 0) {
+        error_exit(errno, __func__);
+    }
+}
+
+int qemu_sem_timedwait(QemuSemaphore *sem, int ms)
+{
+    int rc;
+
+    if (ms <= 0) {
+        /* This is cheaper than sem_timedwait.  */
+        rc = sem_trywait(&sem->sem);
+        if (rc == -1 && errno == EAGAIN) {
+            return -1;
+        }
+    } else {
+        struct timeval tv;
+        struct timespec ts;
+        gettimeofday(&tv, NULL);
+        ts.tv_nsec = tv.tv_usec * 1000 + (ms % 1000) * 1000000;
+        ts.tv_sec = tv.tv_sec + ms / 1000;
+        if (ts.tv_nsec >= 1000000000) {
+            ts.tv_sec++;
+            ts.tv_nsec -= 1000000000;
+        }
+        rc = sem_timedwait(&sem->sem, &ts);
+        if (rc == -1 && errno == ETIMEDOUT) {
+            return -1;
+        }
+    }
+    if (rc < 0) {
+        error_exit(errno, __func__);
+    }
+    return 0;
+}
+
+void qemu_sem_wait(QemuSemaphore *sem)
+{
+    int rc;
+
+    rc = sem_wait(&sem->sem);
+    if (rc < 0) {
+        error_exit(errno, __func__);
+    }
+}
+
 void qemu_thread_create(QemuThread *thread,
                        void *(*start_routine)(void*),
                        void *arg, int mode)
qemu-thread-posix.h +5 -0
index ee4618e..2542c15 100644
@@ -1,6 +1,7 @@
 #ifndef __QEMU_THREAD_POSIX_H
 #define __QEMU_THREAD_POSIX_H 1
 #include "pthread.h"
+#include <semaphore.h>
 
 struct QemuMutex {
     pthread_mutex_t lock;
@@ -10,6 +11,10 @@ struct QemuCond {
     pthread_cond_t cond;
 };
 
+struct QemuSemaphore {
+    sem_t sem;
+};
+
 struct QemuThread {
     pthread_t thread;
 };
qemu-thread-win32.c +35 -0
index 3524c8b..78602d2 100644
@@ -192,6 +192,41 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
     qemu_mutex_lock(mutex);
 }
 
+void qemu_sem_init(QemuSemaphore *sem, int init)
+{
+    /* Manual reset.  */
+    sem->sema = CreateSemaphore(NULL, init, LONG_MAX, NULL);
+}
+
+void qemu_sem_destroy(QemuSemaphore *sem)
+{
+    CloseHandle(sem->sema);
+}
+
+void qemu_sem_post(QemuSemaphore *sem)
+{
+    ReleaseSemaphore(sem->sema, 1, NULL);
+}
+
+int qemu_sem_timedwait(QemuSemaphore *sem, int ms)
+{
+    int rc = WaitForSingleObject(sem->sema, ms);
+    if (rc == WAIT_OBJECT_0) {
+        return 0;
+    }
+    if (rc != WAIT_TIMEOUT) {
+        error_exit(GetLastError(), __func__);
+    }
+    return -1;
+}
+
+void qemu_sem_wait(QemuSemaphore *sem)
+{
+    if (WaitForSingleObject(sem->sema, INFINITE) != WAIT_OBJECT_0) {
+        error_exit(GetLastError(), __func__);
+    }
+}
+
 struct QemuThreadData {
     /* Passed to win32_start_routine.  */
     void             *(*start_routine)(void *);
qemu-thread-win32.h +4 -0
index b9d1be8..13adb95 100644
@@ -13,6 +13,10 @@ struct QemuCond {
     HANDLE continue_event;
 };
 
+struct QemuSemaphore {
+    HANDLE sema;
+};
+
 typedef struct QemuThreadData QemuThreadData;
 struct QemuThread {
     QemuThreadData *data;
qemu-thread.h +7 -0
index a78a8f2..c3f960e 100644
@@ -5,6 +5,7 @@
 
 typedef struct QemuMutex QemuMutex;
 typedef struct QemuCond QemuCond;
+typedef struct QemuSemaphore QemuSemaphore;
 typedef struct QemuThread QemuThread;
 
 #ifdef _WIN32
@@ -37,6 +38,12 @@ void qemu_cond_signal(QemuCond *cond);
 void qemu_cond_broadcast(QemuCond *cond);
 void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
 
+void qemu_sem_init(QemuSemaphore *sem, int init);
+void qemu_sem_post(QemuSemaphore *sem);
+void qemu_sem_wait(QemuSemaphore *sem);
+int qemu_sem_timedwait(QemuSemaphore *sem, int ms);
+void qemu_sem_destroy(QemuSemaphore *sem);
+
 void qemu_thread_create(QemuThread *thread,
                         void *(*start_routine)(void *),
                         void *arg, int mode);
Signature · 1 line
1.7.10.4
#11 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Quoted 2 lines · Paolo Bonzini

The new thread pool will use semaphores instead of condition variables, because QemuCond does not have qemu_cond_timedwait.

I'll post an updated patch (according to last round's review comments) that adds this service for POSIX. I bet you'll find a way to extend it to Win32 if that is required. ;)

Quoted 2 lines

(I also like it more this way, since semaphores model well the producer-consumer problem).

Let's not introduce another synchronization mechanism unless there is a real need. Semaphores tend to be misused for things they don't fit, so better keep them out of reach.

Also, if you do producer-consumer this way, you need a down() for every entity you dequeue. In contrast, you only interact with condition variables if there the consumer queue is empty - less atomic ops.

Jan

Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux
#12 lore raw

PATCH 1/1 qemu-thread: Introduce qemu_cond_timedwait for POSIX

First user will be POSIX compat aio. Windows use cases aren't in sight, so this remains a POSIX-only service for now.

This version uses CLOCK_MONOTONIC for the timeout to avoid jumps on wall clock adjustments, provided the host support pthread_condattr_setclock.

---
 configure           |   21 +++++++++++++++++++++
 qemu-thread-posix.c |   46 +++++++++++++++++++++++++++++++++++++++++++++-
 qemu-thread-posix.h |    5 +++++
 3 files changed, 71 insertions(+), 1 deletions(-)
configure +21 -0
index 0a3896e..dec39c8 100755
@@ -2135,6 +2135,23 @@ if test "$mingw32" != yes -a "$pthread" = no; then
 fi
 
 ##########################################
+# pthread_condattr_setclock probe
+condattr_setclock="no"
+if test "pthread" != "no" ; then
+  cat > $TMPC << EOF
+#include <pthread.h>
+int main(void) {
+  pthread_condattr_t attr;
+  pthread_condattr_init(&attr);
+  return pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+}
+EOF
+  if compile_prog "" "$LIBS" && $TMPE; then
+    condattr_setclock="yes"
+  fi
+fi
+
+##########################################
 # rbd probe
 if test "$rbd" != "no" ; then
   cat > $TMPC <<EOF
@@ -3040,6 +3057,7 @@ echo "preadv support    $preadv"
 echo "fdatasync         $fdatasync"
 echo "madvise           $madvise"
 echo "posix_madvise     $posix_madvise"
+echo "condattr_setclock $condattr_setclock"
 echo "uuid support      $uuid"
 echo "libcap-ng support $cap_ng"
 echo "vhost-net support $vhost_net"
@@ -3324,6 +3342,9 @@ fi
 if test "$posix_madvise" = "yes" ; then
   echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
 fi
+if test "$condattr_setclock" = "yes" ; then
+  echo "CONFIG_CONDATTR_SETCLOCK=y" >> $config_host_mak
+fi
 
 if test "$spice" = "yes" ; then
   echo "CONFIG_SPICE=y" >> $config_host_mak
qemu-thread-posix.c +45 -1

65 lines not on this page · raw message

qemu-thread-posix.h +5 -0
index ee4618e..9f00524 100644
@@ -1,5 +1,6 @@
 #ifndef __QEMU_THREAD_POSIX_H
 #define __QEMU_THREAD_POSIX_H 1
+#include <stdbool.h>
 #include "pthread.h"
 
 struct QemuMutex {
@@ -14,4 +15,8 @@ struct QemuThread {
     pthread_t thread;
 };
 
+/* only provided for posix so far */
+bool qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex,
+                         unsigned int timeout_ms);
+
 #endif
Signature · 1 line
1.7.3.4
#13 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

In reply to Jan Kiszka

Il 16/07/2012 14:00, Jan Kiszka ha scritto:

Quoted 7 lines · Paolo Bonzini
Quoted 2 lines

The new thread pool will use semaphores instead of condition variables, because QemuCond does not have qemu_cond_timedwait.

I'll post an updated patch (according to last round's review comments) that adds this service for POSIX. I bet you'll find a way to extend it to Win32 if that is required. ;)

I can do that (or just use pthreads-win32), but only at the cost of making cond_wait() slower and more complex.

Quoted 6 lines
Quoted 2 lines

(I also like it more this way, since semaphores model well the producer-consumer problem).

Let's not introduce another synchronization mechanism unless there is a real need. Semaphores tend to be misused for things they don't fit, so better keep them out of reach.

That's what patch review is for...

Quoted 3 lines

Also, if you do producer-consumer this way, you need a down() for every entity you dequeue. In contrast, you only interact with condition variables if there the consumer queue is empty - less atomic ops.

It doesn't really matter. You want the thread pool to service requests as fast as possible, which means you'll have always at least one free thread waiting on the semaphore or cv. So, with either semaphores or cvs, the slow path is actually the normal case.

Paolo

#14 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Quoted 11 lines · Paolo Bonzini

Il 16/07/2012 14:00, Jan Kiszka ha scritto:

Quoted 7 lines · Paolo Bonzini
Quoted 2 lines

The new thread pool will use semaphores instead of condition variables, because QemuCond does not have qemu_cond_timedwait.

I'll post an updated patch (according to last round's review comments) that adds this service for POSIX. I bet you'll find a way to extend it to Win32 if that is required. ;)

I can do that (or just use pthreads-win32), but only at the cost of making cond_wait() slower and more complex.

Why will it affect cond_wait? WaitForSingleObject can time out as well.

Quoted 9 lines
Quoted 7 lines
Quoted 3 lines

(I also like it more this way, since semaphores model well the producer-consumer problem).

Let's not introduce another synchronization mechanism unless there is a real need. Semaphores tend to be misused for things they don't fit, so better keep them out of reach.

That's what patch review is for...

But even better is avoiding the temptation.

Jan

Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux
#15 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Il 16/07/2012 15:34, Jan Kiszka ha scritto:

Quoted 6 lines · Paolo Bonzini
Quoted 5 lines

Il 16/07/2012 14:00, Jan Kiszka ha scritto:

Quoted 4 lines · Paolo Bonzini
Quoted 2 lines

The new thread pool will use semaphores instead of condition variables, because QemuCond does not have qemu_cond_timedwait.

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

I'll post an updated patch (according to last round's review comments) that adds this service for POSIX. I bet you'll find a way to extend it to Win32 if that is required. ;)

I can do that (or just use pthreads-win32), but only at the cost of making cond_wait() slower and more complex.

Why will it affect cond_wait? WaitForSingleObject can time out as well.

qemu_cond_wait only uses WaitForSingleObject with INFINITE timeout, and the algorithm relies on that.

Paolo

#16 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Quoted 11 lines · Paolo Bonzini

Il 16/07/2012 15:34, Jan Kiszka ha scritto:

Quoted 10 lines · Paolo Bonzini
Quoted 9 lines

Il 16/07/2012 14:00, Jan Kiszka ha scritto:

Quoted 7 lines · Paolo Bonzini
Quoted 2 lines

The new thread pool will use semaphores instead of condition variables, because QemuCond does not have qemu_cond_timedwait.

I'll post an updated patch (according to last round's review comments) that adds this service for POSIX. I bet you'll find a way to extend it to Win32 if that is required. ;)

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

I can do that (or just use pthreads-win32), but only at the cost of making cond_wait() slower and more complex.

Why will it affect cond_wait? WaitForSingleObject can time out as well.

qemu_cond_wait only uses WaitForSingleObject with INFINITE timeout, and the algorithm relies on that.

I see. But this doesn't look complex awfully. Just move the waker signaling from within cond_wait under the mutex as well, maybe add another flag if there is really someone waiting, and that's it. The costs should be hard to measure, even in line of code.

Jan

Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux
#17 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Il 16/07/2012 15:53, Jan Kiszka ha scritto:

Quoted 7 lines
Quoted 3 lines
Quoted 3 lines

qemu_cond_wait only uses WaitForSingleObject with INFINITE timeout, and the algorithm relies on that.

I see. But this doesn't look complex awfully. Just move the waker signaling from within cond_wait under the mutex as well, maybe add another flag if there is really someone waiting, and that's it. The costs should be hard to measure, even in line of code.

There is still a race after WaitForSingleObject times out. You need to catch the mutex before decreasing the number of waiters, and during that window somebody can broadcast the condition variable.

I'm not saying it's impossible, just that it's hard and I dislike qemu_cond_timedwait as much as you dislike semaphores. :)

Paolo

#18 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Quoted 4 lines · Paolo Bonzini

Il 16/07/2012 15:53, Jan Kiszka ha scritto:

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

qemu_cond_wait only uses WaitForSingleObject with INFINITE timeout, and the algorithm relies on that.

Quoted 8 lines
Quoted 4 lines

I see. But this doesn't look complex awfully. Just move the waker signaling from within cond_wait under the mutex as well, maybe add another flag if there is really someone waiting, and that's it. The costs should be hard to measure, even in line of code.

There is still a race after WaitForSingleObject times out. You need to catch the mutex before decreasing the number of waiters, and during that window somebody can broadcast the condition variable.

...and that's why you check what needs to be done to handle this race after grabbing the mutex. IOW, replicate the state information that the Windows semaphore contains into the emulated condition variable object.

Quoted 3 lines

I'm not saying it's impossible, just that it's hard and I dislike qemu_cond_timedwait as much as you dislike semaphores. :)

As you cannot eliminate our use cases for condition variables, let's focus on the existing synchronization patterns instead of introducing new ones + new mechanisms.

Jan

Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SDP-DE
Corporate Competence Center Embedded Linux
#19 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Il 16/07/2012 16:09, Jan Kiszka ha scritto:

Quoted 10 lines · Paolo Bonzini
Quoted 9 lines

Il 16/07/2012 15:53, Jan Kiszka ha scritto:

Quoted 7 lines
Quoted 3 lines
Quoted 3 lines

qemu_cond_wait only uses WaitForSingleObject with INFINITE timeout, and the algorithm relies on that.

I see. But this doesn't look complex awfully. Just move the waker signaling from within cond_wait under the mutex as well, maybe add another flag if there is really someone waiting, and that's it. The costs should be hard to measure, even in line of code.

Quoted 7 lines
Quoted 3 lines

There is still a race after WaitForSingleObject times out. You need to catch the mutex before decreasing the number of waiters, and during that window somebody can broadcast the condition variable.

...and that's why you check what needs to be done to handle this race after grabbing the mutex. IOW, replicate the state information that the Windows semaphore contains into the emulated condition variable object.

It is already there (cv->waiters), but it is accessed atomically. To do what you suggest I would need to add a mutex.

Quoted 6 lines
Quoted 2 lines

I'm not saying it's impossible, just that it's hard and I dislike qemu_cond_timedwait as much as you dislike semaphores. :)

As you cannot eliminate our use cases for condition variables, let's focus on the existing synchronization patterns instead of introducing new ones + new mechanisms.

I'm not introducing <fancy new primitive of the day>... semaphores have the advantage of having better support under Windows (at least older versions; Microsoft saw the light starting with Vista---yes I know the oxymoron---and added condvars).

Paolo

#20 lore raw

Re: [Qemu-devel] [PATCH 07/12] qemu-thread: add QemuSemaphore

Il 16/07/2012 16:20, Paolo Bonzini ha scritto:

Quoted 5 lines
Quoted 3 lines
Quoted 3 lines

...and that's why you check what needs to be done to handle this race after grabbing the mutex. IOW, replicate the state information that the Windows semaphore contains into the emulated condition variable object.

It is already there (cv->waiters), but it is accessed atomically. To do what you suggest I would need to add a mutex.

FWIW, I found a good condvar implementation in Chromium, but I really don't have the time to port it over to QEMU right now. I still would like to get the semaphore version in 1.2.

Also, the attached pseudo-patch is an example of using semaphores to limit the size of the critical sections, and also decrease the number of threads created. I'm not proposing to include it now, it's just an example of things that are harder with condition variables than with semaphores.

Paolo

thread-pool.c +8 -12
index 7895544..72be971 100644
@@ -71,20 +72,16 @@ static void *worker_thread(void *unused)
         ThreadPoolElement *req;
         int ret;
 
-        qemu_mutex_lock(&lock);
-        idle_threads++;
-        qemu_mutex_unlock(&lock);
-        ret = qemu_sem_timedwait(&sem, 10000);
-        qemu_mutex_lock(&lock);
-        idle_threads--;
+        atomic_inc(&idle_threads);
+        do {
+            ret = qemu_sem_timedwait(&sem, 10000);
+        } while (ret == -1 && atomic_read(&QTAILQ_FIRST(&request_list)) != NULL);
+        atomic_dec(&idle_threads);
         if (ret == -1) {
-            if (QTAILQ_EMPTY(&request_list)) {
-                break;
-            }
-            qemu_mutex_unlock(&lock);
-            continue;
+            break;
         }
 
+        qemu_mutex_lock(&lock);
         req = QTAILQ_FIRST(&request_list);
         QTAILQ_REMOVE(&request_list, req, reqs);
         req->state = THREAD_ACTIVE;
@@ -226,7 +223,7 @@ BlockDriverAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg,
     trace_thread_pool_submit(req, arg);
 
     qemu_mutex_lock(&lock);
-    if (idle_threads == 0 && cur_threads < max_threads) {
+    if (atomic_read(&idle_threads) == 0 && cur_threads < max_threads) {
         spawn_thread();
     }
     QTAILQ_INSERT_TAIL(&request_list, req, reqs);
#21 lore raw

PATCH 8/12 aio: add generic thread-pool facility

In reply to Paolo Bonzini

Add a generic thread-pool. The code is roughly based on posix-aio-compat.c, with some changes, especially the following:

- use QemuSemaphore instead of QemuCond;

- separate the state of the thread from the return code of the worker function. The return code is totally opaque for the thread pool;

- do not busy wait when doing cancellation.

A more generic threadpool (but still specific to I/O so that in the future it can use special scheduling classes or PI mutexes) can have many uses:
it allows more flexibility in raw-posix.c and can more easily be extended to Win32, and it will also be used to do an msync of the persistent bitmap.

---
 Makefile.objs |    2 +-
 thread-pool.c |  279 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 thread-pool.h |   34 +++++++
 trace-events  |    5 ++
 4 files changed, 319 insertions(+), 1 deletion(-)
 create mode 100644 thread-pool.c
 create mode 100644 thread-pool.h
Makefile.objs +1 -1
index 96d0e68..9a30cb5 100644
@@ -41,7 +41,7 @@ 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 = cutils.o cache-utils.o qemu-option.o module.o async.o thread-pool.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
thread-pool.c new file +279 -0

279 lines not on this page · raw message

thread-pool.h new file +34 -0
new file mode 100644
index 0000000..378a4ac
@@ -0,0 +1,34 @@
+/*
+ * QEMU block layer thread pool
+ *
+ * Copyright IBM, Corp. 2008
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <[email protected]>
+ *  Paolo Bonzini     <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef QEMU_THREAD_POOL_H
+#define QEMU_THREAD_POOL_H 1
+
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "qemu-thread.h"
+#include "qemu-coroutine.h"
+#include "block_int.h"
+
+typedef int ThreadPoolFunc(void *opaque);
+
+BlockDriverAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg,
+     BlockDriverCompletionFunc *cb, void *opaque);
+int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg);
+void thread_pool_submit(ThreadPoolFunc *func, void *arg);
+
+#endif
trace-events +5 -0
index c935ba2..42f6592 100644
@@ -86,6 +86,11 @@ virtio_blk_rw_complete(void *req, int ret) "req %p ret %d"
 virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu"
 virtio_blk_handle_read(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu"
 
+# thread-pool.c
+thread_pool_submit(void *req, void *opaque) "req %p opaque %p"
+thread_pool_complete(void *req, void *opaque, int ret) "req %p opaque %p ret %d"
+thread_pool_cancel(void *req, void *opaque) "req %p opaque %p"
+
 # posix-aio-compat.c
 paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d"
 paio_complete(void *acb, void *opaque, int ret) "acb %p opaque %p ret %d"
Signature · 1 line
1.7.10.4
#22 lore raw

PATCH 9/12 block: switch posix-aio-compat to threadpool

In reply to Paolo Bonzini

This is not meant for portability, but to remove code duplication.

---
 block/raw-posix-aio.h |    1 -
 block/raw-posix.c     |    5 -
 posix-aio-compat.c    |  433 +++++--------------------------------------------
 3 files changed, 42 insertions(+), 397 deletions(-)
block/raw-posix-aio.h +0 -1
index ba118f6..6725135 100644
@@ -28,7 +28,6 @@
 
 
 /* posix-aio-compat.c - thread pool based implementation */
-int paio_init(void);
 BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
         BlockDriverCompletionFunc *cb, void *opaque, int type);
block/raw-posix.c +0 -5
index 0dce089..e5faccd 100644
@@ -234,11 +234,6 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
         }
     }
 
-    /* We're falling back to POSIX AIO in some cases so init always */
-    if (paio_init() < 0) {
-        goto out_free_buf;
-    }
-
 #ifdef CONFIG_LINUX_AIO
     /*
      * Currently Linux do AIO only for files opened with O_DIRECT
posix-aio-compat.c +42 -391

487 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#23 lore raw

PATCH 10/12 raw: merge posix-aio-compat.c into block/raw-posix.c

In reply to Paolo Bonzini

Making the qemu_paiocb specific to raw devices will let us access members of the BDRVRawState arbitrarily.

---
 Makefile.objs         |    1 -
 block/raw-posix-aio.h |    8 --
 block/raw-posix.c     |  294 +++++++++++++++++++++++++++++++++++++++++++
 posix-aio-compat.c    |  332 -------------------------------------------------
 4 files changed, 294 insertions(+), 341 deletions(-)
 delete mode 100644 posix-aio-compat.c
Makefile.objs +0 -1
index 9a30cb5..3debcba 100644
@@ -44,7 +44,6 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
 block-obj-y = cutils.o cache-utils.o qemu-option.o module.o async.o thread-pool.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 aio-posix.o
 block-obj-$(CONFIG_WIN32) += event_notifier-win32.o aio-win32.o
 block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block/raw-posix-aio.h +0 -8
index 6725135..c714367 100644
@@ -27,14 +27,6 @@
 #define QEMU_AIO_MISALIGNED   0x1000
 
 
-/* posix-aio-compat.c - thread pool based implementation */
-BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
-        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-        BlockDriverCompletionFunc *cb, void *opaque, int type);
-BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
-        unsigned long int req, void *buf,
-        BlockDriverCompletionFunc *cb, void *opaque);
-
 /* linux-aio.c - Linux native implementation */
 void *laio_init(void);
 BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
block/raw-posix.c +294 -0

312 lines not on this page · raw message

posix-aio-compat.c deleted +0 -332

332 lines not on this page · raw message

Signature · 1 line
1.7.10.4
#24 lore raw

PATCH 11/12 raw-posix: rename raw-posix-aio.h, hide unavailable prototypes

In reply to Paolo Bonzini
---
 block/{raw-posix-aio.h => raw-aio.h} |   10 ++++++----
 block/raw-posix.c                    |    2 +-
 linux-aio.c                          |    2 +-
 3 files changed, 8 insertions(+), 6 deletions(-)
 rename block/{raw-posix-aio.h => raw-aio.h} (86%)
block/raw-aio.h renamed from block/raw-posix-aio.h +6 -4
similarity index 86%
rename from block/raw-posix-aio.h
rename to block/raw-aio.h
index c714367..b3bb073 100644
@@ -1,5 +1,5 @@
 /*
- * QEMU Posix block I/O backend AIO support
+ * Declarations for AIO in the raw protocol
  *
  * Copyright IBM, Corp. 2008
  *
@@ -12,8 +12,8 @@
  * Contributions after 2012-01-13 are licensed under the terms of the
  * GNU GPL, version 2 or (at your option) any later version.
  */
-#ifndef QEMU_RAW_POSIX_AIO_H
-#define QEMU_RAW_POSIX_AIO_H
+#ifndef QEMU_RAW_AIO_H
+#define QEMU_RAW_AIO_H
 
 /* AIO request types */
 #define QEMU_AIO_READ         0x0001
@@ -28,9 +28,11 @@
 
 
 /* linux-aio.c - Linux native implementation */
+#ifdef CONFIG_LINUX_AIO
 void *laio_init(void);
 BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
         BlockDriverCompletionFunc *cb, void *opaque, int type);
+#endif
 
-#endif /* QEMU_RAW_POSIX_AIO_H */
+#endif /* QEMU_RAW_AIO_H */
block/raw-posix.c +1 -1
index 3e2e822..64296a6 100644
@@ -29,7 +29,7 @@
 #include "module.h"
 #include "trace.h"
 #include "thread-pool.h"
-#include "block/raw-posix-aio.h"
+#include "block/raw-aio.h"
 
 #if defined(__APPLE__) && (__MACH__)
 #include <paths.h>
linux-aio.c +1 -1
index 779f793..67c49af 100644
@@ -9,7 +9,7 @@
  */
 #include "qemu-common.h"
 #include "qemu-aio.h"
-#include "block/raw-posix-aio.h"
+#include "block/raw-aio.h"
 #include "event_notifier.h"
 
 #include <libaio.h>
Signature · 1 line
1.7.10.4
#25 lore raw

PATCH 12/12 raw-win32: add emulated AIO support

In reply to Paolo Bonzini

The thread pool can be used under Win32 in the same way as in raw-posix.c. Move the existing synchronous code into callbacks, and pass the return code back.

---
 block/raw-win32.c |  189 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 140 insertions(+), 49 deletions(-)
block/raw-win32.c +140 -49
index e4b0b75..a50d636 100644
@@ -25,6 +25,9 @@
 #include "qemu-timer.h"
 #include "block_int.h"
 #include "module.h"
+#include "raw-aio.h"
+#include "trace.h"
+#include "thread-pool.h"
 #include <windows.h>
 #include <winioctl.h>
 
@@ -32,12 +35,130 @@
 #define FTYPE_CD     1
 #define FTYPE_HARDDISK 2
 
+struct qemu_paiocb {
+    BlockDriverState *bs;
+    HANDLE hfile;
+    struct iovec *aio_iov;
+    int aio_niov;
+    size_t aio_nbytes;
+    off_t aio_offset;
+    int aio_type;
+};
+
 typedef struct BDRVRawState {
     HANDLE hfile;
     int type;
     char drive_path[16]; /* format: "d:\" */
 } BDRVRawState;
 
+/*
+ * Read/writes the data to/from a given linear buffer.
+ *
+ * Returns the number of bytes handles or -errno in case of an error. Short
+ * reads are only returned if the end of the file is reached.
+ */
+static size_t handle_aiocb_rw(struct qemu_paiocb *aiocb)
+{
+    size_t offset = 0;
+    int i;
+
+    for (i = 0; i < aiocb->aio_niov; i++) {
+        OVERLAPPED ov;
+        DWORD ret, ret_count, len;
+
+        memset(&ov, 0, sizeof(ov));
+        ov.Offset = (aiocb->aio_offset + offset);
+        ov.OffsetHigh = (aiocb->aio_offset + offset) >> 32;
+        len = aiocb->aio_iov[i].iov_len;
+        if (aiocb->aio_type & QEMU_AIO_WRITE) {
+            ret = WriteFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
+                            len, &ret_count, &ov);
+        } else {
+            ret = ReadFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
+                           len, &ret_count, &ov);
+        }
+        if (!ret) {
+            ret_count = 0;
+        }
+        if (ret_count != len) {
+            break;
+        }
+        offset += len;
+    }
+
+    return offset;
+}
+
+static int aio_worker(void *arg)
+{
+    struct qemu_paiocb *aiocb = arg;
+    ssize_t ret = 0;
+    size_t count;
+
+    switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
+    case QEMU_AIO_READ:
+        count = handle_aiocb_rw(aiocb);
+        if (count < aiocb->aio_nbytes && aiocb->bs->growable) {
+            /* A short read means that we have reached EOF. Pad the buffer
+             * with zeros for bytes after EOF. */
+            QEMUIOVector qiov;
+
+            qemu_iovec_init_external(&qiov, aiocb->aio_iov,
+                                     aiocb->aio_niov);
+            qemu_iovec_memset_skip(&qiov, 0, aiocb->aio_nbytes - count, count);
+
+            count = aiocb->aio_nbytes;
+        }
+        if (count == aiocb->aio_nbytes) {
+            ret = 0;
+        } else {
+            ret = -EINVAL;
+        }
+        break;
+    case QEMU_AIO_WRITE:
+        count = handle_aiocb_rw(aiocb);
+        if (count == aiocb->aio_nbytes) {
+            count = 0;
+        } else {
+            count = -EINVAL;
+        }
+        break;
+    case QEMU_AIO_FLUSH:
+        if (!FlushFileBuffers(aiocb->hfile)) {
+            return -EIO;
+        }
+        break;
+    default:
+        fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
+        ret = -EINVAL;
+        break;
+    }
+
+    g_slice_free(struct qemu_paiocb, aiocb);
+    return ret;
+}
+
+static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
+        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque, int type)
+{
+    struct qemu_paiocb *acb = g_slice_new(struct qemu_paiocb);
+
+    acb->bs = bs;
+    acb->hfile = hfile;
+    acb->aio_type = type;
+
+    if (qiov) {
+        acb->aio_iov = qiov->iov;
+        acb->aio_niov = qiov->niov;
+    }
+    acb->aio_nbytes = nb_sectors * 512;
+    acb->aio_offset = sector_num * 512;
+
+    trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
+    return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
+}
+
 int qemu_ftruncate64(int fd, int64_t length)
 {
     LARGE_INTEGER li;
@@ -109,59 +230,29 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
     return 0;
 }
 
-static int raw_read(BlockDriverState *bs, int64_t sector_num,
-                    uint8_t *buf, int nb_sectors)
+static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
+                         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+                         BlockDriverCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
-    OVERLAPPED ov;
-    DWORD ret_count;
-    int ret;
-    int64_t offset = sector_num * 512;
-    int count = nb_sectors * 512;
-
-    memset(&ov, 0, sizeof(ov));
-    ov.Offset = offset;
-    ov.OffsetHigh = offset >> 32;
-    ret = ReadFile(s->hfile, buf, count, &ret_count, &ov);
-    if (!ret)
-        return ret_count;
-    if (ret_count == count)
-        ret_count = 0;
-    return ret_count;
+    return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
+                       cb, opaque, QEMU_AIO_READ);
 }
 
-static int raw_write(BlockDriverState *bs, int64_t sector_num,
-                     const uint8_t *buf, int nb_sectors)
+static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
+                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+                          BlockDriverCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
-    OVERLAPPED ov;
-    DWORD ret_count;
-    int ret;
-    int64_t offset = sector_num * 512;
-    int count = nb_sectors * 512;
-
-    memset(&ov, 0, sizeof(ov));
-    ov.Offset = offset;
-    ov.OffsetHigh = offset >> 32;
-    ret = WriteFile(s->hfile, buf, count, &ret_count, &ov);
-    if (!ret)
-        return ret_count;
-    if (ret_count == count)
-        ret_count = 0;
-    return ret_count;
+    return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
+                       cb, opaque, QEMU_AIO_WRITE);
 }
 
-static int raw_flush(BlockDriverState *bs)
+static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
+                         BlockDriverCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
-    int ret;
-
-    ret = FlushFileBuffers(s->hfile);
-    if (ret == 0) {
-        return -EIO;
-    }
-
-    return 0;
+    return paio_submit(bs, s->hfile, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
 }
 
 static void raw_close(BlockDriverState *bs)
@@ -282,9 +373,9 @@ static BlockDriver bdrv_file = {
     .bdrv_close		= raw_close,
     .bdrv_create	= raw_create,
 
-    .bdrv_read              = raw_read,
-    .bdrv_write             = raw_write,
-    .bdrv_co_flush_to_disk  = raw_flush,
+    .bdrv_aio_readv     = raw_aio_readv,
+    .bdrv_aio_writev    = raw_aio_writev,
+    .bdrv_aio_flush     = raw_aio_flush,
 
     .bdrv_truncate	= raw_truncate,
     .bdrv_getlength	= raw_getlength,
@@ -413,9 +504,9 @@ static BlockDriver bdrv_host_device = {
     .bdrv_close		= raw_close,
     .bdrv_has_zero_init = hdev_has_zero_init,
 
-    .bdrv_read              = raw_read,
-    .bdrv_write             = raw_write,
-    .bdrv_co_flush_to_disk  = raw_flush,
+    .bdrv_aio_readv     = raw_aio_readv,
+    .bdrv_aio_writev    = raw_aio_writev,
+    .bdrv_aio_flush     = raw_aio_flush,
 
     .bdrv_getlength	= raw_getlength,
     .bdrv_get_allocated_file_size
Signature · 1 line
1.7.10.4
#26 lore raw

Re: [Qemu-devel] [PATCH 12/12] raw-win32: add emulated AIO support

Quoted 24 lines · Paolo Bonzini

The thread pool can be used under Win32 in the same way as in raw-posix.c. Move the existing synchronous code into callbacks, and pass the return code back.

---
 block/raw-win32.c |  189 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 140 insertions(+), 49 deletions(-)
block/raw-win32.c +3 -0
index e4b0b75..a50d636 100644
@@ -25,6 +25,9 @@
 #include "qemu-timer.h"
 #include "block_int.h"
 #include "module.h"
+#include "raw-aio.h"
+#include "trace.h"
+#include "thread-pool.h"
 #include <windows.h>
 #include <winioctl.h>
 

block/raw-win32.c line 38

Quoted 4 lines
block/raw-win32.c +1 -0
@@ -32,12 +35,130 @@
 #define FTYPE_CD     1
 #define FTYPE_HARDDISK 2
 
+struct qemu_paiocb {

QEMUPAIOCB

Quoted 84 lines
block/raw-win32.c +78 -0
+    BlockDriverState *bs;
+    HANDLE hfile;
+    struct iovec *aio_iov;
+    int aio_niov;
+    size_t aio_nbytes;
+    off_t aio_offset;
+    int aio_type;
+};
+
 typedef struct BDRVRawState {
     HANDLE hfile;
     int type;
     char drive_path[16]; /* format: "d:\" */
 } BDRVRawState;
 
+/*
+ * Read/writes the data to/from a given linear buffer.
+ *
+ * Returns the number of bytes handles or -errno in case of an error. Short
+ * reads are only returned if the end of the file is reached.
+ */
+static size_t handle_aiocb_rw(struct qemu_paiocb *aiocb)
+{
+    size_t offset = 0;
+    int i;
+
+    for (i = 0; i < aiocb->aio_niov; i++) {
+        OVERLAPPED ov;
+        DWORD ret, ret_count, len;
+
+        memset(&ov, 0, sizeof(ov));
+        ov.Offset = (aiocb->aio_offset + offset);
+        ov.OffsetHigh = (aiocb->aio_offset + offset) >> 32;
+        len = aiocb->aio_iov[i].iov_len;
+        if (aiocb->aio_type & QEMU_AIO_WRITE) {
+            ret = WriteFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
+                            len, &ret_count, &ov);
+        } else {
+            ret = ReadFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
+                           len, &ret_count, &ov);
+        }
+        if (!ret) {
+            ret_count = 0;
+        }
+        if (ret_count != len) {
+            break;
+        }
+        offset += len;
+    }
+
+    return offset;
+}
+
+static int aio_worker(void *arg)
+{
+    struct qemu_paiocb *aiocb = arg;
+    ssize_t ret = 0;
+    size_t count;
+
+    switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
+    case QEMU_AIO_READ:
+        count = handle_aiocb_rw(aiocb);
+        if (count < aiocb->aio_nbytes && aiocb->bs->growable) {
+            /* A short read means that we have reached EOF. Pad the buffer
+             * with zeros for bytes after EOF. */
+            QEMUIOVector qiov;
+
+            qemu_iovec_init_external(&qiov, aiocb->aio_iov,
+                                     aiocb->aio_niov);
+            qemu_iovec_memset_skip(&qiov, 0, aiocb->aio_nbytes - count, count);
+
+            count = aiocb->aio_nbytes;
+        }
+        if (count == aiocb->aio_nbytes) {
+            ret = 0;
+        } else {
+            ret = -EINVAL;
+        }
+        break;
+    case QEMU_AIO_WRITE:
+        count = handle_aiocb_rw(aiocb);
+        if (count == aiocb->aio_nbytes) {
+            count = 0;
+        } else {

block/raw-win32.c line 132

Quoted 10 lines
block/raw-win32.c +10 -0
+            count = -EINVAL;
+        }
+        break;
+    case QEMU_AIO_FLUSH:
+        if (!FlushFileBuffers(aiocb->hfile)) {
+            return -EIO;
+        }
+        break;
+    default:
+        fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);

Assert instead?

Quoted 135 lines
block/raw-win32.c +48 -50
+        ret = -EINVAL;
+        break;
+    }
+
+    g_slice_free(struct qemu_paiocb, aiocb);
+    return ret;
+}
+
+static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
+        int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque, int type)
+{
+    struct qemu_paiocb *acb = g_slice_new(struct qemu_paiocb);
+
+    acb->bs = bs;
+    acb->hfile = hfile;
+    acb->aio_type = type;
+
+    if (qiov) {
+        acb->aio_iov = qiov->iov;
+        acb->aio_niov = qiov->niov;
+    }
+    acb->aio_nbytes = nb_sectors * 512;
+    acb->aio_offset = sector_num * 512;
+
+    trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
+    return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
+}
+
 int qemu_ftruncate64(int fd, int64_t length)
 {
     LARGE_INTEGER li;
@@ -109,59 +230,29 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
     return 0;
 }
 
-static int raw_read(BlockDriverState *bs, int64_t sector_num,
-                    uint8_t *buf, int nb_sectors)
+static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
+                         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+                         BlockDriverCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
-    OVERLAPPED ov;
-    DWORD ret_count;
-    int ret;
-    int64_t offset = sector_num * 512;
-    int count = nb_sectors * 512;
-
-    memset(&ov, 0, sizeof(ov));
-    ov.Offset = offset;
-    ov.OffsetHigh = offset >> 32;
-    ret = ReadFile(s->hfile, buf, count, &ret_count, &ov);
-    if (!ret)
-        return ret_count;
-    if (ret_count == count)
-        ret_count = 0;
-    return ret_count;
+    return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
+                       cb, opaque, QEMU_AIO_READ);
 }
 
-static int raw_write(BlockDriverState *bs, int64_t sector_num,
-                     const uint8_t *buf, int nb_sectors)
+static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
+                          int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+                          BlockDriverCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
-    OVERLAPPED ov;
-    DWORD ret_count;
-    int ret;
-    int64_t offset = sector_num * 512;
-    int count = nb_sectors * 512;
-
-    memset(&ov, 0, sizeof(ov));
-    ov.Offset = offset;
-    ov.OffsetHigh = offset >> 32;
-    ret = WriteFile(s->hfile, buf, count, &ret_count, &ov);
-    if (!ret)
-        return ret_count;
-    if (ret_count == count)
-        ret_count = 0;
-    return ret_count;
+    return paio_submit(bs, s->hfile, sector_num, qiov, nb_sectors,
+                       cb, opaque, QEMU_AIO_WRITE);
 }
 
-static int raw_flush(BlockDriverState *bs)
+static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
+                         BlockDriverCompletionFunc *cb, void *opaque)
 {
     BDRVRawState *s = bs->opaque;
-    int ret;
-
-    ret = FlushFileBuffers(s->hfile);
-    if (ret == 0) {
-        return -EIO;
-    }
-
-    return 0;
+    return paio_submit(bs, s->hfile, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
 }
 
 static void raw_close(BlockDriverState *bs)
@@ -282,9 +373,9 @@ static BlockDriver bdrv_file = {
     .bdrv_close                = raw_close,
     .bdrv_create       = raw_create,
 
-    .bdrv_read              = raw_read,
-    .bdrv_write             = raw_write,
-    .bdrv_co_flush_to_disk  = raw_flush,
+    .bdrv_aio_readv     = raw_aio_readv,
+    .bdrv_aio_writev    = raw_aio_writev,
+    .bdrv_aio_flush     = raw_aio_flush,
 
     .bdrv_truncate     = raw_truncate,
     .bdrv_getlength    = raw_getlength,
@@ -413,9 +504,9 @@ static BlockDriver bdrv_host_device = {
     .bdrv_close                = raw_close,
     .bdrv_has_zero_init = hdev_has_zero_init,
 
-    .bdrv_read              = raw_read,
-    .bdrv_write             = raw_write,
-    .bdrv_co_flush_to_disk  = raw_flush,
+    .bdrv_aio_readv     = raw_aio_readv,
+    .bdrv_aio_writev    = raw_aio_writev,
+    .bdrv_aio_flush     = raw_aio_flush,
 
     .bdrv_getlength    = raw_getlength,
     .bdrv_get_allocated_file_size
--

1.7.10.4

#27 lore raw

Re: [Qemu-devel] [PATCH 12/12] raw-win32: add emulated AIO support

Il 23/07/2012 18:35, Blue Swirl ha scritto:

Quoted 2 lines
Quoted 1 line
Quoted 1 line

+struct qemu_paiocb {

QEMUPAIOCB

RawWin32AIOData. :)

Quoted 87 lines
Quoted 87 lines
Quoted 87 lines
block/raw-win32.c +81 -0
+    BlockDriverState *bs;
+    HANDLE hfile;
+    struct iovec *aio_iov;
+    int aio_niov;
+    size_t aio_nbytes;
+    off_t aio_offset;
+    int aio_type;
+};
+
 typedef struct BDRVRawState {
     HANDLE hfile;
     int type;
     char drive_path[16]; /* format: "d:\" */
 } BDRVRawState;
 
+/*
+ * Read/writes the data to/from a given linear buffer.
+ *
+ * Returns the number of bytes handles or -errno in case of an error. Short
+ * reads are only returned if the end of the file is reached.
+ */
+static size_t handle_aiocb_rw(struct qemu_paiocb *aiocb)
+{
+    size_t offset = 0;
+    int i;
+
+    for (i = 0; i < aiocb->aio_niov; i++) {
+        OVERLAPPED ov;
+        DWORD ret, ret_count, len;
+
+        memset(&ov, 0, sizeof(ov));
+        ov.Offset = (aiocb->aio_offset + offset);
+        ov.OffsetHigh = (aiocb->aio_offset + offset) >> 32;
+        len = aiocb->aio_iov[i].iov_len;
+        if (aiocb->aio_type & QEMU_AIO_WRITE) {
+            ret = WriteFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
+                            len, &ret_count, &ov);
+        } else {
+            ret = ReadFile(aiocb->hfile, aiocb->aio_iov[i].iov_base,
+                           len, &ret_count, &ov);
+        }
+        if (!ret) {
+            ret_count = 0;
+        }
+        if (ret_count != len) {
+            break;
+        }
+        offset += len;
+    }
+
+    return offset;
+}
+
+static int aio_worker(void *arg)
+{
+    struct qemu_paiocb *aiocb = arg;
+    ssize_t ret = 0;
+    size_t count;
+
+    switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
+    case QEMU_AIO_READ:
+        count = handle_aiocb_rw(aiocb);
+        if (count < aiocb->aio_nbytes && aiocb->bs->growable) {
+            /* A short read means that we have reached EOF. Pad the buffer
+             * with zeros for bytes after EOF. */
+            QEMUIOVector qiov;
+
+            qemu_iovec_init_external(&qiov, aiocb->aio_iov,
+                                     aiocb->aio_niov);
+            qemu_iovec_memset_skip(&qiov, 0, aiocb->aio_nbytes - count, count);
+
+            count = aiocb->aio_nbytes;
+        }
+        if (count == aiocb->aio_nbytes) {
+            ret = 0;
+        } else {
+            ret = -EINVAL;
+        }
+        break;
+    case QEMU_AIO_WRITE:
+        count = handle_aiocb_rw(aiocb);
+        if (count == aiocb->aio_nbytes) {
+            count = 0;
+        } else {
+            count = -EINVAL;
+        }
+        break;
Quoted 8 lines
Quoted 7 lines

block/raw-win32.c line 132

Quoted 7 lines
block/raw-win32.c +7 -0
+    case QEMU_AIO_FLUSH:
+        if (!FlushFileBuffers(aiocb->hfile)) {
+            return -EIO;
+        }
+        break;
+    default:
+        fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);

Assert instead?

Yeah, this is cut-and-pasted from posix-aio-compat.c, I'll fix both.

Paolo