aio-posix.c 7.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * QEMU aio implementation
 *
 * Copyright IBM, Corp. 2008
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 * the COPYING file in the top-level directory.
 *
12 13
 * Contributions after 2012-01-13 are licensed under the terms of the
 * GNU GPL, version 2 or (at your option) any later version.
14 15 16 17
 */

#include "qemu-common.h"
#include "block.h"
B
Blue Swirl 已提交
18
#include "qemu-queue.h"
19 20 21 22
#include "qemu_socket.h"

struct AioHandler
{
23
    GPollFD pfd;
24 25 26 27 28
    IOHandler *io_read;
    IOHandler *io_write;
    AioFlushHandler *io_flush;
    int deleted;
    void *opaque;
B
Blue Swirl 已提交
29
    QLIST_ENTRY(AioHandler) node;
30 31
};

32
static AioHandler *find_aio_handler(AioContext *ctx, int fd)
33 34 35
{
    AioHandler *node;

36
    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
37
        if (node->pfd.fd == fd)
A
Alexander Graf 已提交
38 39
            if (!node->deleted)
                return node;
40 41 42 43 44
    }

    return NULL;
}

45 46 47 48 49 50
void aio_set_fd_handler(AioContext *ctx,
                        int fd,
                        IOHandler *io_read,
                        IOHandler *io_write,
                        AioFlushHandler *io_flush,
                        void *opaque)
51 52 53
{
    AioHandler *node;

54
    node = find_aio_handler(ctx, fd);
55 56 57 58

    /* Are we deleting the fd handler? */
    if (!io_read && !io_write) {
        if (node) {
P
Paolo Bonzini 已提交
59 60
            g_source_remove_poll(&ctx->source, &node->pfd);

61
            /* If the lock is held, just mark the node as deleted */
62
            if (ctx->walking_handlers) {
63
                node->deleted = 1;
64 65
                node->pfd.revents = 0;
            } else {
66 67 68 69
                /* 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.
                 */
B
Blue Swirl 已提交
70
                QLIST_REMOVE(node, node);
71
                g_free(node);
72 73 74 75 76
            }
        }
    } else {
        if (node == NULL) {
            /* Alloc and insert if it's not already there */
77
            node = g_malloc0(sizeof(AioHandler));
78
            node->pfd.fd = fd;
79
            QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
P
Paolo Bonzini 已提交
80 81

            g_source_add_poll(&ctx->source, &node->pfd);
82 83 84 85 86 87
        }
        /* Update handler with latest information */
        node->io_read = io_read;
        node->io_write = io_write;
        node->io_flush = io_flush;
        node->opaque = opaque;
88 89 90

        node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0);
        node->pfd.events |= (io_write ? G_IO_OUT : 0);
91
    }
92 93
}

94 95 96 97
void aio_set_event_notifier(AioContext *ctx,
                            EventNotifier *notifier,
                            EventNotifierHandler *io_read,
                            AioFlushEventNotifierHandler *io_flush)
98
{
99 100 101
    aio_set_fd_handler(ctx, event_notifier_get_fd(notifier),
                       (IOHandler *)io_read, NULL,
                       (AioFlushHandler *)io_flush, notifier);
102 103
}

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
bool aio_pending(AioContext *ctx)
{
    AioHandler *node;

    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
        int revents;

        /*
         * FIXME: right now we cannot get G_IO_HUP and G_IO_ERR because
         * main-loop.c is still select based (due to the slirp legacy).
         * If main-loop.c ever switches to poll, G_IO_ERR should be
         * tested too.  Dispatching G_IO_ERR to both handlers should be
         * okay, since handlers need to be ready for spurious wakeups.
         */
        revents = node->pfd.revents & node->pfd.events;
        if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
            return true;
        }
        if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
            return true;
        }
    }

    return false;
}

