xref: /openbmc/qemu/util/fdmon-io_uring.c (revision 87ec6f55)
173fd282eSStefan Hajnoczi /* SPDX-License-Identifier: GPL-2.0-or-later */
273fd282eSStefan Hajnoczi /*
373fd282eSStefan Hajnoczi  * Linux io_uring file descriptor monitoring
473fd282eSStefan Hajnoczi  *
573fd282eSStefan Hajnoczi  * The Linux io_uring API supports file descriptor monitoring with a few
673fd282eSStefan Hajnoczi  * advantages over existing APIs like poll(2) and epoll(7):
773fd282eSStefan Hajnoczi  *
873fd282eSStefan Hajnoczi  * 1. Userspace polling of events is possible because the completion queue (cq
973fd282eSStefan Hajnoczi  *    ring) is shared between the kernel and userspace.  This allows
1073fd282eSStefan Hajnoczi  *    applications that rely on userspace polling to also monitor file
1173fd282eSStefan Hajnoczi  *    descriptors in the same userspace polling loop.
1273fd282eSStefan Hajnoczi  *
1373fd282eSStefan Hajnoczi  * 2. Submission and completion is batched and done together in a single system
1473fd282eSStefan Hajnoczi  *    call.  This minimizes the number of system calls.
1573fd282eSStefan Hajnoczi  *
1673fd282eSStefan Hajnoczi  * 3. File descriptor monitoring is O(1) like epoll(7) so it scales better than
1773fd282eSStefan Hajnoczi  *    poll(2).
1873fd282eSStefan Hajnoczi  *
1973fd282eSStefan Hajnoczi  * 4. Nanosecond timeouts are supported so it requires fewer syscalls than
2073fd282eSStefan Hajnoczi  *    epoll(7).
2173fd282eSStefan Hajnoczi  *
2273fd282eSStefan Hajnoczi  * This code only monitors file descriptors and does not do asynchronous disk
2373fd282eSStefan Hajnoczi  * I/O.  Implementing disk I/O efficiently has other requirements and should
2473fd282eSStefan Hajnoczi  * use a separate io_uring so it does not make sense to unify the code.
2573fd282eSStefan Hajnoczi  *
2673fd282eSStefan Hajnoczi  * File descriptor monitoring is implemented using the following operations:
2773fd282eSStefan Hajnoczi  *
2873fd282eSStefan Hajnoczi  * 1. IORING_OP_POLL_ADD - adds a file descriptor to be monitored.
2973fd282eSStefan Hajnoczi  * 2. IORING_OP_POLL_REMOVE - removes a file descriptor being monitored.  When
3073fd282eSStefan Hajnoczi  *    the poll mask changes for a file descriptor it is first removed and then
3173fd282eSStefan Hajnoczi  *    re-added with the new poll mask, so this operation is also used as part
3273fd282eSStefan Hajnoczi  *    of modifying an existing monitored file descriptor.
3373fd282eSStefan Hajnoczi  * 3. IORING_OP_TIMEOUT - added every time a blocking syscall is made to wait
3473fd282eSStefan Hajnoczi  *    for events.  This operation self-cancels if another event completes
3573fd282eSStefan Hajnoczi  *    before the timeout.
3673fd282eSStefan Hajnoczi  *
3773fd282eSStefan Hajnoczi  * io_uring calls the submission queue the "sq ring" and the completion queue
3873fd282eSStefan Hajnoczi  * the "cq ring".  Ring entries are called "sqe" and "cqe", respectively.
3973fd282eSStefan Hajnoczi  *
4073fd282eSStefan Hajnoczi  * The code is structured so that sq/cq rings are only modified within
4173fd282eSStefan Hajnoczi  * fdmon_io_uring_wait().  Changes to AioHandlers are made by enqueuing them on
4273fd282eSStefan Hajnoczi  * ctx->submit_list so that fdmon_io_uring_wait() can submit IORING_OP_POLL_ADD
4373fd282eSStefan Hajnoczi  * and/or IORING_OP_POLL_REMOVE sqes for them.
4473fd282eSStefan Hajnoczi  */
4573fd282eSStefan Hajnoczi 
4673fd282eSStefan Hajnoczi #include "qemu/osdep.h"
4773fd282eSStefan Hajnoczi #include <poll.h>
4873fd282eSStefan Hajnoczi #include "qemu/rcu_queue.h"
4973fd282eSStefan Hajnoczi #include "aio-posix.h"
5073fd282eSStefan Hajnoczi 
5173fd282eSStefan Hajnoczi enum {
5273fd282eSStefan Hajnoczi     FDMON_IO_URING_ENTRIES  = 128, /* sq/cq ring size */
5373fd282eSStefan Hajnoczi 
5473fd282eSStefan Hajnoczi     /* AioHandler::flags */
5573fd282eSStefan Hajnoczi     FDMON_IO_URING_PENDING  = (1 << 0),
5673fd282eSStefan Hajnoczi     FDMON_IO_URING_ADD      = (1 << 1),
5773fd282eSStefan Hajnoczi     FDMON_IO_URING_REMOVE   = (1 << 2),
5873fd282eSStefan Hajnoczi };
5973fd282eSStefan Hajnoczi 
poll_events_from_pfd(int pfd_events)6073fd282eSStefan Hajnoczi static inline int poll_events_from_pfd(int pfd_events)
6173fd282eSStefan Hajnoczi {
6273fd282eSStefan Hajnoczi     return (pfd_events & G_IO_IN ? POLLIN : 0) |
6373fd282eSStefan Hajnoczi            (pfd_events & G_IO_OUT ? POLLOUT : 0) |
6473fd282eSStefan Hajnoczi            (pfd_events & G_IO_HUP ? POLLHUP : 0) |
6573fd282eSStefan Hajnoczi            (pfd_events & G_IO_ERR ? POLLERR : 0);
6673fd282eSStefan Hajnoczi }
6773fd282eSStefan Hajnoczi 
pfd_events_from_poll(int poll_events)6873fd282eSStefan Hajnoczi static inline int pfd_events_from_poll(int poll_events)
6973fd282eSStefan Hajnoczi {
7073fd282eSStefan Hajnoczi     return (poll_events & POLLIN ? G_IO_IN : 0) |
7173fd282eSStefan Hajnoczi            (poll_events & POLLOUT ? G_IO_OUT : 0) |
7273fd282eSStefan Hajnoczi            (poll_events & POLLHUP ? G_IO_HUP : 0) |
7373fd282eSStefan Hajnoczi            (poll_events & POLLERR ? G_IO_ERR : 0);
7473fd282eSStefan Hajnoczi }
7573fd282eSStefan Hajnoczi 
7673fd282eSStefan Hajnoczi /*
7773fd282eSStefan Hajnoczi  * Returns an sqe for submitting a request.  Only be called within
7873fd282eSStefan Hajnoczi  * fdmon_io_uring_wait().
7973fd282eSStefan Hajnoczi  */
get_sqe(AioContext * ctx)8073fd282eSStefan Hajnoczi static struct io_uring_sqe *get_sqe(AioContext *ctx)
8173fd282eSStefan Hajnoczi {
8273fd282eSStefan Hajnoczi     struct io_uring *ring = &ctx->fdmon_io_uring;
8373fd282eSStefan Hajnoczi     struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
8473fd282eSStefan Hajnoczi     int ret;
8573fd282eSStefan Hajnoczi 
8673fd282eSStefan Hajnoczi     if (likely(sqe)) {
8773fd282eSStefan Hajnoczi         return sqe;
8873fd282eSStefan Hajnoczi     }
8973fd282eSStefan Hajnoczi 
9073fd282eSStefan Hajnoczi     /* No free sqes left, submit pending sqes first */
91636b836dSStefan Hajnoczi     do {
9273fd282eSStefan Hajnoczi         ret = io_uring_submit(ring);
93636b836dSStefan Hajnoczi     } while (ret == -EINTR);
94636b836dSStefan Hajnoczi 
9573fd282eSStefan Hajnoczi     assert(ret > 1);
9673fd282eSStefan Hajnoczi     sqe = io_uring_get_sqe(ring);
9773fd282eSStefan Hajnoczi     assert(sqe);
9873fd282eSStefan Hajnoczi     return sqe;
9973fd282eSStefan Hajnoczi }
10073fd282eSStefan Hajnoczi 
10173fd282eSStefan Hajnoczi /* Atomically enqueue an AioHandler for sq ring submission */
enqueue(AioHandlerSList * head,AioHandler * node,unsigned flags)10273fd282eSStefan Hajnoczi static void enqueue(AioHandlerSList *head, AioHandler *node, unsigned flags)
10373fd282eSStefan Hajnoczi {
10473fd282eSStefan Hajnoczi     unsigned old_flags;
10573fd282eSStefan Hajnoczi 
106d73415a3SStefan Hajnoczi     old_flags = qatomic_fetch_or(&node->flags, FDMON_IO_URING_PENDING | flags);
10773fd282eSStefan Hajnoczi     if (!(old_flags & FDMON_IO_URING_PENDING)) {
10873fd282eSStefan Hajnoczi         QSLIST_INSERT_HEAD_ATOMIC(head, node, node_submitted);
10973fd282eSStefan Hajnoczi     }
11073fd282eSStefan Hajnoczi }
11173fd282eSStefan Hajnoczi 
11273fd282eSStefan Hajnoczi /* Dequeue an AioHandler for sq ring submission.  Called by fill_sq_ring(). */
dequeue(AioHandlerSList * head,unsigned * flags)11373fd282eSStefan Hajnoczi static AioHandler *dequeue(AioHandlerSList *head, unsigned *flags)
11473fd282eSStefan Hajnoczi {
11573fd282eSStefan Hajnoczi     AioHandler *node = QSLIST_FIRST(head);
11673fd282eSStefan Hajnoczi 
11773fd282eSStefan Hajnoczi     if (!node) {
11873fd282eSStefan Hajnoczi         return NULL;
11973fd282eSStefan Hajnoczi     }
12073fd282eSStefan Hajnoczi 
12173fd282eSStefan Hajnoczi     /* Doesn't need to be atomic since fill_sq_ring() moves the list */
12273fd282eSStefan Hajnoczi     QSLIST_REMOVE_HEAD(head, node_submitted);
12373fd282eSStefan Hajnoczi 
12473fd282eSStefan Hajnoczi     /*
12573fd282eSStefan Hajnoczi      * Don't clear FDMON_IO_URING_REMOVE.  It's sticky so it can serve two
12673fd282eSStefan Hajnoczi      * purposes: telling fill_sq_ring() to submit IORING_OP_POLL_REMOVE and
12773fd282eSStefan Hajnoczi      * telling process_cqe() to delete the AioHandler when its
12873fd282eSStefan Hajnoczi      * IORING_OP_POLL_ADD completes.
12973fd282eSStefan Hajnoczi      */
130d73415a3SStefan Hajnoczi     *flags = qatomic_fetch_and(&node->flags, ~(FDMON_IO_URING_PENDING |
13173fd282eSStefan Hajnoczi                                               FDMON_IO_URING_ADD));
13273fd282eSStefan Hajnoczi     return node;
13373fd282eSStefan Hajnoczi }
13473fd282eSStefan Hajnoczi 
fdmon_io_uring_update(AioContext * ctx,AioHandler * old_node,AioHandler * new_node)13573fd282eSStefan Hajnoczi static void fdmon_io_uring_update(AioContext *ctx,
13673fd282eSStefan Hajnoczi                                   AioHandler *old_node,
13773fd282eSStefan Hajnoczi                                   AioHandler *new_node)
13873fd282eSStefan Hajnoczi {
13973fd282eSStefan Hajnoczi     if (new_node) {
14073fd282eSStefan Hajnoczi         enqueue(&ctx->submit_list, new_node, FDMON_IO_URING_ADD);
14173fd282eSStefan Hajnoczi     }
14273fd282eSStefan Hajnoczi 
14373fd282eSStefan Hajnoczi     if (old_node) {
14473fd282eSStefan Hajnoczi         /*
14573fd282eSStefan Hajnoczi          * Deletion is tricky because IORING_OP_POLL_ADD and
14673fd282eSStefan Hajnoczi          * IORING_OP_POLL_REMOVE are async.  We need to wait for the original
14773fd282eSStefan Hajnoczi          * IORING_OP_POLL_ADD to complete before this handler can be freed
14873fd282eSStefan Hajnoczi          * safely.
14973fd282eSStefan Hajnoczi          *
15073fd282eSStefan Hajnoczi          * It's possible that the file descriptor becomes ready and the
15173fd282eSStefan Hajnoczi          * IORING_OP_POLL_ADD cqe is enqueued before IORING_OP_POLL_REMOVE is
15273fd282eSStefan Hajnoczi          * submitted, too.
15373fd282eSStefan Hajnoczi          *
15473fd282eSStefan Hajnoczi          * Mark this handler deleted right now but don't place it on
15573fd282eSStefan Hajnoczi          * ctx->deleted_aio_handlers yet.  Instead, manually fudge the list
15673fd282eSStefan Hajnoczi          * entry to make QLIST_IS_INSERTED() think this handler has been
15773fd282eSStefan Hajnoczi          * inserted and other code recognizes this AioHandler as deleted.
15873fd282eSStefan Hajnoczi          *
15973fd282eSStefan Hajnoczi          * Once the original IORING_OP_POLL_ADD completes we enqueue the
16073fd282eSStefan Hajnoczi          * handler on the real ctx->deleted_aio_handlers list to be freed.
16173fd282eSStefan Hajnoczi          */
16273fd282eSStefan Hajnoczi         assert(!QLIST_IS_INSERTED(old_node, node_deleted));
16373fd282eSStefan Hajnoczi         old_node->node_deleted.le_prev = &old_node->node_deleted.le_next;
16473fd282eSStefan Hajnoczi 
16573fd282eSStefan Hajnoczi         enqueue(&ctx->submit_list, old_node, FDMON_IO_URING_REMOVE);
16673fd282eSStefan Hajnoczi     }
16773fd282eSStefan Hajnoczi }
16873fd282eSStefan Hajnoczi 
add_poll_add_sqe(AioContext * ctx,AioHandler * node)16973fd282eSStefan Hajnoczi static void add_poll_add_sqe(AioContext *ctx, AioHandler *node)
17073fd282eSStefan Hajnoczi {
17173fd282eSStefan Hajnoczi     struct io_uring_sqe *sqe = get_sqe(ctx);
17273fd282eSStefan Hajnoczi     int events = poll_events_from_pfd(node->pfd.events);
17373fd282eSStefan Hajnoczi 
17473fd282eSStefan Hajnoczi     io_uring_prep_poll_add(sqe, node->pfd.fd, events);
17573fd282eSStefan Hajnoczi     io_uring_sqe_set_data(sqe, node);
17673fd282eSStefan Hajnoczi }
17773fd282eSStefan Hajnoczi 
add_poll_remove_sqe(AioContext * ctx,AioHandler * node)17873fd282eSStefan Hajnoczi static void add_poll_remove_sqe(AioContext *ctx, AioHandler *node)
17973fd282eSStefan Hajnoczi {
18073fd282eSStefan Hajnoczi     struct io_uring_sqe *sqe = get_sqe(ctx);
18173fd282eSStefan Hajnoczi 
1828a947c7aSHaiyue Wang #ifdef LIBURING_HAVE_DATA64
1838a947c7aSHaiyue Wang     io_uring_prep_poll_remove(sqe, (__u64)(uintptr_t)node);
1848a947c7aSHaiyue Wang #else
18573fd282eSStefan Hajnoczi     io_uring_prep_poll_remove(sqe, node);
1868a947c7aSHaiyue Wang #endif
187*87ec6f55SStefan Hajnoczi     io_uring_sqe_set_data(sqe, NULL);
18873fd282eSStefan Hajnoczi }
18973fd282eSStefan Hajnoczi 
19073fd282eSStefan Hajnoczi /* Add a timeout that self-cancels when another cqe becomes ready */
add_timeout_sqe(AioContext * ctx,int64_t ns)19173fd282eSStefan Hajnoczi static void add_timeout_sqe(AioContext *ctx, int64_t ns)
19273fd282eSStefan Hajnoczi {
19373fd282eSStefan Hajnoczi     struct io_uring_sqe *sqe;
19473fd282eSStefan Hajnoczi     struct __kernel_timespec ts = {
19573fd282eSStefan Hajnoczi         .tv_sec = ns / NANOSECONDS_PER_SECOND,
19673fd282eSStefan Hajnoczi         .tv_nsec = ns % NANOSECONDS_PER_SECOND,
19773fd282eSStefan Hajnoczi     };
19873fd282eSStefan Hajnoczi 
19973fd282eSStefan Hajnoczi     sqe = get_sqe(ctx);
20073fd282eSStefan Hajnoczi     io_uring_prep_timeout(sqe, &ts, 1, 0);
201*87ec6f55SStefan Hajnoczi     io_uring_sqe_set_data(sqe, NULL);
20273fd282eSStefan Hajnoczi }
20373fd282eSStefan Hajnoczi 
20473fd282eSStefan Hajnoczi /* Add sqes from ctx->submit_list for submission */
fill_sq_ring(AioContext * ctx)20573fd282eSStefan Hajnoczi static void fill_sq_ring(AioContext *ctx)
20673fd282eSStefan Hajnoczi {
20773fd282eSStefan Hajnoczi     AioHandlerSList submit_list;
20873fd282eSStefan Hajnoczi     AioHandler *node;
20973fd282eSStefan Hajnoczi     unsigned flags;
21073fd282eSStefan Hajnoczi 
21173fd282eSStefan Hajnoczi     QSLIST_MOVE_ATOMIC(&submit_list, &ctx->submit_list);
21273fd282eSStefan Hajnoczi 
21373fd282eSStefan Hajnoczi     while ((node = dequeue(&submit_list, &flags))) {
21473fd282eSStefan Hajnoczi         /* Order matters, just in case both flags were set */
21573fd282eSStefan Hajnoczi         if (flags & FDMON_IO_URING_ADD) {
21673fd282eSStefan Hajnoczi             add_poll_add_sqe(ctx, node);
21773fd282eSStefan Hajnoczi         }
21873fd282eSStefan Hajnoczi         if (flags & FDMON_IO_URING_REMOVE) {
21973fd282eSStefan Hajnoczi             add_poll_remove_sqe(ctx, node);
22073fd282eSStefan Hajnoczi         }
22173fd282eSStefan Hajnoczi     }
22273fd282eSStefan Hajnoczi }
22373fd282eSStefan Hajnoczi 
22473fd282eSStefan Hajnoczi /* Returns true if a handler became ready */
process_cqe(AioContext * ctx,AioHandlerList * ready_list,struct io_uring_cqe * cqe)22573fd282eSStefan Hajnoczi static bool process_cqe(AioContext *ctx,
22673fd282eSStefan Hajnoczi                         AioHandlerList *ready_list,
22773fd282eSStefan Hajnoczi                         struct io_uring_cqe *cqe)
22873fd282eSStefan Hajnoczi {
22973fd282eSStefan Hajnoczi     AioHandler *node = io_uring_cqe_get_data(cqe);
23073fd282eSStefan Hajnoczi     unsigned flags;
23173fd282eSStefan Hajnoczi 
23273fd282eSStefan Hajnoczi     /* poll_timeout and poll_remove have a zero user_data field */
23373fd282eSStefan Hajnoczi     if (!node) {
23473fd282eSStefan Hajnoczi         return false;
23573fd282eSStefan Hajnoczi     }
23673fd282eSStefan Hajnoczi 
23773fd282eSStefan Hajnoczi     /*
23873fd282eSStefan Hajnoczi      * Deletion can only happen when IORING_OP_POLL_ADD completes.  If we race
23973fd282eSStefan Hajnoczi      * with enqueue() here then we can safely clear the FDMON_IO_URING_REMOVE
24073fd282eSStefan Hajnoczi      * bit before IORING_OP_POLL_REMOVE is submitted.
24173fd282eSStefan Hajnoczi      */
242d73415a3SStefan Hajnoczi     flags = qatomic_fetch_and(&node->flags, ~FDMON_IO_URING_REMOVE);
24373fd282eSStefan Hajnoczi     if (flags & FDMON_IO_URING_REMOVE) {
24473fd282eSStefan Hajnoczi         QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted);
24573fd282eSStefan Hajnoczi         return false;
24673fd282eSStefan Hajnoczi     }
24773fd282eSStefan Hajnoczi 
24873fd282eSStefan Hajnoczi     aio_add_ready_handler(ready_list, node, pfd_events_from_poll(cqe->res));
24973fd282eSStefan Hajnoczi 
25073fd282eSStefan Hajnoczi     /* IORING_OP_POLL_ADD is one-shot so we must re-arm it */
25173fd282eSStefan Hajnoczi     add_poll_add_sqe(ctx, node);
25273fd282eSStefan Hajnoczi     return true;
25373fd282eSStefan Hajnoczi }
25473fd282eSStefan Hajnoczi 
process_cq_ring(AioContext * ctx,AioHandlerList * ready_list)25573fd282eSStefan Hajnoczi static int process_cq_ring(AioContext *ctx, AioHandlerList *ready_list)
25673fd282eSStefan Hajnoczi {
25773fd282eSStefan Hajnoczi     struct io_uring *ring = &ctx->fdmon_io_uring;
25873fd282eSStefan Hajnoczi     struct io_uring_cqe *cqe;
25973fd282eSStefan Hajnoczi     unsigned num_cqes = 0;
26073fd282eSStefan Hajnoczi     unsigned num_ready = 0;
26173fd282eSStefan Hajnoczi     unsigned head;
26273fd282eSStefan Hajnoczi 
26373fd282eSStefan Hajnoczi     io_uring_for_each_cqe(ring, head, cqe) {
26473fd282eSStefan Hajnoczi         if (process_cqe(ctx, ready_list, cqe)) {
26573fd282eSStefan Hajnoczi             num_ready++;
26673fd282eSStefan Hajnoczi         }
26773fd282eSStefan Hajnoczi 
26873fd282eSStefan Hajnoczi         num_cqes++;
26973fd282eSStefan Hajnoczi     }
27073fd282eSStefan Hajnoczi 
27173fd282eSStefan Hajnoczi     io_uring_cq_advance(ring, num_cqes);
27273fd282eSStefan Hajnoczi     return num_ready;
27373fd282eSStefan Hajnoczi }
27473fd282eSStefan Hajnoczi 
fdmon_io_uring_wait(AioContext * ctx,AioHandlerList * ready_list,int64_t timeout)27573fd282eSStefan Hajnoczi static int fdmon_io_uring_wait(AioContext *ctx, AioHandlerList *ready_list,
27673fd282eSStefan Hajnoczi                                int64_t timeout)
27773fd282eSStefan Hajnoczi {
27873fd282eSStefan Hajnoczi     unsigned wait_nr = 1; /* block until at least one cqe is ready */
27973fd282eSStefan Hajnoczi     int ret;
28073fd282eSStefan Hajnoczi 
28173fd282eSStefan Hajnoczi     if (timeout == 0) {
28273fd282eSStefan Hajnoczi         wait_nr = 0; /* non-blocking */
28373fd282eSStefan Hajnoczi     } else if (timeout > 0) {
28473fd282eSStefan Hajnoczi         add_timeout_sqe(ctx, timeout);
28573fd282eSStefan Hajnoczi     }
28673fd282eSStefan Hajnoczi 
28773fd282eSStefan Hajnoczi     fill_sq_ring(ctx);
28873fd282eSStefan Hajnoczi 
289636b836dSStefan Hajnoczi     do {
29073fd282eSStefan Hajnoczi         ret = io_uring_submit_and_wait(&ctx->fdmon_io_uring, wait_nr);
291636b836dSStefan Hajnoczi     } while (ret == -EINTR);
292636b836dSStefan Hajnoczi 
29373fd282eSStefan Hajnoczi     assert(ret >= 0);
29473fd282eSStefan Hajnoczi 
29573fd282eSStefan Hajnoczi     return process_cq_ring(ctx, ready_list);
29673fd282eSStefan Hajnoczi }
29773fd282eSStefan Hajnoczi 
fdmon_io_uring_need_wait(AioContext * ctx)298aa38e19fSStefan Hajnoczi static bool fdmon_io_uring_need_wait(AioContext *ctx)
299aa38e19fSStefan Hajnoczi {
300ff807d55SStefan Hajnoczi     /* Have io_uring events completed? */
301ff807d55SStefan Hajnoczi     if (io_uring_cq_ready(&ctx->fdmon_io_uring)) {
302ff807d55SStefan Hajnoczi         return true;
303ff807d55SStefan Hajnoczi     }
304ff807d55SStefan Hajnoczi 
305ae60ab7eSStefan Hajnoczi     /* Are there pending sqes to submit? */
306ae60ab7eSStefan Hajnoczi     if (io_uring_sq_ready(&ctx->fdmon_io_uring)) {
307ae60ab7eSStefan Hajnoczi         return true;
308ae60ab7eSStefan Hajnoczi     }
309ae60ab7eSStefan Hajnoczi 
310ae60ab7eSStefan Hajnoczi     /* Do we need to process AioHandlers for io_uring changes? */
311ff807d55SStefan Hajnoczi     if (!QSLIST_EMPTY_RCU(&ctx->submit_list)) {
312ff807d55SStefan Hajnoczi         return true;
313ff807d55SStefan Hajnoczi     }
314ff807d55SStefan Hajnoczi 
31560f782b6SStefan Hajnoczi     return false;
316aa38e19fSStefan Hajnoczi }
317aa38e19fSStefan Hajnoczi 
31873fd282eSStefan Hajnoczi static const FDMonOps fdmon_io_uring_ops = {
31973fd282eSStefan Hajnoczi     .update = fdmon_io_uring_update,
32073fd282eSStefan Hajnoczi     .wait = fdmon_io_uring_wait,
321aa38e19fSStefan Hajnoczi     .need_wait = fdmon_io_uring_need_wait,
32273fd282eSStefan Hajnoczi };
32373fd282eSStefan Hajnoczi 
fdmon_io_uring_setup(AioContext * ctx)32473fd282eSStefan Hajnoczi bool fdmon_io_uring_setup(AioContext *ctx)
32573fd282eSStefan Hajnoczi {
32673fd282eSStefan Hajnoczi     int ret;
32773fd282eSStefan Hajnoczi 
32873fd282eSStefan Hajnoczi     ret = io_uring_queue_init(FDMON_IO_URING_ENTRIES, &ctx->fdmon_io_uring, 0);
32973fd282eSStefan Hajnoczi     if (ret != 0) {
33073fd282eSStefan Hajnoczi         return false;
33173fd282eSStefan Hajnoczi     }
33273fd282eSStefan Hajnoczi 
33373fd282eSStefan Hajnoczi     QSLIST_INIT(&ctx->submit_list);
33473fd282eSStefan Hajnoczi     ctx->fdmon_ops = &fdmon_io_uring_ops;
33573fd282eSStefan Hajnoczi     return true;
33673fd282eSStefan Hajnoczi }
33773fd282eSStefan Hajnoczi 
fdmon_io_uring_destroy(AioContext * ctx)33873fd282eSStefan Hajnoczi void fdmon_io_uring_destroy(AioContext *ctx)
33973fd282eSStefan Hajnoczi {
34073fd282eSStefan Hajnoczi     if (ctx->fdmon_ops == &fdmon_io_uring_ops) {
34173fd282eSStefan Hajnoczi         AioHandler *node;
34273fd282eSStefan Hajnoczi 
34373fd282eSStefan Hajnoczi         io_uring_queue_exit(&ctx->fdmon_io_uring);
34473fd282eSStefan Hajnoczi 
345de137e44SStefan Hajnoczi         /* Move handlers due to be removed onto the deleted list */
34673fd282eSStefan Hajnoczi         while ((node = QSLIST_FIRST_RCU(&ctx->submit_list))) {
347d73415a3SStefan Hajnoczi             unsigned flags = qatomic_fetch_and(&node->flags,
348de137e44SStefan Hajnoczi                     ~(FDMON_IO_URING_PENDING |
349de137e44SStefan Hajnoczi                       FDMON_IO_URING_ADD |
350de137e44SStefan Hajnoczi                       FDMON_IO_URING_REMOVE));
351de137e44SStefan Hajnoczi 
352de137e44SStefan Hajnoczi             if (flags & FDMON_IO_URING_REMOVE) {
353de137e44SStefan Hajnoczi                 QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted);
354de137e44SStefan Hajnoczi             }
355de137e44SStefan Hajnoczi 
35673fd282eSStefan Hajnoczi             QSLIST_REMOVE_HEAD_RCU(&ctx->submit_list, node_submitted);
35773fd282eSStefan Hajnoczi         }
35873fd282eSStefan Hajnoczi 
35973fd282eSStefan Hajnoczi         ctx->fdmon_ops = &fdmon_poll_ops;
36073fd282eSStefan Hajnoczi     }
36173fd282eSStefan Hajnoczi }
362