diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-03-11 14:41:27 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-03-11 14:41:27 +0000 |
commit | 6e8a73e911f066527e775e04b98f31ebd19db600 (patch) | |
tree | e7e14f8a09d10c275e4d8164b8b3091fedcdbc46 /util/fdmon-epoll.c | |
parent | ba29883206d92a29ad5a466e679ccfc2ee6132ef (diff) | |
parent | d37d0e365afb6825a90d8356fc6adcc1f58f40f3 (diff) |
Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging
Pull request
# gpg: Signature made Wed 11 Mar 2020 12:40:36 GMT
# gpg: using RSA key 8695A8BFD3F97CDAAC35775A9CA4ABB381AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" [full]
# gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" [full]
# Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35 775A 9CA4 ABB3 81AB 73C8
* remotes/stefanha/tags/block-pull-request:
aio-posix: remove idle poll handlers to improve scalability
aio-posix: support userspace polling of fd monitoring
aio-posix: add io_uring fd monitoring implementation
aio-posix: simplify FDMonOps->update() prototype
aio-posix: extract ppoll(2) and epoll(7) fd monitoring
aio-posix: move RCU_READ_LOCK() into run_poll_handlers()
aio-posix: completely stop polling when disabled
aio-posix: remove confusing QLIST_SAFE_REMOVE()
qemu/queue.h: clear linked list pointers on remove
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'util/fdmon-epoll.c')
-rw-r--r-- | util/fdmon-epoll.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/util/fdmon-epoll.c b/util/fdmon-epoll.c new file mode 100644 index 0000000000..fcd989d47d --- /dev/null +++ b/util/fdmon-epoll.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * epoll(7) file descriptor monitoring + */ + +#include "qemu/osdep.h" +#include <sys/epoll.h> +#include "qemu/rcu_queue.h" +#include "aio-posix.h" + +/* The fd number threshold to switch to epoll */ +#define EPOLL_ENABLE_THRESHOLD 64 + +void fdmon_epoll_disable(AioContext *ctx) +{ + if (ctx->epollfd >= 0) { + close(ctx->epollfd); + ctx->epollfd = -1; + } + + /* Switch back */ + ctx->fdmon_ops = &fdmon_poll_ops; +} + +static inline int epoll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? EPOLLIN : 0) | + (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | + (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | + (pfd_events & G_IO_ERR ? EPOLLERR : 0); +} + +static void fdmon_epoll_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + struct epoll_event event = { + .data.ptr = new_node, + .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0, + }; + int r; + + if (!new_node) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event); + } else if (!old_node) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event); + } else { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event); + } + + if (r) { + fdmon_epoll_disable(ctx); + } +} + +static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + GPollFD pfd = { + .fd = ctx->epollfd, + .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR, + }; + AioHandler *node; + int i, ret = 0; + struct epoll_event events[128]; + + /* 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 > 0) { + ret = qemu_poll_ns(&pfd, 1, timeout); + if (ret > 0) { + timeout = 0; + } + } + if (timeout <= 0 || ret > 0) { + ret = epoll_wait(ctx->epollfd, events, + ARRAY_SIZE(events), + timeout); + if (ret <= 0) { + goto out; + } + for (i = 0; i < ret; i++) { + int ev = events[i].events; + int revents = (ev & EPOLLIN ? G_IO_IN : 0) | + (ev & EPOLLOUT ? G_IO_OUT : 0) | + (ev & EPOLLHUP ? G_IO_HUP : 0) | + (ev & EPOLLERR ? G_IO_ERR : 0); + + node = events[i].data.ptr; + aio_add_ready_handler(ready_list, node, revents); + } + } +out: + return ret; +} + +static const FDMonOps fdmon_epoll_ops = { + .update = fdmon_epoll_update, + .wait = fdmon_epoll_wait, + .need_wait = aio_poll_disabled, +}; + +static bool fdmon_epoll_try_enable(AioContext *ctx) +{ + AioHandler *node; + struct epoll_event event; + + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + int r; + if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) { + continue; + } + event.events = epoll_events_from_pfd(node->pfd.events); + event.data.ptr = node; + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); + if (r) { + return false; + } + } + + ctx->fdmon_ops = &fdmon_epoll_ops; + return true; +} + +bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) +{ + if (ctx->epollfd < 0) { + return false; + } + + /* Do not upgrade while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return false; + } + + if (npfd >= EPOLL_ENABLE_THRESHOLD) { + if (fdmon_epoll_try_enable(ctx)) { + return true; + } else { + fdmon_epoll_disable(ctx); + } + } + return false; +} + +void fdmon_epoll_setup(AioContext *ctx) +{ + ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); + if (ctx->epollfd == -1) { + fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); + } +} |