blob: 53f7a6d5d4f2833d6ce9339fa4d7b4b8b3f5b93b [file] [log] [blame]
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -07001/*
2 * QEMU aio implementation
3 *
4 * Copyright IBM, Corp. 2008
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
11 *
12 */
13
14#include "qemu-common.h"
15#include "block.h"
David 'Digit' Turnera5d41202010-05-10 18:37:10 -070016#include "qemu-queue.h"
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070017#include "qemu_socket.h"
18#include "iolooper.h"
19
20typedef struct AioHandler AioHandler;
21
22/* The list of registered AIO handlers */
David 'Digit' Turnera5d41202010-05-10 18:37:10 -070023static QLIST_HEAD(, AioHandler) aio_handlers;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070024
25/* This is a simple lock used to protect the aio_handlers list. Specifically,
26 * it's used to ensure that no callbacks are removed while we're walking and
27 * dispatching callbacks.
28 */
29static int walking_handlers;
30
31struct AioHandler
32{
33 int fd;
34 IOHandler *io_read;
35 IOHandler *io_write;
36 AioFlushHandler *io_flush;
37 int deleted;
38 void *opaque;
David 'Digit' Turnera5d41202010-05-10 18:37:10 -070039 QLIST_ENTRY(AioHandler) node;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070040};
41
42static AioHandler *find_aio_handler(int fd)
43{
44 AioHandler *node;
45
David 'Digit' Turnera5d41202010-05-10 18:37:10 -070046 QLIST_FOREACH(node, &aio_handlers, node) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070047 if (node->fd == fd)
48 if (!node->deleted)
49 return node;
50 }
51
52 return NULL;
53}
54
55int qemu_aio_set_fd_handler(int fd,
56 IOHandler *io_read,
57 IOHandler *io_write,
58 AioFlushHandler *io_flush,
59 void *opaque)
60{
61 AioHandler *node;
62
63 node = find_aio_handler(fd);
64
65 /* Are we deleting the fd handler? */
66 if (!io_read && !io_write) {
67 if (node) {
68 /* If the lock is held, just mark the node as deleted */
69 if (walking_handlers)
70 node->deleted = 1;
71 else {
72 /* Otherwise, delete it for real. We can't just mark it as
73 * deleted because deleted nodes are only cleaned up after
74 * releasing the walking_handlers lock.
75 */
David 'Digit' Turnera5d41202010-05-10 18:37:10 -070076 QLIST_REMOVE(node, node);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070077 qemu_free(node);
78 }
79 }
80 } else {
81 if (node == NULL) {
82 /* Alloc and insert if it's not already there */
83 node = qemu_mallocz(sizeof(AioHandler));
84 node->fd = fd;
David 'Digit' Turnera5d41202010-05-10 18:37:10 -070085 QLIST_INSERT_HEAD(&aio_handlers, node, node);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070086 }
87 /* Update handler with latest information */
88 node->io_read = io_read;
89 node->io_write = io_write;
90 node->io_flush = io_flush;
91 node->opaque = opaque;
92 }
93
94 qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque);
95
96 return 0;
97}
98
99void qemu_aio_flush(void)
100{
101 AioHandler *node;
102 int ret;
103
104 do {
105 ret = 0;
106
107 /*
108 * If there are pending emulated aio start them now so flush
109 * will be able to return 1.
110 */
111 qemu_aio_wait();
112
David 'Digit' Turnera5d41202010-05-10 18:37:10 -0700113 QLIST_FOREACH(node, &aio_handlers, node) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700114 ret |= node->io_flush(node->opaque);
115 }
116 } while (ret > 0);
117}
118
119void qemu_aio_wait(void)
120{
121 int ret;
122 IoLooper* looper;
123
124 if (qemu_bh_poll())
125 return;
126
127 looper = iolooper_new();
128
129 do {
130 AioHandler *node;
131
132 iolooper_reset(looper);
133 walking_handlers = 1;
134
135 /* fill fd sets */
David 'Digit' Turnera5d41202010-05-10 18:37:10 -0700136 QLIST_FOREACH(node, &aio_handlers, node) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700137 /* If there aren't pending AIO operations, don't invoke callbacks.
138 * Otherwise, if there are no AIO requests, qemu_aio_wait() would
139 * wait indefinitely.
140 */
141 if (node->io_flush && node->io_flush(node->opaque) == 0)
142 continue;
143
144 if (!node->deleted && node->io_read) {
145 iolooper_add_read(looper, node->fd);
146 }
147 if (!node->deleted && node->io_write) {
148 iolooper_add_write(looper, node->fd);
149 }
150 }
151
152 walking_handlers = 0;
153
154 /* wait until next event */
155 ret = iolooper_wait(looper, -1);
156
157 /* if we have any readable fds, dispatch event */
158 if (ret > 0) {
159 walking_handlers = 1;
160
161 /* we have to walk very carefully in case
162 * qemu_aio_set_fd_handler is called while we're walking */
David 'Digit' Turnera5d41202010-05-10 18:37:10 -0700163 node = QLIST_FIRST(&aio_handlers);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700164 while (node) {
165 AioHandler *tmp;
166
167 if (!node->deleted &&
168 iolooper_is_read(looper, node->fd) &&
169 node->io_read) {
170 node->io_read(node->opaque);
171 }
172 if (!node->deleted &&
173 iolooper_is_write(looper, node->fd) &&
174 node->io_write) {
175 node->io_write(node->opaque);
176 }
177
178 tmp = node;
David 'Digit' Turnera5d41202010-05-10 18:37:10 -0700179 node = QLIST_NEXT(node, node);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700180
181 if (tmp->deleted) {
David 'Digit' Turnera5d41202010-05-10 18:37:10 -0700182 QLIST_REMOVE(tmp, node);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700183 qemu_free(tmp);
184 }
185 }
186
187 walking_handlers = 0;
188 }
189 } while (ret == 0);
190
191 iolooper_free(looper);
192}