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