aio-posix.c 7.2 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 59

    /* Are we deleting the fd handler? */
    if (!io_read && !io_write) {
        if (node) {
            /* If the lock is held, just mark the node as deleted */
60
            if (ctx->walking_handlers) {
61
                node->deleted = 1;
62 63
                node->pfd.revents = 0;
            } else {
64 65 66 67
                /* 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 已提交
68
                QLIST_REMOVE(node, node);
69
                g_free(node);
70 71 72 73 74
            }
        }
    } else {
        if (node == NULL) {
            /* Alloc and insert if it's not already there */
75
            node = g_malloc0(sizeof(AioHandler));
76
            node->pfd.fd = fd;
77
            QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
78 79 80 81 82 83
        }
        /* Update handler with latest information */
        node->io_read = io_read;
        node->io_write = io_write;
        node->io_flush = io_flush;
        node->opaque = opaque;
84 85 86

        node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0);
        node->pfd.events |= (io_write ? G_IO_OUT : 0);
87
    }
88 89
}

90 91 92 93
void aio_set_event_notifier(AioContext *ctx,
                            EventNotifier *notifier,
                            EventNotifierHandler *io_read,
                            AioFlushEventNotifierHandler *io_flush)
94
{
95 96 97
    aio_set_fd_handler(ctx, event_notifier_get_fd(notifier),
                       (IOHandler *)io_read, NULL,
                       (AioFlushHandler *)io_flush, notifier);
98 99
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
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;
}

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

    progress = false;
136

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

147 148 149 150 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
    /*
     * 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);
        }
    }

184
    if (progress && !blocking) {
185
        return true;
186
    }
K
Kevin Wolf 已提交
187

188
    ctx->walking_handlers++;
189

P
Paolo Bonzini 已提交
190 191
    FD_ZERO(&rdfds);
    FD_ZERO(&wrfds);
192

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

216
    ctx->walking_handlers--;
217

P
Paolo Bonzini 已提交
218 219
    /* No AIO operations?  Get us out of here */
    if (!busy) {
220
        return progress;
P
Paolo Bonzini 已提交
221
    }
222

P
Paolo Bonzini 已提交
223
    /* wait until next event */
224
    ret = select(max_fd, &rdfds, &wrfds, NULL, blocking ? NULL : &tv0);
P
Paolo Bonzini 已提交
225 226 227 228 229

    /* 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 */
230
        node = QLIST_FIRST(&ctx->aio_handlers);
P
Paolo Bonzini 已提交
231 232 233
        while (node) {
            AioHandler *tmp;

234
            ctx->walking_handlers++;
235

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

P
Paolo Bonzini 已提交
249 250 251
            tmp = node;
            node = QLIST_NEXT(node, node);

252
            ctx->walking_handlers--;
253

254
            if (!ctx->walking_handlers && tmp->deleted) {
P
Paolo Bonzini 已提交
255 256 257
                QLIST_REMOVE(tmp, node);
                g_free(tmp);
            }
258
        }
P
Paolo Bonzini 已提交
259
    }
260

261
    return progress;
262
}