Merge branch 'adjusting-libpmem' of https://github.com/lukaszstolarczuk/fio into...
[fio.git] / engines / io_uring.c
1 /*
2  * io_uring engine
3  *
4  * IO engine using the new native Linux aio io_uring interface. See:
5  *
6  * http://git.kernel.dk/cgit/linux-block/log/?h=io_uring
7  *
8  */
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <sys/time.h>
13 #include <sys/resource.h>
14
15 #include "../fio.h"
16 #include "../lib/pow2.h"
17 #include "../optgroup.h"
18 #include "../lib/memalign.h"
19 #include "../lib/fls.h"
20 #include "../lib/roundup.h"
21
22 #ifdef ARCH_HAVE_IOURING
23
24 #include "../lib/types.h"
25 #include "../os/linux/io_uring.h"
26
27 struct io_sq_ring {
28         unsigned *head;
29         unsigned *tail;
30         unsigned *ring_mask;
31         unsigned *ring_entries;
32         unsigned *flags;
33         unsigned *array;
34 };
35
36 struct io_cq_ring {
37         unsigned *head;
38         unsigned *tail;
39         unsigned *ring_mask;
40         unsigned *ring_entries;
41         struct io_uring_cqe *cqes;
42 };
43
44 struct ioring_mmap {
45         void *ptr;
46         size_t len;
47 };
48
49 struct ioring_data {
50         int ring_fd;
51
52         struct io_u **io_u_index;
53
54         int *fds;
55
56         struct io_sq_ring sq_ring;
57         struct io_uring_sqe *sqes;
58         struct iovec *iovecs;
59         unsigned sq_ring_mask;
60
61         struct io_cq_ring cq_ring;
62         unsigned cq_ring_mask;
63
64         int queued;
65         int cq_ring_off;
66         unsigned iodepth;
67         bool ioprio_class_set;
68         bool ioprio_set;
69
70         struct ioring_mmap mmap[3];
71 };
72
73 struct ioring_options {
74         void *pad;
75         unsigned int hipri;
76         unsigned int cmdprio_percentage;
77         unsigned int fixedbufs;
78         unsigned int registerfiles;
79         unsigned int sqpoll_thread;
80         unsigned int sqpoll_set;
81         unsigned int sqpoll_cpu;
82         unsigned int nonvectored;
83         unsigned int uncached;
84         unsigned int nowait;
85 };
86
87 static const int ddir_to_op[2][2] = {
88         { IORING_OP_READV, IORING_OP_READ },
89         { IORING_OP_WRITEV, IORING_OP_WRITE }
90 };
91
92 static const int fixed_ddir_to_op[2] = {
93         IORING_OP_READ_FIXED,
94         IORING_OP_WRITE_FIXED
95 };
96
97 static int fio_ioring_sqpoll_cb(void *data, unsigned long long *val)
98 {
99         struct ioring_options *o = data;
100
101         o->sqpoll_cpu = *val;
102         o->sqpoll_set = 1;
103         return 0;
104 }
105
106 static struct fio_option options[] = {
107         {
108                 .name   = "hipri",
109                 .lname  = "High Priority",
110                 .type   = FIO_OPT_STR_SET,
111                 .off1   = offsetof(struct ioring_options, hipri),
112                 .help   = "Use polled IO completions",
113                 .category = FIO_OPT_C_ENGINE,
114                 .group  = FIO_OPT_G_IOURING,
115         },
116 #ifdef FIO_HAVE_IOPRIO_CLASS
117         {
118                 .name   = "cmdprio_percentage",
119                 .lname  = "high priority percentage",
120                 .type   = FIO_OPT_INT,
121                 .off1   = offsetof(struct ioring_options, cmdprio_percentage),
122                 .minval = 1,
123                 .maxval = 100,
124                 .help   = "Send high priority I/O this percentage of the time",
125                 .category = FIO_OPT_C_ENGINE,
126                 .group  = FIO_OPT_G_IOURING,
127         },
128 #else
129         {
130                 .name   = "cmdprio_percentage",
131                 .lname  = "high priority percentage",
132                 .type   = FIO_OPT_UNSUPPORTED,
133                 .help   = "Your platform does not support I/O priority classes",
134         },
135 #endif
136         {
137                 .name   = "fixedbufs",
138                 .lname  = "Fixed (pre-mapped) IO buffers",
139                 .type   = FIO_OPT_STR_SET,
140                 .off1   = offsetof(struct ioring_options, fixedbufs),
141                 .help   = "Pre map IO buffers",
142                 .category = FIO_OPT_C_ENGINE,
143                 .group  = FIO_OPT_G_IOURING,
144         },
145         {
146                 .name   = "registerfiles",
147                 .lname  = "Register file set",
148                 .type   = FIO_OPT_STR_SET,
149                 .off1   = offsetof(struct ioring_options, registerfiles),
150                 .help   = "Pre-open/register files",
151                 .category = FIO_OPT_C_ENGINE,
152                 .group  = FIO_OPT_G_IOURING,
153         },
154         {
155                 .name   = "sqthread_poll",
156                 .lname  = "Kernel SQ thread polling",
157                 .type   = FIO_OPT_INT,
158                 .off1   = offsetof(struct ioring_options, sqpoll_thread),
159                 .help   = "Offload submission/completion to kernel thread",
160                 .category = FIO_OPT_C_ENGINE,
161                 .group  = FIO_OPT_G_IOURING,
162         },
163         {
164                 .name   = "sqthread_poll_cpu",
165                 .lname  = "SQ Thread Poll CPU",
166                 .type   = FIO_OPT_INT,
167                 .cb     = fio_ioring_sqpoll_cb,
168                 .help   = "What CPU to run SQ thread polling on",
169                 .category = FIO_OPT_C_ENGINE,
170                 .group  = FIO_OPT_G_IOURING,
171         },
172         {
173                 .name   = "nonvectored",
174                 .lname  = "Non-vectored",
175                 .type   = FIO_OPT_INT,
176                 .off1   = offsetof(struct ioring_options, nonvectored),
177                 .help   = "Use non-vectored read/write commands",
178                 .category = FIO_OPT_C_ENGINE,
179                 .group  = FIO_OPT_G_IOURING,
180         },
181         {
182                 .name   = "uncached",
183                 .lname  = "Uncached",
184                 .type   = FIO_OPT_INT,
185                 .off1   = offsetof(struct ioring_options, uncached),
186                 .help   = "Use RWF_UNCACHED for buffered read/writes",
187                 .category = FIO_OPT_C_ENGINE,
188                 .group  = FIO_OPT_G_IOURING,
189         },
190         {
191                 .name   = "nowait",
192                 .lname  = "RWF_NOWAIT",
193                 .type   = FIO_OPT_BOOL,
194                 .off1   = offsetof(struct ioring_options, nowait),
195                 .help   = "Use RWF_NOWAIT for reads/writes",
196                 .category = FIO_OPT_C_ENGINE,
197                 .group  = FIO_OPT_G_IOURING,
198         },
199         {
200                 .name   = NULL,
201         },
202 };
203
204 static int io_uring_enter(struct ioring_data *ld, unsigned int to_submit,
205                          unsigned int min_complete, unsigned int flags)
206 {
207         return syscall(__NR_io_uring_enter, ld->ring_fd, to_submit,
208                         min_complete, flags, NULL, 0);
209 }
210
211 static int fio_ioring_prep(struct thread_data *td, struct io_u *io_u)
212 {
213         struct ioring_data *ld = td->io_ops_data;
214         struct ioring_options *o = td->eo;
215         struct fio_file *f = io_u->file;
216         struct io_uring_sqe *sqe;
217
218         sqe = &ld->sqes[io_u->index];
219
220         /* zero out fields not used in this submission */
221         memset(sqe, 0, sizeof(*sqe));
222
223         if (o->registerfiles) {
224                 sqe->fd = f->engine_pos;
225                 sqe->flags = IOSQE_FIXED_FILE;
226         } else {
227                 sqe->fd = f->fd;
228         }
229
230         if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
231                 if (o->fixedbufs) {
232                         sqe->opcode = fixed_ddir_to_op[io_u->ddir];
233                         sqe->addr = (unsigned long) io_u->xfer_buf;
234                         sqe->len = io_u->xfer_buflen;
235                         sqe->buf_index = io_u->index;
236                 } else {
237                         struct iovec *iov = &ld->iovecs[io_u->index];
238
239                         /*
240                          * Update based on actual io_u, requeue could have
241                          * adjusted these
242                          */
243                         iov->iov_base = io_u->xfer_buf;
244                         iov->iov_len = io_u->xfer_buflen;
245
246                         sqe->opcode = ddir_to_op[io_u->ddir][!!o->nonvectored];
247                         if (o->nonvectored) {
248                                 sqe->addr = (unsigned long) iov->iov_base;
249                                 sqe->len = iov->iov_len;
250                         } else {
251                                 sqe->addr = (unsigned long) iov;
252                                 sqe->len = 1;
253                         }
254                 }
255                 if (!td->o.odirect && o->uncached)
256                         sqe->rw_flags = RWF_UNCACHED;
257                 if (o->nowait)
258                         sqe->rw_flags |= RWF_NOWAIT;
259                 if (ld->ioprio_class_set)
260                         sqe->ioprio = td->o.ioprio_class << 13;
261                 if (ld->ioprio_set)
262                         sqe->ioprio |= td->o.ioprio;
263                 sqe->off = io_u->offset;
264         } else if (ddir_sync(io_u->ddir)) {
265                 if (io_u->ddir == DDIR_SYNC_FILE_RANGE) {
266                         sqe->off = f->first_write;
267                         sqe->len = f->last_write - f->first_write;
268                         sqe->sync_range_flags = td->o.sync_file_range;
269                         sqe->opcode = IORING_OP_SYNC_FILE_RANGE;
270                 } else {
271                         if (io_u->ddir == DDIR_DATASYNC)
272                                 sqe->fsync_flags |= IORING_FSYNC_DATASYNC;
273                         sqe->opcode = IORING_OP_FSYNC;
274                 }
275         }
276
277         sqe->user_data = (unsigned long) io_u;
278         return 0;
279 }
280
281 static struct io_u *fio_ioring_event(struct thread_data *td, int event)
282 {
283         struct ioring_data *ld = td->io_ops_data;
284         struct io_uring_cqe *cqe;
285         struct io_u *io_u;
286         unsigned index;
287
288         index = (event + ld->cq_ring_off) & ld->cq_ring_mask;
289
290         cqe = &ld->cq_ring.cqes[index];
291         io_u = (struct io_u *) (uintptr_t) cqe->user_data;
292
293         if (cqe->res != io_u->xfer_buflen) {
294                 if (cqe->res > io_u->xfer_buflen)
295                         io_u->error = -cqe->res;
296                 else
297                         io_u->resid = io_u->xfer_buflen - cqe->res;
298         } else
299                 io_u->error = 0;
300
301         return io_u;
302 }
303
304 static int fio_ioring_cqring_reap(struct thread_data *td, unsigned int events,
305                                    unsigned int max)
306 {
307         struct ioring_data *ld = td->io_ops_data;
308         struct io_cq_ring *ring = &ld->cq_ring;
309         unsigned head, reaped = 0;
310
311         head = *ring->head;
312         do {
313                 if (head == atomic_load_acquire(ring->tail))
314                         break;
315                 reaped++;
316                 head++;
317         } while (reaped + events < max);
318
319         if (reaped)
320                 atomic_store_release(ring->head, head);
321
322         return reaped;
323 }
324
325 static int fio_ioring_getevents(struct thread_data *td, unsigned int min,
326                                 unsigned int max, const struct timespec *t)
327 {
328         struct ioring_data *ld = td->io_ops_data;
329         unsigned actual_min = td->o.iodepth_batch_complete_min == 0 ? 0 : min;
330         struct ioring_options *o = td->eo;
331         struct io_cq_ring *ring = &ld->cq_ring;
332         unsigned events = 0;
333         int r;
334
335         ld->cq_ring_off = *ring->head;
336         do {
337                 r = fio_ioring_cqring_reap(td, events, max);
338                 if (r) {
339                         events += r;
340                         if (actual_min != 0)
341                                 actual_min -= r;
342                         continue;
343                 }
344
345                 if (!o->sqpoll_thread) {
346                         r = io_uring_enter(ld, 0, actual_min,
347                                                 IORING_ENTER_GETEVENTS);
348                         if (r < 0) {
349                                 if (errno == EAGAIN || errno == EINTR)
350                                         continue;
351                                 td_verror(td, errno, "io_uring_enter");
352                                 break;
353                         }
354                 }
355         } while (events < min);
356
357         return r < 0 ? r : events;
358 }
359
360 static void fio_ioring_prio_prep(struct thread_data *td, struct io_u *io_u)
361 {
362         struct ioring_options *o = td->eo;
363         struct ioring_data *ld = td->io_ops_data;
364         if (rand_between(&td->prio_state, 0, 99) < o->cmdprio_percentage) {
365                 ld->sqes[io_u->index].ioprio = IOPRIO_CLASS_RT << IOPRIO_CLASS_SHIFT;
366                 io_u->flags |= IO_U_F_PRIORITY;
367         }
368         return;
369 }
370
371 static enum fio_q_status fio_ioring_queue(struct thread_data *td,
372                                           struct io_u *io_u)
373 {
374         struct ioring_data *ld = td->io_ops_data;
375         struct io_sq_ring *ring = &ld->sq_ring;
376         struct ioring_options *o = td->eo;
377         unsigned tail, next_tail;
378
379         fio_ro_check(td, io_u);
380
381         if (ld->queued == ld->iodepth)
382                 return FIO_Q_BUSY;
383
384         if (io_u->ddir == DDIR_TRIM) {
385                 if (ld->queued)
386                         return FIO_Q_BUSY;
387
388                 do_io_u_trim(td, io_u);
389                 io_u_mark_submit(td, 1);
390                 io_u_mark_complete(td, 1);
391                 return FIO_Q_COMPLETED;
392         }
393
394         tail = *ring->tail;
395         next_tail = tail + 1;
396         if (next_tail == atomic_load_acquire(ring->head))
397                 return FIO_Q_BUSY;
398
399         if (o->cmdprio_percentage)
400                 fio_ioring_prio_prep(td, io_u);
401         ring->array[tail & ld->sq_ring_mask] = io_u->index;
402         atomic_store_release(ring->tail, next_tail);
403
404         ld->queued++;
405         return FIO_Q_QUEUED;
406 }
407
408 static void fio_ioring_queued(struct thread_data *td, int start, int nr)
409 {
410         struct ioring_data *ld = td->io_ops_data;
411         struct timespec now;
412
413         if (!fio_fill_issue_time(td))
414                 return;
415
416         fio_gettime(&now, NULL);
417
418         while (nr--) {
419                 struct io_sq_ring *ring = &ld->sq_ring;
420                 int index = ring->array[start & ld->sq_ring_mask];
421                 struct io_u *io_u = ld->io_u_index[index];
422
423                 memcpy(&io_u->issue_time, &now, sizeof(now));
424                 io_u_queued(td, io_u);
425
426                 start++;
427         }
428 }
429
430 static int fio_ioring_commit(struct thread_data *td)
431 {
432         struct ioring_data *ld = td->io_ops_data;
433         struct ioring_options *o = td->eo;
434         int ret;
435
436         if (!ld->queued)
437                 return 0;
438
439         /*
440          * Kernel side does submission. just need to check if the ring is
441          * flagged as needing a kick, if so, call io_uring_enter(). This
442          * only happens if we've been idle too long.
443          */
444         if (o->sqpoll_thread) {
445                 struct io_sq_ring *ring = &ld->sq_ring;
446
447                 read_barrier();
448                 if (*ring->flags & IORING_SQ_NEED_WAKEUP)
449                         io_uring_enter(ld, ld->queued, 0,
450                                         IORING_ENTER_SQ_WAKEUP);
451                 ld->queued = 0;
452                 return 0;
453         }
454
455         do {
456                 unsigned start = *ld->sq_ring.head;
457                 long nr = ld->queued;
458
459                 ret = io_uring_enter(ld, nr, 0, IORING_ENTER_GETEVENTS);
460                 if (ret > 0) {
461                         fio_ioring_queued(td, start, ret);
462                         io_u_mark_submit(td, ret);
463
464                         ld->queued -= ret;
465                         ret = 0;
466                 } else if (!ret) {
467                         io_u_mark_submit(td, ret);
468                         continue;
469                 } else {
470                         if (errno == EAGAIN || errno == EINTR) {
471                                 ret = fio_ioring_cqring_reap(td, 0, ld->queued);
472                                 if (ret)
473                                         continue;
474                                 /* Shouldn't happen */
475                                 usleep(1);
476                                 continue;
477                         }
478                         td_verror(td, errno, "io_uring_enter submit");
479                         break;
480                 }
481         } while (ld->queued);
482
483         return ret;
484 }
485
486 static void fio_ioring_unmap(struct ioring_data *ld)
487 {
488         int i;
489
490         for (i = 0; i < ARRAY_SIZE(ld->mmap); i++)
491                 munmap(ld->mmap[i].ptr, ld->mmap[i].len);
492         close(ld->ring_fd);
493 }
494
495 static void fio_ioring_cleanup(struct thread_data *td)
496 {
497         struct ioring_data *ld = td->io_ops_data;
498
499         if (ld) {
500                 if (!(td->flags & TD_F_CHILD))
501                         fio_ioring_unmap(ld);
502
503                 free(ld->io_u_index);
504                 free(ld->iovecs);
505                 free(ld->fds);
506                 free(ld);
507         }
508 }
509
510 static int fio_ioring_mmap(struct ioring_data *ld, struct io_uring_params *p)
511 {
512         struct io_sq_ring *sring = &ld->sq_ring;
513         struct io_cq_ring *cring = &ld->cq_ring;
514         void *ptr;
515
516         ld->mmap[0].len = p->sq_off.array + p->sq_entries * sizeof(__u32);
517         ptr = mmap(0, ld->mmap[0].len, PROT_READ | PROT_WRITE,
518                         MAP_SHARED | MAP_POPULATE, ld->ring_fd,
519                         IORING_OFF_SQ_RING);
520         ld->mmap[0].ptr = ptr;
521         sring->head = ptr + p->sq_off.head;
522         sring->tail = ptr + p->sq_off.tail;
523         sring->ring_mask = ptr + p->sq_off.ring_mask;
524         sring->ring_entries = ptr + p->sq_off.ring_entries;
525         sring->flags = ptr + p->sq_off.flags;
526         sring->array = ptr + p->sq_off.array;
527         ld->sq_ring_mask = *sring->ring_mask;
528
529         ld->mmap[1].len = p->sq_entries * sizeof(struct io_uring_sqe);
530         ld->sqes = mmap(0, ld->mmap[1].len, PROT_READ | PROT_WRITE,
531                                 MAP_SHARED | MAP_POPULATE, ld->ring_fd,
532                                 IORING_OFF_SQES);
533         ld->mmap[1].ptr = ld->sqes;
534
535         ld->mmap[2].len = p->cq_off.cqes +
536                                 p->cq_entries * sizeof(struct io_uring_cqe);
537         ptr = mmap(0, ld->mmap[2].len, PROT_READ | PROT_WRITE,
538                         MAP_SHARED | MAP_POPULATE, ld->ring_fd,
539                         IORING_OFF_CQ_RING);
540         ld->mmap[2].ptr = ptr;
541         cring->head = ptr + p->cq_off.head;
542         cring->tail = ptr + p->cq_off.tail;
543         cring->ring_mask = ptr + p->cq_off.ring_mask;
544         cring->ring_entries = ptr + p->cq_off.ring_entries;
545         cring->cqes = ptr + p->cq_off.cqes;
546         ld->cq_ring_mask = *cring->ring_mask;
547         return 0;
548 }
549
550 static int fio_ioring_queue_init(struct thread_data *td)
551 {
552         struct ioring_data *ld = td->io_ops_data;
553         struct ioring_options *o = td->eo;
554         int depth = td->o.iodepth;
555         struct io_uring_params p;
556         int ret;
557
558         memset(&p, 0, sizeof(p));
559
560         if (o->hipri)
561                 p.flags |= IORING_SETUP_IOPOLL;
562         if (o->sqpoll_thread) {
563                 p.flags |= IORING_SETUP_SQPOLL;
564                 if (o->sqpoll_set) {
565                         p.flags |= IORING_SETUP_SQ_AFF;
566                         p.sq_thread_cpu = o->sqpoll_cpu;
567                 }
568         }
569
570         ret = syscall(__NR_io_uring_setup, depth, &p);
571         if (ret < 0)
572                 return ret;
573
574         ld->ring_fd = ret;
575
576         if (o->fixedbufs) {
577                 struct rlimit rlim = {
578                         .rlim_cur = RLIM_INFINITY,
579                         .rlim_max = RLIM_INFINITY,
580                 };
581
582                 if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
583                         return -1;
584
585                 ret = syscall(__NR_io_uring_register, ld->ring_fd,
586                                 IORING_REGISTER_BUFFERS, ld->iovecs, depth);
587                 if (ret < 0)
588                         return ret;
589         }
590
591         return fio_ioring_mmap(ld, &p);
592 }
593
594 static int fio_ioring_register_files(struct thread_data *td)
595 {
596         struct ioring_data *ld = td->io_ops_data;
597         struct fio_file *f;
598         unsigned int i;
599         int ret;
600
601         ld->fds = calloc(td->o.nr_files, sizeof(int));
602
603         for_each_file(td, f, i) {
604                 ret = generic_open_file(td, f);
605                 if (ret)
606                         goto err;
607                 ld->fds[i] = f->fd;
608                 f->engine_pos = i;
609         }
610
611         ret = syscall(__NR_io_uring_register, ld->ring_fd,
612                         IORING_REGISTER_FILES, ld->fds, td->o.nr_files);
613         if (ret) {
614 err:
615                 free(ld->fds);
616                 ld->fds = NULL;
617         }
618
619         /*
620          * Pretend the file is closed again, and really close it if we hit
621          * an error.
622          */
623         for_each_file(td, f, i) {
624                 if (ret) {
625                         int fio_unused ret2;
626                         ret2 = generic_close_file(td, f);
627                 } else
628                         f->fd = -1;
629         }
630
631         return ret;
632 }
633
634 static int fio_ioring_post_init(struct thread_data *td)
635 {
636         struct ioring_data *ld = td->io_ops_data;
637         struct ioring_options *o = td->eo;
638         struct io_u *io_u;
639         int err, i;
640
641         for (i = 0; i < td->o.iodepth; i++) {
642                 struct iovec *iov = &ld->iovecs[i];
643
644                 io_u = ld->io_u_index[i];
645                 iov->iov_base = io_u->buf;
646                 iov->iov_len = td_max_bs(td);
647         }
648
649         err = fio_ioring_queue_init(td);
650         if (err) {
651                 td_verror(td, errno, "io_queue_init");
652                 return 1;
653         }
654
655         if (o->registerfiles) {
656                 err = fio_ioring_register_files(td);
657                 if (err) {
658                         td_verror(td, errno, "ioring_register_files");
659                         return 1;
660                 }
661         }
662
663         return 0;
664 }
665
666 static int fio_ioring_init(struct thread_data *td)
667 {
668         struct ioring_options *o = td->eo;
669         struct ioring_data *ld;
670         struct thread_options *to = &td->o;
671
672         /* sqthread submission requires registered files */
673         if (o->sqpoll_thread)
674                 o->registerfiles = 1;
675
676         if (o->registerfiles && td->o.nr_files != td->o.open_files) {
677                 log_err("fio: io_uring registered files require nr_files to "
678                         "be identical to open_files\n");
679                 return 1;
680         }
681
682         ld = calloc(1, sizeof(*ld));
683
684         /* ring depth must be a power-of-2 */
685         ld->iodepth = td->o.iodepth;
686         td->o.iodepth = roundup_pow2(td->o.iodepth);
687
688         /* io_u index */
689         ld->io_u_index = calloc(td->o.iodepth, sizeof(struct io_u *));
690         ld->iovecs = calloc(td->o.iodepth, sizeof(struct iovec));
691
692         td->io_ops_data = ld;
693
694         /*
695          * Check for option conflicts
696          */
697         if ((fio_option_is_set(to, ioprio) || fio_option_is_set(to, ioprio_class)) &&
698                         o->cmdprio_percentage != 0) {
699                 log_err("%s: cmdprio_percentage option and mutually exclusive "
700                                 "prio or prioclass option is set, exiting\n", to->name);
701                 td_verror(td, EINVAL, "fio_io_uring_init");
702                 return 1;
703         }
704
705         if (fio_option_is_set(&td->o, ioprio_class))
706                 ld->ioprio_class_set = true;
707         if (fio_option_is_set(&td->o, ioprio))
708                 ld->ioprio_set = true;
709
710         return 0;
711 }
712
713 static int fio_ioring_io_u_init(struct thread_data *td, struct io_u *io_u)
714 {
715         struct ioring_data *ld = td->io_ops_data;
716
717         ld->io_u_index[io_u->index] = io_u;
718         return 0;
719 }
720
721 static int fio_ioring_open_file(struct thread_data *td, struct fio_file *f)
722 {
723         struct ioring_data *ld = td->io_ops_data;
724         struct ioring_options *o = td->eo;
725
726         if (!ld || !o->registerfiles)
727                 return generic_open_file(td, f);
728
729         f->fd = ld->fds[f->engine_pos];
730         return 0;
731 }
732
733 static int fio_ioring_close_file(struct thread_data *td, struct fio_file *f)
734 {
735         struct ioring_data *ld = td->io_ops_data;
736         struct ioring_options *o = td->eo;
737
738         if (!ld || !o->registerfiles)
739                 return generic_close_file(td, f);
740
741         f->fd = -1;
742         return 0;
743 }
744
745 static struct ioengine_ops ioengine = {
746         .name                   = "io_uring",
747         .version                = FIO_IOOPS_VERSION,
748         .flags                  = FIO_ASYNCIO_SYNC_TRIM,
749         .init                   = fio_ioring_init,
750         .post_init              = fio_ioring_post_init,
751         .io_u_init              = fio_ioring_io_u_init,
752         .prep                   = fio_ioring_prep,
753         .queue                  = fio_ioring_queue,
754         .commit                 = fio_ioring_commit,
755         .getevents              = fio_ioring_getevents,
756         .event                  = fio_ioring_event,
757         .cleanup                = fio_ioring_cleanup,
758         .open_file              = fio_ioring_open_file,
759         .close_file             = fio_ioring_close_file,
760         .get_file_size          = generic_get_file_size,
761         .options                = options,
762         .option_struct_size     = sizeof(struct ioring_options),
763 };
764
765 static void fio_init fio_ioring_register(void)
766 {
767         register_ioengine(&ioengine);
768 }
769
770 static void fio_exit fio_ioring_unregister(void)
771 {
772         unregister_ioengine(&ioengine);
773 }
774 #endif