11f050a46SStefan Hajnoczi /* SPDX-License-Identifier: GPL-2.0-or-later */
21f050a46SStefan Hajnoczi /*
31f050a46SStefan Hajnoczi * epoll(7) file descriptor monitoring
41f050a46SStefan Hajnoczi */
51f050a46SStefan Hajnoczi
61f050a46SStefan Hajnoczi #include "qemu/osdep.h"
71f050a46SStefan Hajnoczi #include <sys/epoll.h>
81f050a46SStefan Hajnoczi #include "qemu/rcu_queue.h"
91f050a46SStefan Hajnoczi #include "aio-posix.h"
101f050a46SStefan Hajnoczi
111f050a46SStefan Hajnoczi /* The fd number threshold to switch to epoll */
121f050a46SStefan Hajnoczi #define EPOLL_ENABLE_THRESHOLD 64
131f050a46SStefan Hajnoczi
fdmon_epoll_disable(AioContext * ctx)141f050a46SStefan Hajnoczi void fdmon_epoll_disable(AioContext *ctx)
151f050a46SStefan Hajnoczi {
161f050a46SStefan Hajnoczi if (ctx->epollfd >= 0) {
171f050a46SStefan Hajnoczi close(ctx->epollfd);
181f050a46SStefan Hajnoczi ctx->epollfd = -1;
191f050a46SStefan Hajnoczi }
201f050a46SStefan Hajnoczi
211f050a46SStefan Hajnoczi /* Switch back */
221f050a46SStefan Hajnoczi ctx->fdmon_ops = &fdmon_poll_ops;
231f050a46SStefan Hajnoczi }
241f050a46SStefan Hajnoczi
epoll_events_from_pfd(int pfd_events)251f050a46SStefan Hajnoczi static inline int epoll_events_from_pfd(int pfd_events)
261f050a46SStefan Hajnoczi {
271f050a46SStefan Hajnoczi return (pfd_events & G_IO_IN ? EPOLLIN : 0) |
281f050a46SStefan Hajnoczi (pfd_events & G_IO_OUT ? EPOLLOUT : 0) |
291f050a46SStefan Hajnoczi (pfd_events & G_IO_HUP ? EPOLLHUP : 0) |
301f050a46SStefan Hajnoczi (pfd_events & G_IO_ERR ? EPOLLERR : 0);
311f050a46SStefan Hajnoczi }
321f050a46SStefan Hajnoczi
fdmon_epoll_update(AioContext * ctx,AioHandler * old_node,AioHandler * new_node)33b321051cSStefan Hajnoczi static void fdmon_epoll_update(AioContext *ctx,
34b321051cSStefan Hajnoczi AioHandler *old_node,
35b321051cSStefan Hajnoczi AioHandler *new_node)
361f050a46SStefan Hajnoczi {
37b321051cSStefan Hajnoczi struct epoll_event event = {
38b321051cSStefan Hajnoczi .data.ptr = new_node,
39b321051cSStefan Hajnoczi .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0,
40b321051cSStefan Hajnoczi };
411f050a46SStefan Hajnoczi int r;
421f050a46SStefan Hajnoczi
43b321051cSStefan Hajnoczi if (!new_node) {
44b321051cSStefan Hajnoczi r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event);
45b321051cSStefan Hajnoczi } else if (!old_node) {
46b321051cSStefan Hajnoczi r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event);
471f050a46SStefan Hajnoczi } else {
48b321051cSStefan Hajnoczi r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event);
491f050a46SStefan Hajnoczi }
501f050a46SStefan Hajnoczi
511f050a46SStefan Hajnoczi if (r) {
521f050a46SStefan Hajnoczi fdmon_epoll_disable(ctx);
531f050a46SStefan Hajnoczi }
541f050a46SStefan Hajnoczi }
551f050a46SStefan Hajnoczi
fdmon_epoll_wait(AioContext * ctx,AioHandlerList * ready_list,int64_t timeout)561f050a46SStefan Hajnoczi static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list,
571f050a46SStefan Hajnoczi int64_t timeout)
581f050a46SStefan Hajnoczi {
591f050a46SStefan Hajnoczi GPollFD pfd = {
601f050a46SStefan Hajnoczi .fd = ctx->epollfd,
611f050a46SStefan Hajnoczi .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
621f050a46SStefan Hajnoczi };
631f050a46SStefan Hajnoczi AioHandler *node;
641f050a46SStefan Hajnoczi int i, ret = 0;
651f050a46SStefan Hajnoczi struct epoll_event events[128];
661f050a46SStefan Hajnoczi
671f050a46SStefan Hajnoczi if (timeout > 0) {
681f050a46SStefan Hajnoczi ret = qemu_poll_ns(&pfd, 1, timeout);
691f050a46SStefan Hajnoczi if (ret > 0) {
701f050a46SStefan Hajnoczi timeout = 0;
711f050a46SStefan Hajnoczi }
721f050a46SStefan Hajnoczi }
731f050a46SStefan Hajnoczi if (timeout <= 0 || ret > 0) {
741f050a46SStefan Hajnoczi ret = epoll_wait(ctx->epollfd, events,
751f050a46SStefan Hajnoczi ARRAY_SIZE(events),
761f050a46SStefan Hajnoczi timeout);
771f050a46SStefan Hajnoczi if (ret <= 0) {
781f050a46SStefan Hajnoczi goto out;
791f050a46SStefan Hajnoczi }
801f050a46SStefan Hajnoczi for (i = 0; i < ret; i++) {
811f050a46SStefan Hajnoczi int ev = events[i].events;
821f050a46SStefan Hajnoczi int revents = (ev & EPOLLIN ? G_IO_IN : 0) |
831f050a46SStefan Hajnoczi (ev & EPOLLOUT ? G_IO_OUT : 0) |
841f050a46SStefan Hajnoczi (ev & EPOLLHUP ? G_IO_HUP : 0) |
851f050a46SStefan Hajnoczi (ev & EPOLLERR ? G_IO_ERR : 0);
861f050a46SStefan Hajnoczi
871f050a46SStefan Hajnoczi node = events[i].data.ptr;
881f050a46SStefan Hajnoczi aio_add_ready_handler(ready_list, node, revents);
891f050a46SStefan Hajnoczi }
901f050a46SStefan Hajnoczi }
911f050a46SStefan Hajnoczi out:
921f050a46SStefan Hajnoczi return ret;
931f050a46SStefan Hajnoczi }
941f050a46SStefan Hajnoczi
951f050a46SStefan Hajnoczi static const FDMonOps fdmon_epoll_ops = {
961f050a46SStefan Hajnoczi .update = fdmon_epoll_update,
971f050a46SStefan Hajnoczi .wait = fdmon_epoll_wait,
98aa38e19fSStefan Hajnoczi .need_wait = aio_poll_disabled,
991f050a46SStefan Hajnoczi };
1001f050a46SStefan Hajnoczi
fdmon_epoll_try_enable(AioContext * ctx)1011f050a46SStefan Hajnoczi static bool fdmon_epoll_try_enable(AioContext *ctx)
1021f050a46SStefan Hajnoczi {
1031f050a46SStefan Hajnoczi AioHandler *node;
1041f050a46SStefan Hajnoczi struct epoll_event event;
1051f050a46SStefan Hajnoczi
1061f050a46SStefan Hajnoczi QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
1071f050a46SStefan Hajnoczi int r;
1081f050a46SStefan Hajnoczi if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) {
1091f050a46SStefan Hajnoczi continue;
1101f050a46SStefan Hajnoczi }
1111f050a46SStefan Hajnoczi event.events = epoll_events_from_pfd(node->pfd.events);
1121f050a46SStefan Hajnoczi event.data.ptr = node;
1131f050a46SStefan Hajnoczi r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event);
1141f050a46SStefan Hajnoczi if (r) {
1151f050a46SStefan Hajnoczi return false;
1161f050a46SStefan Hajnoczi }
1171f050a46SStefan Hajnoczi }
1181f050a46SStefan Hajnoczi
1191f050a46SStefan Hajnoczi ctx->fdmon_ops = &fdmon_epoll_ops;
1201f050a46SStefan Hajnoczi return true;
1211f050a46SStefan Hajnoczi }
1221f050a46SStefan Hajnoczi
fdmon_epoll_try_upgrade(AioContext * ctx,unsigned npfd)1231f050a46SStefan Hajnoczi bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
1241f050a46SStefan Hajnoczi {
125*e62da985SStefan Hajnoczi bool ok;
126*e62da985SStefan Hajnoczi
1271f050a46SStefan Hajnoczi if (ctx->epollfd < 0) {
1281f050a46SStefan Hajnoczi return false;
1291f050a46SStefan Hajnoczi }
1301f050a46SStefan Hajnoczi
131*e62da985SStefan Hajnoczi if (npfd < EPOLL_ENABLE_THRESHOLD) {
132*e62da985SStefan Hajnoczi return false;
133*e62da985SStefan Hajnoczi }
134*e62da985SStefan Hajnoczi
135*e62da985SStefan Hajnoczi /* The list must not change while we add fds to epoll */
136*e62da985SStefan Hajnoczi if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
137*e62da985SStefan Hajnoczi return false;
138*e62da985SStefan Hajnoczi }
139*e62da985SStefan Hajnoczi
140*e62da985SStefan Hajnoczi ok = fdmon_epoll_try_enable(ctx);
141*e62da985SStefan Hajnoczi
142*e62da985SStefan Hajnoczi qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
143*e62da985SStefan Hajnoczi
144*e62da985SStefan Hajnoczi if (!ok) {
1451f050a46SStefan Hajnoczi fdmon_epoll_disable(ctx);
1461f050a46SStefan Hajnoczi }
147*e62da985SStefan Hajnoczi return ok;
1481f050a46SStefan Hajnoczi }
1491f050a46SStefan Hajnoczi
fdmon_epoll_setup(AioContext * ctx)1501f050a46SStefan Hajnoczi void fdmon_epoll_setup(AioContext *ctx)
1511f050a46SStefan Hajnoczi {
1521f050a46SStefan Hajnoczi ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
1531f050a46SStefan Hajnoczi if (ctx->epollfd == -1) {
1541f050a46SStefan Hajnoczi fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno));
1551f050a46SStefan Hajnoczi }
1561f050a46SStefan Hajnoczi }
157