libvhost-user: add a simple link test without glib
[qemu.git] / util / fdmon-epoll.c
blobe11a8a022e9822e05c267901ca2a1f859803a9b4
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * epoll(7) file descriptor monitoring
4 */
6 #include "qemu/osdep.h"
7 #include <sys/epoll.h>
8 #include "qemu/rcu_queue.h"
9 #include "aio-posix.h"
11 /* The fd number threshold to switch to epoll */
12 #define EPOLL_ENABLE_THRESHOLD 64
14 void fdmon_epoll_disable(AioContext *ctx)
16 if (ctx->epollfd >= 0) {
17 close(ctx->epollfd);
18 ctx->epollfd = -1;
21 /* Switch back */
22 ctx->fdmon_ops = &fdmon_poll_ops;
25 static inline int epoll_events_from_pfd(int pfd_events)
27 return (pfd_events & G_IO_IN ? EPOLLIN : 0) |
28 (pfd_events & G_IO_OUT ? EPOLLOUT : 0) |
29 (pfd_events & G_IO_HUP ? EPOLLHUP : 0) |
30 (pfd_events & G_IO_ERR ? EPOLLERR : 0);
33 static void fdmon_epoll_update(AioContext *ctx,
34 AioHandler *old_node,
35 AioHandler *new_node)
37 struct epoll_event event = {
38 .data.ptr = new_node,
39 .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0,
41 int r;
43 if (!new_node) {
44 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event);
45 } else if (!old_node) {
46 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event);
47 } else {
48 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event);
51 if (r) {
52 fdmon_epoll_disable(ctx);
56 static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list,
57 int64_t timeout)
59 GPollFD pfd = {
60 .fd = ctx->epollfd,
61 .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
63 AioHandler *node;
64 int i, ret = 0;
65 struct epoll_event events[128];
67 /* Fall back while external clients are disabled */
68 if (qatomic_read(&ctx->external_disable_cnt)) {
69 return fdmon_poll_ops.wait(ctx, ready_list, timeout);
72 if (timeout > 0) {
73 ret = qemu_poll_ns(&pfd, 1, timeout);
74 if (ret > 0) {
75 timeout = 0;
78 if (timeout <= 0 || ret > 0) {
79 ret = epoll_wait(ctx->epollfd, events,
80 ARRAY_SIZE(events),
81 timeout);
82 if (ret <= 0) {
83 goto out;
85 for (i = 0; i < ret; i++) {
86 int ev = events[i].events;
87 int revents = (ev & EPOLLIN ? G_IO_IN : 0) |
88 (ev & EPOLLOUT ? G_IO_OUT : 0) |
89 (ev & EPOLLHUP ? G_IO_HUP : 0) |
90 (ev & EPOLLERR ? G_IO_ERR : 0);
92 node = events[i].data.ptr;
93 aio_add_ready_handler(ready_list, node, revents);
96 out:
97 return ret;
100 static const FDMonOps fdmon_epoll_ops = {
101 .update = fdmon_epoll_update,
102 .wait = fdmon_epoll_wait,
103 .need_wait = aio_poll_disabled,
106 static bool fdmon_epoll_try_enable(AioContext *ctx)
108 AioHandler *node;
109 struct epoll_event event;
111 QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
112 int r;
113 if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) {
114 continue;
116 event.events = epoll_events_from_pfd(node->pfd.events);
117 event.data.ptr = node;
118 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event);
119 if (r) {
120 return false;
124 ctx->fdmon_ops = &fdmon_epoll_ops;
125 return true;
128 bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
130 if (ctx->epollfd < 0) {
131 return false;
134 /* Do not upgrade while external clients are disabled */
135 if (qatomic_read(&ctx->external_disable_cnt)) {
136 return false;
139 if (npfd >= EPOLL_ENABLE_THRESHOLD) {
140 if (fdmon_epoll_try_enable(ctx)) {
141 return true;
142 } else {
143 fdmon_epoll_disable(ctx);
146 return false;
149 void fdmon_epoll_setup(AioContext *ctx)
151 ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
152 if (ctx->epollfd == -1) {
153 fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno));