xref: /openbmc/qemu/util/fdmon-epoll.c (revision 60f782b6)
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