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 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 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 */ 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 */ 10273fd282eSStefan Hajnoczi static void enqueue(AioHandlerSList *head, AioHandler *node, unsigned flags) 10373fd282eSStefan Hajnoczi { 10473fd282eSStefan Hajnoczi unsigned old_flags; 10573fd282eSStefan Hajnoczi 10673fd282eSStefan Hajnoczi old_flags = atomic_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(). */ 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 */ 13073fd282eSStefan Hajnoczi *flags = atomic_fetch_and(&node->flags, ~(FDMON_IO_URING_PENDING | 13173fd282eSStefan Hajnoczi FDMON_IO_URING_ADD)); 13273fd282eSStefan Hajnoczi return node; 13373fd282eSStefan Hajnoczi } 13473fd282eSStefan Hajnoczi 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 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 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 18273fd282eSStefan Hajnoczi io_uring_prep_poll_remove(sqe, node); 18373fd282eSStefan Hajnoczi } 18473fd282eSStefan Hajnoczi 18573fd282eSStefan Hajnoczi /* Add a timeout that self-cancels when another cqe becomes ready */ 18673fd282eSStefan Hajnoczi static void add_timeout_sqe(AioContext *ctx, int64_t ns) 18773fd282eSStefan Hajnoczi { 18873fd282eSStefan Hajnoczi struct io_uring_sqe *sqe; 18973fd282eSStefan Hajnoczi struct __kernel_timespec ts = { 19073fd282eSStefan Hajnoczi .tv_sec = ns / NANOSECONDS_PER_SECOND, 19173fd282eSStefan Hajnoczi .tv_nsec = ns % NANOSECONDS_PER_SECOND, 19273fd282eSStefan Hajnoczi }; 19373fd282eSStefan Hajnoczi 19473fd282eSStefan Hajnoczi sqe = get_sqe(ctx); 19573fd282eSStefan Hajnoczi io_uring_prep_timeout(sqe, &ts, 1, 0); 19673fd282eSStefan Hajnoczi } 19773fd282eSStefan Hajnoczi 19873fd282eSStefan Hajnoczi /* Add sqes from ctx->submit_list for submission */ 19973fd282eSStefan Hajnoczi static void fill_sq_ring(AioContext *ctx) 20073fd282eSStefan Hajnoczi { 20173fd282eSStefan Hajnoczi AioHandlerSList submit_list; 20273fd282eSStefan Hajnoczi AioHandler *node; 20373fd282eSStefan Hajnoczi unsigned flags; 20473fd282eSStefan Hajnoczi 20573fd282eSStefan Hajnoczi QSLIST_MOVE_ATOMIC(&submit_list, &ctx->submit_list); 20673fd282eSStefan Hajnoczi 20773fd282eSStefan Hajnoczi while ((node = dequeue(&submit_list, &flags))) { 20873fd282eSStefan Hajnoczi /* Order matters, just in case both flags were set */ 20973fd282eSStefan Hajnoczi if (flags & FDMON_IO_URING_ADD) { 21073fd282eSStefan Hajnoczi add_poll_add_sqe(ctx, node); 21173fd282eSStefan Hajnoczi } 21273fd282eSStefan Hajnoczi if (flags & FDMON_IO_URING_REMOVE) { 21373fd282eSStefan Hajnoczi add_poll_remove_sqe(ctx, node); 21473fd282eSStefan Hajnoczi } 21573fd282eSStefan Hajnoczi } 21673fd282eSStefan Hajnoczi } 21773fd282eSStefan Hajnoczi 21873fd282eSStefan Hajnoczi /* Returns true if a handler became ready */ 21973fd282eSStefan Hajnoczi static bool process_cqe(AioContext *ctx, 22073fd282eSStefan Hajnoczi AioHandlerList *ready_list, 22173fd282eSStefan Hajnoczi struct io_uring_cqe *cqe) 22273fd282eSStefan Hajnoczi { 22373fd282eSStefan Hajnoczi AioHandler *node = io_uring_cqe_get_data(cqe); 22473fd282eSStefan Hajnoczi unsigned flags; 22573fd282eSStefan Hajnoczi 22673fd282eSStefan Hajnoczi /* poll_timeout and poll_remove have a zero user_data field */ 22773fd282eSStefan Hajnoczi if (!node) { 22873fd282eSStefan Hajnoczi return false; 22973fd282eSStefan Hajnoczi } 23073fd282eSStefan Hajnoczi 23173fd282eSStefan Hajnoczi /* 23273fd282eSStefan Hajnoczi * Deletion can only happen when IORING_OP_POLL_ADD completes. If we race 23373fd282eSStefan Hajnoczi * with enqueue() here then we can safely clear the FDMON_IO_URING_REMOVE 23473fd282eSStefan Hajnoczi * bit before IORING_OP_POLL_REMOVE is submitted. 23573fd282eSStefan Hajnoczi */ 23673fd282eSStefan Hajnoczi flags = atomic_fetch_and(&node->flags, ~FDMON_IO_URING_REMOVE); 23773fd282eSStefan Hajnoczi if (flags & FDMON_IO_URING_REMOVE) { 23873fd282eSStefan Hajnoczi QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); 23973fd282eSStefan Hajnoczi return false; 24073fd282eSStefan Hajnoczi } 24173fd282eSStefan Hajnoczi 24273fd282eSStefan Hajnoczi aio_add_ready_handler(ready_list, node, pfd_events_from_poll(cqe->res)); 24373fd282eSStefan Hajnoczi 24473fd282eSStefan Hajnoczi /* IORING_OP_POLL_ADD is one-shot so we must re-arm it */ 24573fd282eSStefan Hajnoczi add_poll_add_sqe(ctx, node); 24673fd282eSStefan Hajnoczi return true; 24773fd282eSStefan Hajnoczi } 24873fd282eSStefan Hajnoczi 24973fd282eSStefan Hajnoczi static int process_cq_ring(AioContext *ctx, AioHandlerList *ready_list) 25073fd282eSStefan Hajnoczi { 25173fd282eSStefan Hajnoczi struct io_uring *ring = &ctx->fdmon_io_uring; 25273fd282eSStefan Hajnoczi struct io_uring_cqe *cqe; 25373fd282eSStefan Hajnoczi unsigned num_cqes = 0; 25473fd282eSStefan Hajnoczi unsigned num_ready = 0; 25573fd282eSStefan Hajnoczi unsigned head; 25673fd282eSStefan Hajnoczi 25773fd282eSStefan Hajnoczi io_uring_for_each_cqe(ring, head, cqe) { 25873fd282eSStefan Hajnoczi if (process_cqe(ctx, ready_list, cqe)) { 25973fd282eSStefan Hajnoczi num_ready++; 26073fd282eSStefan Hajnoczi } 26173fd282eSStefan Hajnoczi 26273fd282eSStefan Hajnoczi num_cqes++; 26373fd282eSStefan Hajnoczi } 26473fd282eSStefan Hajnoczi 26573fd282eSStefan Hajnoczi io_uring_cq_advance(ring, num_cqes); 26673fd282eSStefan Hajnoczi return num_ready; 26773fd282eSStefan Hajnoczi } 26873fd282eSStefan Hajnoczi 26973fd282eSStefan Hajnoczi static int fdmon_io_uring_wait(AioContext *ctx, AioHandlerList *ready_list, 27073fd282eSStefan Hajnoczi int64_t timeout) 27173fd282eSStefan Hajnoczi { 27273fd282eSStefan Hajnoczi unsigned wait_nr = 1; /* block until at least one cqe is ready */ 27373fd282eSStefan Hajnoczi int ret; 27473fd282eSStefan Hajnoczi 27573fd282eSStefan Hajnoczi /* Fall back while external clients are disabled */ 27673fd282eSStefan Hajnoczi if (atomic_read(&ctx->external_disable_cnt)) { 27773fd282eSStefan Hajnoczi return fdmon_poll_ops.wait(ctx, ready_list, timeout); 27873fd282eSStefan Hajnoczi } 27973fd282eSStefan Hajnoczi 28073fd282eSStefan Hajnoczi if (timeout == 0) { 28173fd282eSStefan Hajnoczi wait_nr = 0; /* non-blocking */ 28273fd282eSStefan Hajnoczi } else if (timeout > 0) { 28373fd282eSStefan Hajnoczi add_timeout_sqe(ctx, timeout); 28473fd282eSStefan Hajnoczi } 28573fd282eSStefan Hajnoczi 28673fd282eSStefan Hajnoczi fill_sq_ring(ctx); 28773fd282eSStefan Hajnoczi 288636b836dSStefan Hajnoczi do { 28973fd282eSStefan Hajnoczi ret = io_uring_submit_and_wait(&ctx->fdmon_io_uring, wait_nr); 290636b836dSStefan Hajnoczi } while (ret == -EINTR); 291636b836dSStefan Hajnoczi 29273fd282eSStefan Hajnoczi assert(ret >= 0); 29373fd282eSStefan Hajnoczi 29473fd282eSStefan Hajnoczi return process_cq_ring(ctx, ready_list); 29573fd282eSStefan Hajnoczi } 29673fd282eSStefan Hajnoczi 297aa38e19fSStefan Hajnoczi static bool fdmon_io_uring_need_wait(AioContext *ctx) 298aa38e19fSStefan Hajnoczi { 299ff807d55SStefan Hajnoczi /* Have io_uring events completed? */ 300ff807d55SStefan Hajnoczi if (io_uring_cq_ready(&ctx->fdmon_io_uring)) { 301ff807d55SStefan Hajnoczi return true; 302ff807d55SStefan Hajnoczi } 303ff807d55SStefan Hajnoczi 304ae60ab7eSStefan Hajnoczi /* Are there pending sqes to submit? */ 305ae60ab7eSStefan Hajnoczi if (io_uring_sq_ready(&ctx->fdmon_io_uring)) { 306ae60ab7eSStefan Hajnoczi return true; 307ae60ab7eSStefan Hajnoczi } 308ae60ab7eSStefan Hajnoczi 309ae60ab7eSStefan Hajnoczi /* Do we need to process AioHandlers for io_uring changes? */ 310ff807d55SStefan Hajnoczi if (!QSLIST_EMPTY_RCU(&ctx->submit_list)) { 311ff807d55SStefan Hajnoczi return true; 312ff807d55SStefan Hajnoczi } 313ff807d55SStefan Hajnoczi 314ff807d55SStefan Hajnoczi /* Are we falling back to fdmon-poll? */ 315ff807d55SStefan Hajnoczi return atomic_read(&ctx->external_disable_cnt); 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 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 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 345*de137e44SStefan Hajnoczi /* Move handlers due to be removed onto the deleted list */ 34673fd282eSStefan Hajnoczi while ((node = QSLIST_FIRST_RCU(&ctx->submit_list))) { 347*de137e44SStefan Hajnoczi unsigned flags = atomic_fetch_and(&node->flags, 348*de137e44SStefan Hajnoczi ~(FDMON_IO_URING_PENDING | 349*de137e44SStefan Hajnoczi FDMON_IO_URING_ADD | 350*de137e44SStefan Hajnoczi FDMON_IO_URING_REMOVE)); 351*de137e44SStefan Hajnoczi 352*de137e44SStefan Hajnoczi if (flags & FDMON_IO_URING_REMOVE) { 353*de137e44SStefan Hajnoczi QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); 354*de137e44SStefan Hajnoczi } 355*de137e44SStefan 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