Statistics
| Branch: | Revision:

root / aio-win32.c @ f2e5dca4

History | View | Annotate | Download (5.5 kB)

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/block.h"
20
#include "qemu/queue.h"
21
#include "qemu/sockets.h"
22

    
23
struct AioHandler {
24
    EventNotifier *e;
25
    EventNotifierHandler *io_notify;
26
    GPollFD pfd;
27
    int deleted;
28
    QLIST_ENTRY(AioHandler) node;
29
};
30

    
31
void aio_set_event_notifier(AioContext *ctx,
32
                            EventNotifier *e,
33
                            EventNotifierHandler *io_notify)
34
{
35
    AioHandler *node;
36

    
37
    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
38
        if (node->e == e && !node->deleted) {
39
            break;
40
        }
41
    }
42

    
43
    /* Are we deleting the fd handler? */
44
    if (!io_notify) {
45
        if (node) {
46
            g_source_remove_poll(&ctx->source, &node->pfd);
47

    
48
            /* If the lock is held, just mark the node as deleted */
49
            if (ctx->walking_handlers) {
50
                node->deleted = 1;
51
                node->pfd.revents = 0;
52
            } else {
53
                /* Otherwise, delete it for real.  We can't just mark it as
54
                 * deleted because deleted nodes are only cleaned up after
55
                 * releasing the walking_handlers lock.
56
                 */
57
                QLIST_REMOVE(node, node);
58
                g_free(node);
59
            }
60
        }
61
    } else {
62
        if (node == NULL) {
63
            /* Alloc and insert if it's not already there */
64
            node = g_malloc0(sizeof(AioHandler));
65
            node->e = e;
66
            node->pfd.fd = (uintptr_t)event_notifier_get_handle(e);
67
            node->pfd.events = G_IO_IN;
68
            QLIST_INSERT_HEAD(&ctx->aio_handlers, node, node);
69

    
70
            g_source_add_poll(&ctx->source, &node->pfd);
71
        }
72
        /* Update handler with latest information */
73
        node->io_notify = io_notify;
74
    }
75

    
76
    aio_notify(ctx);
77
}
78

    
79
bool aio_pending(AioContext *ctx)
80
{
81
    AioHandler *node;
82

    
83
    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
84
        if (node->pfd.revents && node->io_notify) {
85
            return true;
86
        }
87
    }
88

    
89
    return false;
90
}
91

    
92
bool aio_poll(AioContext *ctx, bool blocking)
93
{
94
    AioHandler *node;
95
    HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
96
    bool progress;
97
    int count;
98

    
99
    progress = false;
100

    
101
    /*
102
     * If there are callbacks left that have been queued, we need to call then.
103
     * Do not call select in this case, because it is possible that the caller
104
     * does not need a complete flush (as is the case for qemu_aio_wait loops).
105
     */
106
    if (aio_bh_poll(ctx)) {
107
        blocking = false;
108
        progress = true;
109
    }
110

    
111
    /*
112
     * Then dispatch any pending callbacks from the GSource.
113
     *
114
     * We have to walk very carefully in case qemu_aio_set_fd_handler is
115
     * called while we're walking.
116
     */
117
    node = QLIST_FIRST(&ctx->aio_handlers);
118
    while (node) {
119
        AioHandler *tmp;
120

    
121
        ctx->walking_handlers++;
122

    
123
        if (node->pfd.revents && node->io_notify) {
124
            node->pfd.revents = 0;
125
            node->io_notify(node->e);
126

    
127
            /* aio_notify() does not count as progress */
128
            if (node->opaque != &ctx->notifier) {
129
                progress = true;
130
            }
131
        }
132

    
133
        tmp = node;
134
        node = QLIST_NEXT(node, node);
135

    
136
        ctx->walking_handlers--;
137

    
138
        if (!ctx->walking_handlers && tmp->deleted) {
139
            QLIST_REMOVE(tmp, node);
140
            g_free(tmp);
141
        }
142
    }
143

    
144
    if (progress && !blocking) {
145
        return true;
146
    }
147

    
148
    ctx->walking_handlers++;
149

    
150
    /* fill fd sets */
151
    count = 0;
152
    QLIST_FOREACH(node, &ctx->aio_handlers, node) {
153
        if (!node->deleted && node->io_notify) {
154
            events[count++] = event_notifier_get_handle(node->e);
155
        }
156
    }
157

    
158
    ctx->walking_handlers--;
159

    
160
    /* early return if we only have the aio_notify() fd */
161
    if (count == 1) {
162
        return progress;
163
    }
164

    
165
    /* wait until next event */
166
    while (count > 0) {
167
        int timeout = blocking ? INFINITE : 0;
168
        int ret = WaitForMultipleObjects(count, events, FALSE, timeout);
169

    
170
        /* if we have any signaled events, dispatch event */
171
        if ((DWORD) (ret - WAIT_OBJECT_0) >= count) {
172
            break;
173
        }
174

    
175
        blocking = false;
176

    
177
        /* we have to walk very carefully in case
178
         * qemu_aio_set_fd_handler is called while we're walking */
179
        node = QLIST_FIRST(&ctx->aio_handlers);
180
        while (node) {
181
            AioHandler *tmp;
182

    
183
            ctx->walking_handlers++;
184

    
185
            if (!node->deleted &&
186
                event_notifier_get_handle(node->e) == events[ret - WAIT_OBJECT_0] &&
187
                node->io_notify) {
188
                node->io_notify(node->e);
189

    
190
                /* aio_notify() does not count as progress */
191
                if (node->opaque != &ctx->notifier) {
192
                    progress = true;
193
                }
194
            }
195

    
196
            tmp = node;
197
            node = QLIST_NEXT(node, node);
198

    
199
            ctx->walking_handlers--;
200

    
201
            if (!ctx->walking_handlers && tmp->deleted) {
202
                QLIST_REMOVE(tmp, node);
203
                g_free(tmp);
204
            }
205
        }
206

    
207
        /* Try again, but only call each handler once.  */
208
        events[ret - WAIT_OBJECT_0] = events[--count];
209
    }
210

    
211
    return progress;
212
}