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