Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2020, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : : #define IO_SIGNAL SIGUSR1
31 : :
32 : : #ifdef HAVE_CONFIG_H
33 : : #include "config.h"
34 : : #endif
35 : :
36 : : #include <errno.h>
37 : : #include <stdlib.h>
38 : : #include <unistd.h>
39 : : #include <aio.h>
40 : :
41 : : #include "tapdisk.h"
42 : : #include "tapdisk-log.h"
43 : : #include "posixaio-backend.h"
44 : : #include "tapdisk-server.h"
45 : : #include "tapdisk-utils.h"
46 : : #include "timeout-math.h"
47 : : #include <signal.h>
48 : :
49 : : #include "atomicio.h"
50 : : #include "aio_getevents.h"
51 : : #include <sys/signalfd.h>
52 : : #include "debug.h"
53 : :
54 : :
55 : : #define WARN(_f, _a...) tlog_write(TLOG_WARN, _f, ##_a)
56 : : #define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
57 : : #define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a)
58 : :
59 : : #define posixaio_backend_queue_empty(q) ((q)->queued == 0)
60 : : #define posixaio_backend_queue_full(q) \
61 : : (((q)->tiocbs_pending + (q)->queued) >= (q)->size)
62 : : typedef struct _posix_aio_queue {
63 : : int size;
64 : :
65 : : const struct tio *tio;
66 : : void *tio_data;
67 : :
68 : : int queued;
69 : : struct aiocb **aiocbList;
70 : : struct tiocb **tiocbList;
71 : :
72 : : int tiocbs_pending;
73 : : struct tlist pending;
74 : :
75 : : struct tlist deferred;
76 : : int tiocbs_deferred;
77 : :
78 : : uint64_t deferrals;
79 : : } posix_aio_queue;
80 : :
81 : : struct tio {
82 : : const char *name;
83 : : size_t data_size;
84 : :
85 : : int (*tio_setup) (posix_aio_queue *queue, int qlen);
86 : : void (*tio_destroy) (posix_aio_queue *queue);
87 : : int (*tio_submit) (posix_aio_queue *queue);
88 : : };
89 : :
90 : : enum {
91 : : TIO_DRV_LIO = 1,
92 : : };
93 : :
94 : : static inline void
95 : : queue_tiocb(posix_aio_queue *queue, struct tiocb *tiocb)
96 : : {
97 : 0 : queue->aiocbList[queue->queued] = &(tiocb->uiocb.aio);
98 : 0 : queue->tiocbList[queue->queued] = tiocb;
99 : 0 : queue->queued++;
100 : : }
101 : :
102 : : static inline int
103 : 0 : deferred_tiocbs(posix_aio_queue *queue)
104 : : {
105 : 0 : return (queue->deferred.head != NULL);
106 : : }
107 : :
108 : : static inline void
109 : : defer_tiocb(posix_aio_queue *queue, struct tiocb *tiocb)
110 : : {
111 : 0 : struct tlist *list = &queue->deferred;
112 : :
113 : 0 : if (!list->head)
114 : 0 : list->head = list->tail = tiocb;
115 : : else
116 : 0 : list->tail = list->tail->next = tiocb;
117 : :
118 : 0 : queue->tiocbs_deferred++;
119 : 0 : queue->deferrals++;
120 : : }
121 : :
122 : : static void
123 : : push_list(struct tlist *list, struct tiocb* tiocb)
124 : : {
125 : 0 : if (!list->head)
126 : 0 : list->head = list->tail = tiocb;
127 : : else
128 : 0 : list->tail = list->tail->next = tiocb;
129 : : }
130 : :
131 : : static void
132 : : pending_tiocb(posix_aio_queue *queue, struct tiocb *tiocb)
133 : : {
134 : 0 : struct tlist *list = &queue->pending;
135 : :
136 : : push_list(list, tiocb);
137 : 0 : queue->tiocbs_pending++;
138 : : }
139 : :
140 : : static struct tiocb*
141 : : pop_pending_tiocb(posix_aio_queue *queue)
142 : : {
143 : 0 : struct tiocb *tiocb = NULL;
144 : 0 : struct tlist *list = &queue->pending;
145 : :
146 : 0 : if (list->head == list->tail){
147 : 0 : tiocb = list->head;
148 : 0 : list->head = list->tail = NULL;
149 : : } else {
150 : 0 : tiocb = list->head;
151 : 0 : list->head = tiocb->next;
152 : : }
153 : :
154 : 0 : queue->tiocbs_pending--;
155 : : return tiocb;
156 : : }
157 : :
158 : : static void
159 : : shallow_copy_list(struct tlist *list, struct tlist *copy_to)
160 : : {
161 : 0 : copy_to->head = list->head;
162 : 0 : copy_to->tail = list->tail;
163 : : }
164 : :
165 : : static void
166 : : list_init(struct tlist *list)
167 : : {
168 : 0 : list->head = NULL;
169 : 0 : list->tail = NULL;
170 : : }
171 : :
172 : : static inline void
173 : 0 : queue_deferred_tiocb(posix_aio_queue *queue)
174 : : {
175 : 0 : struct tlist *list = &queue->deferred;
176 : :
177 : 0 : if (list->head) {
178 : 0 : struct tiocb *tiocb = list->head;
179 : :
180 : 0 : list->head = tiocb->next;
181 : 0 : if (!list->head)
182 : 0 : list->tail = NULL;
183 : :
184 : : queue_tiocb(queue, tiocb);
185 : 0 : queue->tiocbs_deferred--;
186 : : }
187 : 0 : }
188 : :
189 : : static inline void
190 : 0 : queue_deferred_tiocbs(posix_aio_queue *queue)
191 : : {
192 : 0 : while (!posixaio_backend_queue_full(queue) && deferred_tiocbs(queue))
193 : 0 : queue_deferred_tiocb(queue);
194 : 0 : }
195 : :
196 : : /*
197 : : * td_complete may queue more tiocbs
198 : : */
199 : : static void
200 : 0 : complete_tiocb(posix_aio_queue *queue, struct tiocb *tiocb)
201 : : {
202 : : int err;
203 : : unsigned long actual_res;
204 : 0 : struct aiocb *aiocb = &(tiocb->uiocb.aio);
205 : 0 : unsigned long res = aiocb->aio_nbytes;
206 : :
207 : : /*TO DO THIS IS WRONG IN NORMAL QUEUE*/
208 : 0 : if (res == (actual_res = aio_return(aiocb)))
209 : : err = 0;
210 : 0 : else if ((int)actual_res < 0)
211 : 0 : err = (int)res;
212 : : else
213 : : err = -EIO;
214 : :
215 : 0 : tiocb->cb(tiocb->arg, tiocb, err);
216 : 0 : }
217 : :
218 : : struct lio {
219 : : struct io_event *aio_events;
220 : :
221 : : int event_fd;
222 : : int event_id;
223 : :
224 : : int flags;
225 : : };
226 : :
227 : : #define LIO_FLAG_EVENTFD (1<<0)
228 : :
229 : : static void
230 : 0 : posixaio_backend_lio_destroy_aio(posix_aio_queue *queue)
231 : : {
232 : 0 : struct lio *lio = queue->tio_data;
233 : :
234 : 0 : if (lio->event_fd >= 0) {
235 : 0 : close(lio->event_fd);
236 : 0 : lio->event_fd = -1;
237 : : }
238 : 0 : }
239 : :
240 : : static int
241 : 0 : __lio_setup_aio_eventfd(posix_aio_queue *queue, int qlen)
242 : : {
243 : 0 : struct lio *lio = queue->tio_data;
244 : : sigset_t mask;
245 : :
246 : 0 : sigemptyset(&mask);
247 : 0 : sigaddset(&mask, IO_SIGNAL);
248 : :
249 : 0 : if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
250 : 0 : return -errno;
251 : :
252 : 0 : lio->event_fd = signalfd(-1, &mask, 0);
253 : 0 : if (lio->event_fd == -1)
254 : 0 : return -errno;
255 : :
256 : : return 0;
257 : : }
258 : :
259 : : static int
260 : 0 : posixaio_backend_lio_setup_aio(posix_aio_queue *queue, int qlen)
261 : : {
262 : 0 : struct lio *lio = queue->tio_data;
263 : 0 : int err = 0;
264 : :
265 : 0 : lio->event_fd = -1;
266 : :
267 : 0 : err = __lio_setup_aio_eventfd(queue, qlen);
268 : :
269 : 0 : return err;
270 : : }
271 : :
272 : : static void
273 : 0 : posixaio_backend_lio_destroy(posix_aio_queue *queue)
274 : : {
275 : 0 : struct lio *lio = queue->tio_data;
276 : :
277 : 0 : if (!lio)
278 : 0 : return;
279 : :
280 : 0 : if (lio->event_id >= 0) {
281 : 0 : tapdisk_server_unregister_event(lio->event_id);
282 : 0 : lio->event_id = -1;
283 : : }
284 : :
285 : 0 : posixaio_backend_lio_destroy_aio(queue);
286 : :
287 : 0 : if (lio->aio_events) {
288 : 0 : free(lio->aio_events);
289 : 0 : lio->aio_events = NULL;
290 : : }
291 : : }
292 : :
293 : : static void
294 : 0 : posixaio_backend_lio_event(event_id_t id, char mode, void *private)
295 : : {
296 : 0 : posix_aio_queue *queue = private;
297 : : struct tiocb *tiocb;
298 : : struct tlist list;
299 : 0 : int tiocbs_pending = 0;
300 : :
301 : : list_init(&list);
302 : :
303 : 0 : while((tiocb = pop_pending_tiocb(queue))) {
304 : 0 : if ( EINPROGRESS != aio_error(&(tiocb->uiocb.aio))) {
305 : 0 : complete_tiocb(queue, tiocb);
306 : : } else {
307 : : push_list(&list, tiocb);
308 : 0 : tiocbs_pending++;
309 : : }
310 : : }
311 : :
312 : 0 : shallow_copy_list(&list, &queue->pending);
313 : 0 : queue->tiocbs_pending = tiocbs_pending;
314 : :
315 : 0 : queue_deferred_tiocbs(queue);
316 : 0 : }
317 : :
318 : : static int
319 : 0 : posixaio_backend_lio_setup(posix_aio_queue *queue, int qlen)
320 : : {
321 : 0 : WARN("posixaio_backend_lio_setup");
322 : 0 : struct lio *lio = queue->tio_data;
323 : : int err;
324 : :
325 : 0 : lio->event_id = -1;
326 : :
327 : 0 : err = posixaio_backend_lio_setup_aio(queue, qlen);
328 : 0 : if (err)
329 : : goto fail;
330 : :
331 : 0 : lio->event_id =
332 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
333 : 0 : lio->event_fd, TV_ZERO,
334 : : posixaio_backend_lio_event,
335 : : queue);
336 : 0 : err = lio->event_id;
337 : 0 : if (err < 0)
338 : : goto fail;
339 : :
340 : 0 : lio->aio_events = calloc(qlen, sizeof(struct io_event));
341 : 0 : if (!lio->aio_events) {
342 : 0 : err = -errno;
343 : 0 : goto fail;
344 : : }
345 : :
346 : : return 0;
347 : :
348 : : fail:
349 : 0 : posixaio_backend_lio_destroy(queue);
350 : 0 : return err;
351 : : }
352 : :
353 : : static int
354 : 0 : posixaio_backend_lio_submit(posix_aio_queue *queue)
355 : : {
356 : 0 : int j, err = 0, queued = queue->queued;
357 : 0 : struct aiocb **aiocbList = queue->aiocbList;
358 : 0 : struct tiocb **tiocbList = queue->tiocbList;
359 : 0 : if(queued == 0)
360 : : return 0;
361 : :
362 : 0 : for(j = 0; j < queue->queued; j++)
363 : : {
364 : 0 : pending_tiocb(queue, tiocbList[j]);
365 : : }
366 : :
367 : 0 : ASSERT(queue->pending.tail == tiocbList[queued-1])
368 : 0 : err = lio_listio(LIO_NOWAIT, aiocbList, queued, NULL);
369 : :
370 : 0 : if (err) {
371 : 0 : for(j = 0; j < queue->queued; j++)
372 : : {
373 : 0 : aio_cancel(aiocbList[j]->aio_fildes, aiocbList[j]);
374 : : }
375 : : }
376 : :
377 : 0 : queue->queued = 0;
378 : :
379 : 0 : return queued;
380 : : }
381 : :
382 : : static const struct tio td_tio_lio = {
383 : : .name = "lio",
384 : : .data_size = sizeof(struct lio),
385 : : .tio_setup = posixaio_backend_lio_setup,
386 : : .tio_destroy = posixaio_backend_lio_destroy,
387 : : .tio_submit = posixaio_backend_lio_submit,
388 : : };
389 : :
390 : : static void
391 : 0 : posixaio_backend_queue_free_io(posix_aio_queue *queue)
392 : : {
393 : 0 : if (queue->tio) {
394 : 0 : if (queue->tio->tio_destroy)
395 : 0 : queue->tio->tio_destroy(queue);
396 : 0 : queue->tio = NULL;
397 : : }
398 : :
399 : 0 : if (queue->tio_data) {
400 : 0 : free(queue->tio_data);
401 : 0 : queue->tio_data = NULL;
402 : : }
403 : 0 : }
404 : :
405 : : static int
406 : 0 : posixaio_backend_queue_init_io(posix_aio_queue *queue, int drv)
407 : : {
408 : : const struct tio *tio;
409 : : int err;
410 : :
411 : 0 : switch (drv) {
412 : : case TIO_DRV_LIO:
413 : 0 : tio = &td_tio_lio;
414 : : break;
415 : : default:
416 : : err = -EINVAL;
417 : : goto fail;
418 : : }
419 : :
420 : 0 : queue->tio_data = calloc(1, tio->data_size);
421 : 0 : if (!queue->tio_data) {
422 : 0 : PERROR("malloc(%zu)", tio->data_size);
423 : 0 : err = -errno;
424 : 0 : goto fail;
425 : : }
426 : :
427 : 0 : queue->tio = tio;
428 : :
429 : : if (tio->tio_setup) {
430 : 0 : err = tio->tio_setup(queue, queue->size);
431 : 0 : if (err)
432 : : goto fail;
433 : : }
434 : :
435 : 0 : queue->tiocbs_pending = 0;
436 : 0 : list_init(&queue->pending);
437 : :
438 : 0 : queue->tiocbs_deferred = 0;
439 : 0 : list_init(&queue->deferred);
440 : :
441 : :
442 : 0 : DPRINTF("I/O queue driver: %s\n", tio->name);
443 : :
444 : 0 : return 0;
445 : :
446 : : fail:
447 : 0 : posixaio_backend_queue_free_io(queue);
448 : 0 : return err;
449 : : }
450 : :
451 : : static void
452 : 0 : posixaio_backend_free_queue(tqueue* pqueue)
453 : : {
454 : 0 : posix_aio_queue* queue = (posix_aio_queue*)*pqueue;
455 : 0 : posixaio_backend_queue_free_io(queue);
456 : :
457 : 0 : free(queue->aiocbList);
458 : 0 : queue->aiocbList = NULL;
459 : 0 : free(queue->tiocbList);
460 : 0 : queue->tiocbList = NULL;
461 : 0 : free(queue);
462 : 0 : *pqueue = NULL;
463 : 0 : }
464 : :
465 : : static int
466 : 0 : posixaio_backend_init_queue(tqueue *pqueue, int size,
467 : : int drv, struct tfilter *filter)
468 : : {
469 : : int err;
470 : 0 : *pqueue = (tqueue)malloc(sizeof(posix_aio_queue));
471 : 0 : posix_aio_queue *queue = *pqueue;
472 : 0 : if(queue == NULL)
473 : : return ENOMEM;
474 : :
475 : : memset(queue, 0, sizeof(posix_aio_queue));
476 : :
477 : 0 : queue->size = size;
478 : :
479 : 0 : if (!size)
480 : : return 0;
481 : :
482 : 0 : err = posixaio_backend_queue_init_io(queue, drv);
483 : 0 : if (err){
484 : 0 : WARN("error from posixaio_backend_queue_init_io\n");
485 : 0 : goto fail;
486 : : }
487 : :
488 : 0 : queue->aiocbList = calloc(size, sizeof(struct aiocb*));
489 : 0 : if (!queue->aiocbList) {
490 : 0 : WARN("could not alloc aiocblist\n");
491 : 0 : err = -errno;
492 : 0 : goto fail;
493 : : }
494 : 0 : queue->tiocbList = calloc(size, sizeof(struct tiocb*));
495 : 0 : if (!queue->tiocbList) {
496 : 0 : WARN("could not alloc tiocblist\n");
497 : 0 : err = -errno;
498 : 0 : goto fail;
499 : : }
500 : :
501 : : return 0;
502 : :
503 : : fail:
504 : 0 : posixaio_backend_free_queue(pqueue);
505 : 0 : return err;
506 : : }
507 : :
508 : : static void
509 : 0 : posixaio_backend_debug_queue(tqueue q)
510 : : {
511 : 0 : posix_aio_queue* queue = (posix_aio_queue*)q;
512 : 0 : struct tiocb *tiocb = queue->deferred.head;
513 : :
514 : 0 : WARN("POSIX AIO QUEUE:\n");
515 : 0 : WARN("size: %d, queued: %d, "
516 : : "tiocbs_pending: %d, tiocbs_deferred: %d, deferrals: %"PRIx64"\n",
517 : : queue->size, queue->queued,
518 : : queue->tiocbs_pending, queue->tiocbs_deferred, queue->deferrals);
519 : :
520 : 0 : if (tiocb) {
521 : 0 : WARN("deferred:\n");
522 : 0 : for (; tiocb != NULL; tiocb = tiocb->next) {
523 : 0 : struct aiocb *aiocb = &(tiocb->uiocb.aio);
524 : 0 : char* op = aiocb->aio_lio_opcode == LIO_WRITE ? "read" : "write";
525 : 0 : WARN("%s of %lu bytes at %jd\n",
526 : : op, aiocb->aio_nbytes,
527 : : aiocb->aio_offset);
528 : : }
529 : : }
530 : 0 : }
531 : :
532 : : void
533 : 0 : posixaio_backend_prep_tiocb(struct tiocb *tiocb, int fd, int rw, char *buf, size_t size,
534 : : long long offset, td_queue_callback_t cb, void *arg)
535 : : {
536 : 0 : struct aiocb *aiocb = &(tiocb->uiocb.aio);
537 : :
538 : 0 : aiocb->aio_fildes = fd;
539 : 0 : aiocb->aio_buf = buf;
540 : 0 : aiocb->aio_nbytes = size;
541 : 0 : aiocb->aio_reqprio = 0;
542 : 0 : aiocb->aio_offset = offset;
543 : 0 : aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
544 : 0 : aiocb->aio_sigevent.sigev_signo = IO_SIGNAL;
545 : 0 : aiocb->aio_sigevent.sigev_value.sival_ptr = NULL;
546 : :
547 : 0 : aiocb->aio_lio_opcode = rw ? LIO_WRITE : LIO_READ;
548 : :
549 : 0 : tiocb->cb = cb;
550 : 0 : tiocb->arg = arg;
551 : 0 : tiocb->next = NULL;
552 : 0 : }
553 : :
554 : : static void
555 : 0 : posixaio_backend_queue_tiocb(tqueue q, struct tiocb *tiocb)
556 : : {
557 : 0 : posix_aio_queue* queue = (posix_aio_queue*)q;
558 : 0 : if (!posixaio_backend_queue_full(queue))
559 : : queue_tiocb(queue, tiocb);
560 : : else
561 : : defer_tiocb(queue, tiocb);
562 : 0 : }
563 : :
564 : : static int
565 : 0 : posixaio_backend_submit_tiocbs(tqueue q)
566 : : {
567 : 0 : posix_aio_queue* queue = (posix_aio_queue*)q;
568 : 0 : return queue->tio->tio_submit(queue);
569 : : }
570 : :
571 : : static int
572 : 0 : posixaio_backend_submit_all_tiocbs(tqueue q)
573 : : {
574 : 0 : posix_aio_queue* queue = (posix_aio_queue*)q;
575 : 0 : int submitted = 0;
576 : :
577 : : do {
578 : 0 : submitted += posixaio_backend_submit_tiocbs(queue);
579 : 0 : } while (!posixaio_backend_queue_empty(queue));
580 : :
581 : 0 : return submitted;
582 : : }
583 : :
584 : 0 : struct backend* get_posix_aio_backend()
585 : : {
586 : : static struct backend posix_aio_backend = {
587 : : .debug=posixaio_backend_debug_queue,
588 : : .init=posixaio_backend_init_queue,
589 : : .free_queue=posixaio_backend_free_queue,
590 : : .queue=posixaio_backend_queue_tiocb,
591 : : .submit_all=posixaio_backend_submit_all_tiocbs,
592 : : .submit_tiocbs=posixaio_backend_submit_tiocbs,
593 : : .prep=posixaio_backend_prep_tiocb
594 : : };
595 : 0 : return &posix_aio_backend;
596 : : }
|