qemu/aio-win32.c
<<
>>
Prefs
   1/*
   2 * QEMU aio implementation
   3 *
   4 * Copyright IBM Corp., 2008
   5 * Copyright Red Hat Inc., 2012
   6 *
   7 * Authors:
   8 *  Anthony Liguori   <aliguori@us.ibm.com>
   9 *  Paolo Bonzini     <pbonzini@redhat.com>
  10 *
  11 * This work is licensed under the terms of the GNU GPL, version 2.  See
  12 * the COPYING file in the top-level directory.
  13 *
  14 * Contributions after 2012-01-13 are licensed under the terms of the
  15 * GNU GPL, version 2 or (at your option) any later version.
  16 */
  17
  18#include "qemu-common.h"
  19#include "block.h"
  20#include "qemu-queue.h"
  21#include "qemu_socket.h"
  22
  23struct AioHandler {
  24    EventNotifier *e;
  25    EventNotifierHandler *io_notify;
  26    AioFlushEventNotifierHandler *io_flush;
  27    GPollFD pfd;
  28    int deleted;
  29    QLIST_ENTRY(AioHandler) node;
  30};
  31
  32void aio_set_event_notifier(AioContext *ctx,
  33                            EventNotifier *e,
  34                            EventNotifierHandler *io_notify,
  35                            AioFlushEventNotifierHandler *io_flush)
  36{
  37    AioHandler *node;
  38
  39    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
  40        if (node->e == e && !node->deleted) {
  41            break;
  42        }
  43    }
  44
  45    /* Are we deleting the fd handler? */
  46    if (!io_notify) {
  47        if (node) {
  48            g_source_remove_poll(&ctx->source, &node->pfd);
  49
  50            /* If the lock is held, just mark the node as deleted */
  51            if (ctx->walking_handlers) {
  52                node->deleted = 1;
  53                node->pfd.revents = 0;
  54            } else {
  55                /* Otherwise, delete it for real.  We can't just mark it as
  56                 * deleted because deleted nodes are only cleaned up after
  57                 * releasing the walking_handlers lock.
  58                 */
  59                QLIST_REMOVE(node, node);
  60                g_free(node);
  61            }
  62        }
  63    } else {
  64        if (node == NULL) {
  65            /* Alloc and insert if it's not already there */
  66            node = g_malloc0(sizeof(AioHandler));
  67            node->e = e;
  68            node->pfd.fd = (uintptr_t)event_notifier_get_handle(e);
  69            node->pfd.events = G_IO_IN;
  70            QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
  71
  72            g_source_add_poll(&ctx->source, &node->pfd);
  73        }
  74        /* Update handler with latest information */
  75        node->io_notify = io_notify;
  76        node->io_flush = io_flush;
  77    }
  78
  79    aio_notify(ctx);
  80}
  81
  82bool aio_pending(AioContext *ctx)
  83{
  84    AioHandler *node;
  85
  86    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
  87        if (node->pfd.revents && node->io_notify) {
  88            return true;
  89        }
  90    }
  91
  92    return false;
  93}
  94
  95bool aio_poll(AioContext *ctx, bool blocking)
  96{
  97    AioHandler *node;
  98    HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
  99    bool busy, progress;
 100    int count;
 101
 102    progress = false;
 103
 104    /*
 105     * If there are callbacks left that have been queued, we need to call then.
 106     * Do not call select in this case, because it is possible that the caller
 107     * does not need a complete flush (as is the case for qemu_aio_wait loops).
 108     */
 109    if (aio_bh_poll(ctx)) {
 110        blocking = false;
 111        progress = true;
 112    }
 113
 114    /*
 115     * Then dispatch any pending callbacks from the GSource.
 116     *
 117     * We have to walk very carefully in case qemu_aio_set_fd_handler is
 118     * called while we're walking.
 119     */
 120    node = QLIST_FIRST(&ctx->aio_handlers);
 121    while (node) {
 122        AioHandler *tmp;
 123
 124        ctx->walking_handlers++;
 125
 126        if (node->pfd.revents && node->io_notify) {
 127            node->pfd.revents = 0;
 128            node->io_notify(node->e);
 129            progress = true;
 130        }
 131
 132        tmp = node;
 133        node = QLIST_NEXT(node, node);
 134
 135        ctx->walking_handlers--;
 136
 137        if (!ctx->walking_handlers && tmp->deleted) {
 138            QLIST_REMOVE(tmp, node);
 139            g_free(tmp);
 140        }
 141    }
 142
 143    if (progress && !blocking) {
 144        return true;
 145    }
 146
 147    ctx->walking_handlers++;
 148
 149    /* fill fd sets */
 150    busy = false;
 151    count = 0;
 152    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
 153        /* If there aren't pending AIO operations, don't invoke callbacks.
 154         * Otherwise, if there are no AIO requests, qemu_aio_wait() would
 155         * wait indefinitely.
 156         */
 157        if (!node->deleted && node->io_flush) {
 158            if (node->io_flush(node->e) == 0) {
 159                continue;
 160            }
 161            busy = true;
 162        }
 163        if (!node->deleted && node->io_notify) {
 164            events[count++] = event_notifier_get_handle(node->e);
 165        }
 166    }
 167
 168    ctx->walking_handlers--;
 169
 170    /* No AIO operations?  Get us out of here */
 171    if (!busy) {
 172        return progress;
 173    }
 174
 175    /* wait until next event */
 176    while (count > 0) {
 177        int timeout = blocking ? INFINITE : 0;
 178        int ret = WaitForMultipleObjects(count, events, FALSE, timeout);
 179
 180        /* if we have any signaled events, dispatch event */
 181        if ((DWORD) (ret - WAIT_OBJECT_0) >= count) {
 182            break;
 183        }
 184
 185        blocking = false;
 186
 187        /* we have to walk very carefully in case
 188         * qemu_aio_set_fd_handler is called while we're walking */
 189        node = QLIST_FIRST(&ctx->aio_handlers);
 190        while (node) {
 191            AioHandler *tmp;
 192
 193            ctx->walking_handlers++;
 194
 195            if (!node->deleted &&
 196                event_notifier_get_handle(node->e) == events[ret - WAIT_OBJECT_0] &&
 197                node->io_notify) {
 198                node->io_notify(node->e);
 199                progress = true;
 200            }
 201
 202            tmp = node;
 203            node = QLIST_NEXT(node, node);
 204
 205            ctx->walking_handlers--;
 206
 207            if (!ctx->walking_handlers && tmp->deleted) {
 208                QLIST_REMOVE(tmp, node);
 209                g_free(tmp);
 210            }
 211        }
 212
 213        /* Try again, but only call each handler once.  */
 214        events[ret - WAIT_OBJECT_0] = events[--count];
 215    }
 216
 217    return progress;
 218}
 219