]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * QEMU aio implementation | |
3 | * | |
4 | * Copyright IBM Corp., 2008 | |
5 | * Copyright Red Hat Inc., 2012 | |
6 | * | |
7 | * Authors: | |
8 | * Anthony Liguori <[email protected]> | |
9 | * Paolo Bonzini <[email protected]> | |
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 | AioFlushEventNotifierHandler *io_flush; | |
27 | GPollFD pfd; | |
28 | int deleted; | |
29 | QLIST_ENTRY(AioHandler) node; | |
30 | }; | |
31 | ||
32 | void 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 | ||
82 | bool 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 | ||
95 | bool 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 | assert(progress || busy); | |
218 | return true; | |
219 | } |