130
bool aio_poll(AioContext *ctx, bool blocking)
131
{
132
    static struct timeval tv0;
P
Paolo Bonzini 已提交
133 134 135
    AioHandler *node;
    fd_set rdfds, wrfds;
    int max_fd = -1;
136
    int ret;
137 138 139
    bool busy, progress;

    progress = false;
140

K
Kevin Wolf 已提交
141 142
    /*
     * If there are callbacks left that have been queued, we need to call then.
143 144
     * 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).
K
Kevin Wolf 已提交
145
     */
146
    if (aio_bh_poll(ctx)) {
147 148 149 150
        blocking = false;
        progress = true;
    }

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    /*
     * Then dispatch any pending callbacks from the GSource.
     *
     * We have to walk very carefully in case qemu_aio_set_fd_handler is
     * called while we're walking.
     */
    node = QLIST_FIRST(&ctx->aio_handlers);
    while (node) {
        AioHandler *tmp;
        int revents;

        ctx->walking_handlers++;

        revents = node->pfd.revents & node->pfd.events;
        node->pfd.revents = 0;

        /* See comment in aio_pending.  */
        if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
            node->io_read(node->opaque);
            progress = true;
        }
        if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
            node->io_write(node->opaque);
            progress = true;
        }

        tmp = node;
        node = QLIST_NEXT(node, node);

        ctx->walking_handlers--;

        if (!ctx->walking_handlers && tmp->deleted) {
            QLIST_REMOVE(tmp, node);
            g_free(tmp);
        }
    }

188
    if (progress && !blocking) {
189
        return true;
190
    }
K
Kevin Wolf 已提交
191

192
    ctx->walking_handlers++;
193

P
Paolo Bonzini 已提交
194 195
    FD_ZERO(&rdfds);
    FD_ZERO(&wrfds);
196

P
Paolo Bonzini 已提交
197 198
    /* fill fd sets */
    busy = false;
199
    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
P
Paolo Bonzini 已提交
200 201 202 203
        /* If there aren't pending AIO operations, don't invoke callbacks.
         * Otherwise, if there are no AIO requests, qemu_aio_wait() would
         * wait indefinitely.
         */
204
        if (!node->deleted && node->io_flush) {
P
Paolo Bonzini 已提交
205 206
            if (node->io_flush(node->opaque) == 0) {
                continue;
207
            }
P
Paolo Bonzini 已提交
208 209 210
            busy = true;
        }
        if (!node->deleted && node->io_read) {
211 212
            FD_SET(node->pfd.fd, &rdfds);
            max_fd = MAX(max_fd, node->pfd.fd + 1);
213
        }
P
Paolo Bonzini 已提交
214
        if (!node->deleted && node->io_write) {
215 216
            FD_SET(node->pfd.fd, &wrfds);
            max_fd = MAX(max_fd, node->pfd.fd + 1);
P
Paolo Bonzini 已提交
217 218
        }
    }
219

220
    ctx->walking_handlers--;
221

P
Paolo Bonzini 已提交
222 223
    /* No AIO operations?  Get us out of here */
    if (!busy) {
224
        return progress;
P
Paolo Bonzini 已提交
225
    }
226

P
Paolo Bonzini 已提交
227
    /* wait until next event */
228
    ret = select(max_fd, &rdfds, &wrfds, NULL, blocking ? NULL : &tv0);
P
Paolo Bonzini 已提交
229 230 231 232 233

    /* if we have any readable fds, dispatch event */
    if (ret > 0) {
        /* we have to walk very carefully in case
         * qemu_aio_set_fd_handler is called while we're walking */
234
        node = QLIST_FIRST(&ctx->aio_handlers);
P
Paolo Bonzini 已提交
235 236 237
        while (node) {
            AioHandler *tmp;

238
            ctx->walking_handlers++;
239

P
Paolo Bonzini 已提交
240
            if (!node->deleted &&
241
                FD_ISSET(node->pfd.fd, &rdfds) &&
P
Paolo Bonzini 已提交
242 243
                node->io_read) {
                node->io_read(node->opaque);
244
                progress = true;
P
Paolo Bonzini 已提交
245 246
            }
            if (!node->deleted &&
247
                FD_ISSET(node->pfd.fd, &wrfds) &&
P
Paolo Bonzini 已提交
248 249
                node->io_write) {
                node->io_write(node->opaque);
250
                progress = true;
251 252
            }

P
Paolo Bonzini 已提交
253 254 255
            tmp = node;
            node = QLIST_NEXT(node, node);

256
            ctx->walking_handlers--;
257

258
            if (!ctx->walking_handlers && tmp->deleted) {
P
Paolo Bonzini 已提交
259 260 261
                QLIST_REMOVE(tmp, node);
                g_free(tmp);
            }
262
        }
P
Paolo Bonzini 已提交
263
    }
264

265
    return progress;
266
}