master
c 181 lines 4.66 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * epoll(7) file descriptor monitoring
4 */
5
6 #include "qemu/osdep.h"
7 #include <sys/epoll.h>
8 #include "qemu/lockcnt.h"
9 #include "qemu/rcu_queue.h"
10 #include "aio-posix.h"
11
12 /* The fd number threshold to switch to epoll */
13 #define EPOLL_ENABLE_THRESHOLD 64
14
15 void fdmon_epoll_disable(AioContext *ctx)
16 {
17 if (ctx->epollfd >= 0) {
18 close(ctx->epollfd);
19 ctx->epollfd = -1;
20 }
21
22 if (ctx->epollfd_tag) {
23 g_source_remove_unix_fd(&ctx->source, ctx->epollfd_tag);
24 ctx->epollfd_tag = NULL;
25 }
26
27 fdmon_poll_downgrade(ctx);
28 }
29
30 static inline int epoll_events_from_pfd(int pfd_events)
31 {
32 return (pfd_events & G_IO_IN ? EPOLLIN : 0) |
33 (pfd_events & G_IO_OUT ? EPOLLOUT : 0) |
34 (pfd_events & G_IO_HUP ? EPOLLHUP : 0) |
35 (pfd_events & G_IO_ERR ? EPOLLERR : 0);
36 }
37
38 static void fdmon_epoll_update(AioContext *ctx,
39 AioHandler *old_node,
40 AioHandler *new_node)
41 {
42 struct epoll_event event = {
43 .data.ptr = new_node,
44 .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0,
45 };
46 int r;
47
48 if (!new_node) {
49 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event);
50 } else if (!old_node) {
51 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event);
52 } else {
53 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event);
54 }
55
56 if (r) {
57 fdmon_epoll_disable(ctx);
58 }
59 }
60
61 static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list,
62 int64_t timeout)
63 {
64 GPollFD pfd = {
65 .fd = ctx->epollfd,
66 .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
67 };
68 AioHandler *node;
69 int i, ret = 0;
70 struct epoll_event events[128];
71
72 if (timeout > 0) {
73 ret = qemu_poll_ns(&pfd, 1, timeout);
74 if (ret > 0) {
75 timeout = 0;
76 }
77 }
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;
84 }
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);
91
92 node = events[i].data.ptr;
93 aio_add_ready_handler(ready_list, node, revents);
94 }
95 }
96 out:
97 return ret;
98 }
99
100 static void fdmon_epoll_gsource_prepare(AioContext *ctx)
101 {
102 /* Do nothing */
103 }
104
105 static bool fdmon_epoll_gsource_check(AioContext *ctx)
106 {
107 return g_source_query_unix_fd(&ctx->source, ctx->epollfd_tag) & G_IO_IN;
108 }
109
110 static void fdmon_epoll_gsource_dispatch(AioContext *ctx,
111 AioHandlerList *ready_list)
112 {
113 fdmon_epoll_wait(ctx, ready_list, 0);
114 }
115
116 static const FDMonOps fdmon_epoll_ops = {
117 .update = fdmon_epoll_update,
118 .wait = fdmon_epoll_wait,
119 .need_wait = aio_poll_disabled,
120 .gsource_prepare = fdmon_epoll_gsource_prepare,
121 .gsource_check = fdmon_epoll_gsource_check,
122 .gsource_dispatch = fdmon_epoll_gsource_dispatch,
123 };
124
125 static bool fdmon_epoll_try_enable(AioContext *ctx)
126 {
127 AioHandler *node;
128 struct epoll_event event;
129
130 QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
131 int r;
132 if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) {
133 continue;
134 }
135 event.events = epoll_events_from_pfd(node->pfd.events);
136 event.data.ptr = node;
137 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event);
138 if (r) {
139 return false;
140 }
141 }
142
143 ctx->fdmon_ops = &fdmon_epoll_ops;
144 ctx->epollfd_tag = g_source_add_unix_fd(&ctx->source, ctx->epollfd,
145 G_IO_IN);
146 return true;
147 }
148
149 bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
150 {
151 bool ok;
152
153 if (ctx->epollfd < 0) {
154 return false;
155 }
156
157 if (npfd < EPOLL_ENABLE_THRESHOLD) {
158 return false;
159 }
160
161 /* The list must not change while we add fds to epoll */
162 if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
163 return false;
164 }
165
166 ok = fdmon_epoll_try_enable(ctx);
167 if (!ok) {
168 fdmon_epoll_disable(ctx);
169 }
170
171 qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
172 return ok;
173 }
174
175 void fdmon_epoll_setup(AioContext *ctx)
176 {
177 ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
178 if (ctx->epollfd == -1) {
179 fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno));
180 }
181 }