From nobody Fri Nov 14 00:48:04 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1583428660; cv=none; d=zohomail.com; s=zohoarc; b=kX1e8xVMWVWlEi4BpMLdvh9gJ7oCCDgPi6s5I3IRt2dyNOuo0xY1OrPQGzJdXDgG3YEaCnZ+777SGdTkcLO3v8tt/U32bvh5apvjmrd7c+P5a7e3XPNC3UVk4kEjiYpbfariU8XQLZxB+j/uqOZrcxXqWSH1vZkbh61/Mnbk+Gc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1583428660; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=l64hh/QfUxbG6dgP8gOtGSFXjvY4+UHjy+pP2EyLJbA=; b=iG09yuZUbekPfXlyAjL5JeXyCS0Eg85OyB5Gaw2HD0VCozaFjE5A/8hkfRoGgvbIw9xeHiy6bAkx6oA2t/8aBHsQArQ3pR0olz2BoRteSHGu8YYzzAvrrDX4SMzmwF0GMdiU1isuzvayyYpfKv8CXtO6igPyQhl0ngY/Io2W9OI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1583428660069477.23042774566727; Thu, 5 Mar 2020 09:17:40 -0800 (PST) Received: from localhost ([::1]:53852 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j9u7u-0005fu-E4 for importer@patchew.org; Thu, 05 Mar 2020 12:17:38 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38085) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j9tzB-0000UP-0n for qemu-devel@nongnu.org; Thu, 05 Mar 2020 12:08:39 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1j9tz8-0003Lv-KU for qemu-devel@nongnu.org; Thu, 05 Mar 2020 12:08:36 -0500 Received: from us-smtp-delivery-1.mimecast.com ([205.139.110.120]:46886 helo=us-smtp-1.mimecast.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1j9tz8-0003Li-El for qemu-devel@nongnu.org; Thu, 05 Mar 2020 12:08:34 -0500 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-362-ixwJvmXzOYWb2n7Q-iS-2A-1; Thu, 05 Mar 2020 12:08:32 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3365518AB2C2; Thu, 5 Mar 2020 17:08:31 +0000 (UTC) Received: from localhost (ovpn-117-104.ams2.redhat.com [10.36.117.104]) by smtp.corp.redhat.com (Postfix) with ESMTP id D447C5C219; Thu, 5 Mar 2020 17:08:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1583428114; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=l64hh/QfUxbG6dgP8gOtGSFXjvY4+UHjy+pP2EyLJbA=; b=O8yb4wCbBbgW2WSYm71lQXYDh54d2LxRRHdVb5igjOp3ex/mlb0IkX6ddT+FxTqPwQOQWe bvJjdm367JZwR+/cHiEDev9T9nc60ehM5KTdJpXTna+EidD39RJnlM1N97JzyW4Sgsj3lf OCE8OFE+FSdM2XRiz/XX2lJKpGIQ3mU= X-MC-Unique: ixwJvmXzOYWb2n7Q-iS-2A-1 From: Stefan Hajnoczi To: qemu-devel@nongnu.org Subject: [PATCH 5/7] aio-posix: add io_uring fd monitoring implementation Date: Thu, 5 Mar 2020 17:08:04 +0000 Message-Id: <20200305170806.1313245-6-stefanha@redhat.com> In-Reply-To: <20200305170806.1313245-1-stefanha@redhat.com> References: <20200305170806.1313245-1-stefanha@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 205.139.110.120 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Fam Zheng , Kevin Wolf , qemu-block@nongnu.org, Max Reitz , Stefan Hajnoczi , Paolo Bonzini Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" The recent Linux io_uring API has several advantages over ppoll(2) and epoll(2). Details are given in the source code. Add an io_uring implementation and make it the default on Linux. Performance is the same as with epoll(7) but later patches add optimizations that take advantage of io_uring. It is necessary to change how aio_set_fd_handler() deals with deleting AioHandlers since removing monitored file descriptors is asynchronous in io_uring. fdmon_io_uring_remove() marks the AioHandler deleted and aio_set_fd_handler() will let it handle deletion in that case. Signed-off-by: Stefan Hajnoczi --- configure | 5 + include/block/aio.h | 9 ++ util/Makefile.objs | 1 + util/aio-posix.c | 20 ++- util/aio-posix.h | 20 ++- util/fdmon-io_uring.c | 326 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 376 insertions(+), 5 deletions(-) create mode 100644 util/fdmon-io_uring.c diff --git a/configure b/configure index fab6281eb7..8ca2fb5c88 100755 --- a/configure +++ b/configure @@ -4093,6 +4093,11 @@ if test "$linux_io_uring" !=3D "no" ; then linux_io_uring_cflags=3D$($pkg_config --cflags liburing) linux_io_uring_libs=3D$($pkg_config --libs liburing) linux_io_uring=3Dyes + + # io_uring is used in libqemuutil.a where per-file -libs variables are= not + # seen by programs linking the archive. It's not ideal, but just add = the + # library dependency globally. + LIBS=3D"$linux_io_uring_libs $LIBS" else if test "$linux_io_uring" =3D "yes" ; then feature_not_found "linux io_uring" "Install liburing devel" diff --git a/include/block/aio.h b/include/block/aio.h index bd76b08f1a..83fc9b844d 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -14,6 +14,9 @@ #ifndef QEMU_AIO_H #define QEMU_AIO_H =20 +#ifdef CONFIG_LINUX_IO_URING +#include +#endif #include "qemu/queue.h" #include "qemu/event_notifier.h" #include "qemu/thread.h" @@ -96,6 +99,8 @@ struct BHListSlice { QSIMPLEQ_ENTRY(BHListSlice) next; }; =20 +typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; + struct AioContext { GSource source; =20 @@ -181,6 +186,10 @@ struct AioContext { * locking. */ struct LuringState *linux_io_uring; + + /* State for file descriptor monitoring using Linux io_uring */ + struct io_uring fdmon_io_uring; + AioHandlerSList submit_list; #endif =20 /* TimerLists for calling timers - one per clock type. Has its own diff --git a/util/Makefile.objs b/util/Makefile.objs index 6439077a68..6718a38b61 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -7,6 +7,7 @@ util-obj-$(call lnot,$(CONFIG_ATOMIC64)) +=3D atomic64.o util-obj-$(CONFIG_POSIX) +=3D aio-posix.o util-obj-$(CONFIG_POSIX) +=3D fdmon-poll.o util-obj-$(CONFIG_EPOLL_CREATE1) +=3D fdmon-epoll.o +util-obj-$(CONFIG_LINUX_IO_URING) +=3D fdmon-io_uring.o util-obj-$(CONFIG_POSIX) +=3D compatfd.o util-obj-$(CONFIG_POSIX) +=3D event_notifier-posix.o util-obj-$(CONFIG_POSIX) +=3D mmap-alloc.o diff --git a/util/aio-posix.c b/util/aio-posix.c index 3b9aad4ca7..a24a33c15a 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -57,10 +57,16 @@ static bool aio_remove_fd_handler(AioContext *ctx, AioH= andler *node) g_source_remove_poll(&ctx->source, &node->pfd); } =20 + node->pfd.revents =3D 0; + + /* If the fd monitor has already marked it deleted, leave it alone */ + if (QLIST_IS_INSERTED(node, node_deleted)) { + return false; + } + /* If a read is in progress, just mark the node as deleted */ if (qemu_lockcnt_count(&ctx->list_lock)) { QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_delet= ed); - node->pfd.revents =3D 0; return false; } /* Otherwise, delete it for real. We can't just mark it as @@ -126,9 +132,6 @@ void aio_set_fd_handler(AioContext *ctx, =20 QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, new_node, node); } - if (node) { - deleted =3D aio_remove_fd_handler(ctx, node); - } =20 /* No need to order poll_disable_cnt writes against other updates; * the counter is only used to avoid wasting time and latency on @@ -140,6 +143,9 @@ void aio_set_fd_handler(AioContext *ctx, atomic_read(&ctx->poll_disable_cnt) + poll_disable_change); =20 ctx->fdmon_ops->update(ctx, node, new_node); + if (node) { + deleted =3D aio_remove_fd_handler(ctx, node); + } qemu_lockcnt_unlock(&ctx->list_lock); aio_notify(ctx); =20 @@ -565,11 +571,17 @@ void aio_context_setup(AioContext *ctx) ctx->fdmon_ops =3D &fdmon_poll_ops; ctx->epollfd =3D -1; =20 + /* Use the fastest fd monitoring implementation if available */ + if (fdmon_io_uring_setup(ctx)) { + return; + } + fdmon_epoll_setup(ctx); } =20 void aio_context_destroy(AioContext *ctx) { + fdmon_io_uring_destroy(ctx); fdmon_epoll_disable(ctx); } =20 diff --git a/util/aio-posix.h b/util/aio-posix.h index 97899d0fbc..55fc771327 100644 --- a/util/aio-posix.h +++ b/util/aio-posix.h @@ -27,10 +27,14 @@ struct AioHandler { IOHandler *io_poll_begin; IOHandler *io_poll_end; void *opaque; - bool is_external; QLIST_ENTRY(AioHandler) node; QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */ QLIST_ENTRY(AioHandler) node_deleted; +#ifdef CONFIG_LINUX_IO_URING + QSLIST_ENTRY(AioHandler) node_submitted; + unsigned flags; /* see fdmon-io_uring.c */ +#endif + bool is_external; }; =20 /* Add a handler to a ready list */ @@ -58,4 +62,18 @@ static inline void fdmon_epoll_disable(AioContext *ctx) } #endif /* !CONFIG_EPOLL_CREATE1 */ =20 +#ifdef CONFIG_LINUX_IO_URING +bool fdmon_io_uring_setup(AioContext *ctx); +void fdmon_io_uring_destroy(AioContext *ctx); +#else +static inline bool fdmon_io_uring_setup(AioContext *ctx) +{ + return false; +} + +static inline void fdmon_io_uring_destroy(AioContext *ctx) +{ +} +#endif /* !CONFIG_LINUX_IO_URING */ + #endif /* AIO_POSIX_H */ diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c new file mode 100644 index 0000000000..fb99b4b61e --- /dev/null +++ b/util/fdmon-io_uring.c @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux io_uring file descriptor monitoring + * + * The Linux io_uring API supports file descriptor monitoring with a few + * advantages over existing APIs like poll(2) and epoll(7): + * + * 1. Userspace polling of events is possible because the completion queue= (cq + * ring) is shared between the kernel and userspace. This allows + * applications that rely on userspace polling to also monitor file + * descriptors in the same userspace polling loop. + * + * 2. Submission and completion is batched and done together in a single s= ystem + * call. This minimizes the number of system calls. + * + * 3. File descriptor monitoring is O(1) like epoll(7) so it scales better= than + * poll(2). + * + * 4. Nanosecond timeouts are supported so it requires fewer syscalls than + * epoll(7). + * + * This code only monitors file descriptors and does not do asynchronous d= isk + * I/O. Implementing disk I/O efficiently has other requirements and shou= ld + * use a separate io_uring so it does not make sense to unify the code. + * + * File descriptor monitoring is implemented using the following operation= s: + * + * 1. IORING_OP_POLL_ADD - adds a file descriptor to be monitored. + * 2. IORING_OP_POLL_REMOVE - removes a file descriptor being monitored. = When + * the poll mask changes for a file descriptor it is first removed and = then + * re-added with the new poll mask, so this operation is also used as p= art + * of modifying an existing monitored file descriptor. + * 3. IORING_OP_TIMEOUT - added every time a blocking syscall is made to w= ait + * for events. This operation self-cancels if another event completes + * before the timeout. + * + * io_uring calls the submission queue the "sq ring" and the completion qu= eue + * the "cq ring". Ring entries are called "sqe" and "cqe", respectively. + * + * The code is structured so that sq/cq rings are only modified within + * fdmon_io_uring_wait(). Changes to AioHandlers are made by enqueuing th= em on + * ctx->submit_list so that fdmon_io_uring_wait() can submit IORING_OP_POL= L_ADD + * and/or IORING_OP_POLL_REMOVE sqes for them. + */ + +#include "qemu/osdep.h" +#include +#include "qemu/rcu_queue.h" +#include "aio-posix.h" + +enum { + FDMON_IO_URING_ENTRIES =3D 128, /* sq/cq ring size */ + + /* AioHandler::flags */ + FDMON_IO_URING_PENDING =3D (1 << 0), + FDMON_IO_URING_ADD =3D (1 << 1), + FDMON_IO_URING_REMOVE =3D (1 << 2), +}; + +static inline int poll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? POLLIN : 0) | + (pfd_events & G_IO_OUT ? POLLOUT : 0) | + (pfd_events & G_IO_HUP ? POLLHUP : 0) | + (pfd_events & G_IO_ERR ? POLLERR : 0); +} + +static inline int pfd_events_from_poll(int poll_events) +{ + return (poll_events & POLLIN ? G_IO_IN : 0) | + (poll_events & POLLOUT ? G_IO_OUT : 0) | + (poll_events & POLLHUP ? G_IO_HUP : 0) | + (poll_events & POLLERR ? G_IO_ERR : 0); +} + +/* + * Returns an sqe for submitting a request. Only be called within + * fdmon_io_uring_wait(). + */ +static struct io_uring_sqe *get_sqe(AioContext *ctx) +{ + struct io_uring *ring =3D &ctx->fdmon_io_uring; + struct io_uring_sqe *sqe =3D io_uring_get_sqe(ring); + int ret; + + if (likely(sqe)) { + return sqe; + } + + /* No free sqes left, submit pending sqes first */ + ret =3D io_uring_submit(ring); + assert(ret > 1); + sqe =3D io_uring_get_sqe(ring); + assert(sqe); + return sqe; +} + +/* Atomically enqueue an AioHandler for sq ring submission */ +static void enqueue(AioHandlerSList *head, AioHandler *node, unsigned flag= s) +{ + unsigned old_flags; + + old_flags =3D atomic_fetch_or(&node->flags, FDMON_IO_URING_PENDING | f= lags); + if (!(old_flags & FDMON_IO_URING_PENDING)) { + QSLIST_INSERT_HEAD_ATOMIC(head, node, node_submitted); + } +} + +/* Dequeue an AioHandler for sq ring submission. Called by fill_sq_ring()= . */ +static AioHandler *dequeue(AioHandlerSList *head, unsigned *flags) +{ + AioHandler *node =3D QSLIST_FIRST(head); + + if (!node) { + return NULL; + } + + /* Doesn't need to be atomic since fill_sq_ring() moves the list */ + QSLIST_REMOVE_HEAD(head, node_submitted); + + /* + * Don't clear FDMON_IO_URING_REMOVE. It's sticky so it can serve two + * purposes: telling fill_sq_ring() to submit IORING_OP_POLL_REMOVE and + * telling process_cqe() to delete the AioHandler when its + * IORING_OP_POLL_ADD completes. + */ + *flags =3D atomic_fetch_and(&node->flags, ~(FDMON_IO_URING_PENDING | + FDMON_IO_URING_ADD)); + return node; +} + +static void fdmon_io_uring_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + if (new_node) { + enqueue(&ctx->submit_list, new_node, FDMON_IO_URING_ADD); + } + + if (old_node) { + /* + * Deletion is tricky because IORING_OP_POLL_ADD and + * IORING_OP_POLL_REMOVE are async. We need to wait for the origi= nal + * IORING_OP_POLL_ADD to complete before this handler can be freed + * safely. + * + * It's possible that the file descriptor becomes ready and the + * IORING_OP_POLL_ADD cqe is enqueued before IORING_OP_POLL_REMOVE= is + * submitted, too. + * + * Mark this handler deleted right now but don't place it on + * ctx->deleted_aio_handlers yet. Instead, manually fudge the list + * entry to make QLIST_IS_INSERTED() think this handler has been + * inserted and other code recognizes this AioHandler as deleted. + * + * Once the original IORING_OP_POLL_ADD completes we enqueue the + * handler on the real ctx->deleted_aio_handlers list to be freed. + */ + assert(!QLIST_IS_INSERTED(old_node, node_deleted)); + old_node->node_deleted.le_prev =3D &old_node->node_deleted.le_next; + + enqueue(&ctx->submit_list, old_node, FDMON_IO_URING_REMOVE); + } +} + +static void add_poll_add_sqe(AioContext *ctx, AioHandler *node) +{ + struct io_uring_sqe *sqe =3D get_sqe(ctx); + int events =3D poll_events_from_pfd(node->pfd.events); + + io_uring_prep_poll_add(sqe, node->pfd.fd, events); + io_uring_sqe_set_data(sqe, node); +} + +static void add_poll_remove_sqe(AioContext *ctx, AioHandler *node) +{ + struct io_uring_sqe *sqe =3D get_sqe(ctx); + + io_uring_prep_poll_remove(sqe, node); +} + +/* Add a timeout that self-cancels when another cqe becomes ready */ +static void add_timeout_sqe(AioContext *ctx, int64_t ns) +{ + struct io_uring_sqe *sqe; + struct __kernel_timespec ts =3D { + .tv_sec =3D ns / NANOSECONDS_PER_SECOND, + .tv_nsec =3D ns % NANOSECONDS_PER_SECOND, + }; + + sqe =3D get_sqe(ctx); + io_uring_prep_timeout(sqe, &ts, 1, 0); +} + +/* Add sqes from ctx->submit_list for submission */ +static void fill_sq_ring(AioContext *ctx) +{ + AioHandlerSList submit_list; + AioHandler *node; + unsigned flags; + + QSLIST_MOVE_ATOMIC(&submit_list, &ctx->submit_list); + + while ((node =3D dequeue(&submit_list, &flags))) { + /* Order matters, just in case both flags were set */ + if (flags & FDMON_IO_URING_ADD) { + add_poll_add_sqe(ctx, node); + } + if (flags & FDMON_IO_URING_REMOVE) { + add_poll_remove_sqe(ctx, node); + } + } +} + +/* Returns true if a handler became ready */ +static bool process_cqe(AioContext *ctx, + AioHandlerList *ready_list, + struct io_uring_cqe *cqe) +{ + AioHandler *node =3D io_uring_cqe_get_data(cqe); + unsigned flags; + + /* poll_timeout and poll_remove have a zero user_data field */ + if (!node) { + return false; + } + + /* + * Deletion can only happen when IORING_OP_POLL_ADD completes. If we = race + * with enqueue() here then we can safely clear the FDMON_IO_URING_REM= OVE + * bit before IORING_OP_POLL_REMOVE is submitted. + */ + flags =3D atomic_fetch_and(&node->flags, ~FDMON_IO_URING_REMOVE); + if (flags & FDMON_IO_URING_REMOVE) { + QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_delet= ed); + return false; + } + + aio_add_ready_handler(ready_list, node, pfd_events_from_poll(cqe->res)= ); + + /* IORING_OP_POLL_ADD is one-shot so we must re-arm it */ + add_poll_add_sqe(ctx, node); + return true; +} + +static int process_cq_ring(AioContext *ctx, AioHandlerList *ready_list) +{ + struct io_uring *ring =3D &ctx->fdmon_io_uring; + struct io_uring_cqe *cqe; + unsigned num_cqes =3D 0; + unsigned num_ready =3D 0; + unsigned head; + + io_uring_for_each_cqe(ring, head, cqe) { + if (process_cqe(ctx, ready_list, cqe)) { + num_ready++; + } + + num_cqes++; + } + + io_uring_cq_advance(ring, num_cqes); + return num_ready; +} + +static int fdmon_io_uring_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + unsigned wait_nr =3D 1; /* block until at least one cqe is ready */ + int ret; + + /* Fall back while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return fdmon_poll_ops.wait(ctx, ready_list, timeout); + } + + if (timeout =3D=3D 0) { + wait_nr =3D 0; /* non-blocking */ + } else if (timeout > 0) { + add_timeout_sqe(ctx, timeout); + } + + fill_sq_ring(ctx); + + ret =3D io_uring_submit_and_wait(&ctx->fdmon_io_uring, wait_nr); + assert(ret >=3D 0); + + return process_cq_ring(ctx, ready_list); +} + +static const FDMonOps fdmon_io_uring_ops =3D { + .update =3D fdmon_io_uring_update, + .wait =3D fdmon_io_uring_wait, +}; + +bool fdmon_io_uring_setup(AioContext *ctx) +{ + int ret; + + ret =3D io_uring_queue_init(FDMON_IO_URING_ENTRIES, &ctx->fdmon_io_uri= ng, 0); + if (ret !=3D 0) { + return false; + } + + QSLIST_INIT(&ctx->submit_list); + ctx->fdmon_ops =3D &fdmon_io_uring_ops; + return true; +} + +void fdmon_io_uring_destroy(AioContext *ctx) +{ + if (ctx->fdmon_ops =3D=3D &fdmon_io_uring_ops) { + AioHandler *node; + + io_uring_queue_exit(&ctx->fdmon_io_uring); + + /* No need to submit these anymore, just free them. */ + while ((node =3D QSLIST_FIRST_RCU(&ctx->submit_list))) { + QSLIST_REMOVE_HEAD_RCU(&ctx->submit_list, node_submitted); + QLIST_REMOVE(node, node); + g_free(node); + } + + ctx->fdmon_ops =3D &fdmon_poll_ops; + } +} --=20 2.24.1