• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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"
16 #include "qemu-queue.h"
17 #include "qemu_socket.h"
18 #include "iolooper.h"
19 
20 typedef struct AioHandler AioHandler;
21 
22 /* The list of registered AIO handlers */
23 static QLIST_HEAD(, AioHandler) aio_handlers;
24 
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  */
29 static int walking_handlers;
30 
31 struct AioHandler
32 {
33     int fd;
34     IOHandler *io_read;
35     IOHandler *io_write;
36     AioFlushHandler *io_flush;
37     AioProcessQueue *io_process_queue;
38     int deleted;
39     void *opaque;
40     QLIST_ENTRY(AioHandler) node;
41 };
42 
find_aio_handler(int fd)43 static AioHandler *find_aio_handler(int fd)
44 {
45     AioHandler *node;
46 
47     QLIST_FOREACH(node, &aio_handlers, node) {
48         if (node->fd == fd)
49             if (!node->deleted)
50                 return node;
51     }
52 
53     return NULL;
54 }
55 
qemu_aio_set_fd_handler(int fd,IOHandler * io_read,IOHandler * io_write,AioFlushHandler * io_flush,AioProcessQueue * io_process_queue,void * opaque)56 int qemu_aio_set_fd_handler(int fd,
57                             IOHandler *io_read,
58                             IOHandler *io_write,
59                             AioFlushHandler *io_flush,
60                             AioProcessQueue *io_process_queue,
61                             void *opaque)
62 {
63     AioHandler *node;
64 
65     node = find_aio_handler(fd);
66 
67     /* Are we deleting the fd handler? */
68     if (!io_read && !io_write) {
69         if (node) {
70             /* If the lock is held, just mark the node as deleted */
71             if (walking_handlers)
72                 node->deleted = 1;
73             else {
74                 /* Otherwise, delete it for real.  We can't just mark it as
75                  * deleted because deleted nodes are only cleaned up after
76                  * releasing the walking_handlers lock.
77                  */
78                 QLIST_REMOVE(node, node);
79                 qemu_free(node);
80             }
81         }
82     } else {
83         if (node == NULL) {
84             /* Alloc and insert if it's not already there */
85             node = qemu_mallocz(sizeof(AioHandler));
86             node->fd = fd;
87             QLIST_INSERT_HEAD(&aio_handlers, node, node);
88         }
89         /* Update handler with latest information */
90         node->io_read = io_read;
91         node->io_write = io_write;
92         node->io_flush = io_flush;
93         node->io_process_queue = io_process_queue;
94         node->opaque = opaque;
95     }
96 
97     qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque);
98 
99     return 0;
100 }
101 
qemu_aio_flush(void)102 void qemu_aio_flush(void)
103 {
104     AioHandler *node;
105     int ret;
106 
107     do {
108         ret = 0;
109 
110 	/*
111 	 * If there are pending emulated aio start them now so flush
112 	 * will be able to return 1.
113 	 */
114         qemu_aio_wait();
115 
116         QLIST_FOREACH(node, &aio_handlers, node) {
117             if (node->io_flush) {
118                 ret |= node->io_flush(node->opaque);
119             }
120         }
121     } while (qemu_bh_poll() || ret > 0);
122 }
123 
qemu_aio_process_queue(void)124 int qemu_aio_process_queue(void)
125 {
126     AioHandler *node;
127     int ret = 0;
128 
129     walking_handlers = 1;
130 
131     QLIST_FOREACH(node, &aio_handlers, node) {
132         if (node->io_process_queue) {
133             if (node->io_process_queue(node->opaque)) {
134                 ret = 1;
135             }
136         }
137     }
138 
139     walking_handlers = 0;
140 
141     return ret;
142 }
143 
qemu_aio_wait(void)144 void qemu_aio_wait(void)
145 {
146     int ret;
147     IoLooper*  looper;
148 
149     if (qemu_bh_poll())
150         return;
151 
152     /*
153      * If there are callbacks left that have been queued, we need to call then.
154      * Return afterwards to avoid waiting needlessly in select().
155      */
156     if (qemu_aio_process_queue())
157         return;
158 
159     looper = iolooper_new();
160 
161     do {
162         AioHandler *node;
163 
164         iolooper_reset(looper);
165         walking_handlers = 1;
166 
167         /* fill fd sets */
168         QLIST_FOREACH(node, &aio_handlers, node) {
169             /* If there aren't pending AIO operations, don't invoke callbacks.
170              * Otherwise, if there are no AIO requests, qemu_aio_wait() would
171              * wait indefinitely.
172              */
173             if (node->io_flush && node->io_flush(node->opaque) == 0)
174                 continue;
175 
176             if (!node->deleted && node->io_read) {
177                 iolooper_add_read(looper, node->fd);
178             }
179             if (!node->deleted && node->io_write) {
180                 iolooper_add_write(looper, node->fd);
181             }
182         }
183 
184         walking_handlers = 0;
185 
186         /* No AIO operations? Get us out of here */
187         if (!iolooper_has_operations(looper)) {
188             break;
189         }
190 
191         /* wait until next event */
192         ret = iolooper_wait(looper, -1);
193 
194         /* if we have any readable fds, dispatch event */
195         if (ret > 0) {
196             walking_handlers = 1;
197 
198             /* we have to walk very carefully in case
199              * qemu_aio_set_fd_handler is called while we're walking */
200             node = QLIST_FIRST(&aio_handlers);
201             while (node) {
202                 AioHandler *tmp;
203 
204                 if (!node->deleted &&
205                     iolooper_is_read(looper, node->fd) &&
206                     node->io_read) {
207                     node->io_read(node->opaque);
208                 }
209                 if (!node->deleted &&
210                     iolooper_is_write(looper, node->fd) &&
211                     node->io_write) {
212                     node->io_write(node->opaque);
213                 }
214 
215                 tmp = node;
216                 node = QLIST_NEXT(node, node);
217 
218                 if (tmp->deleted) {
219                     QLIST_REMOVE(tmp, node);
220                     qemu_free(tmp);
221                 }
222             }
223 
224             walking_handlers = 0;
225         }
226     } while (ret == 0);
227 
228     iolooper_free(looper);
229 }
230