]> git.proxmox.com Git - mirror_qemu.git/blame - util/fdmon-epoll.c
Merge tag 'C3700-pull-request' of https://github.com/hdeller/qemu-hppa into staging
[mirror_qemu.git] / util / fdmon-epoll.c
CommitLineData
1f050a46
SH
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/rcu_queue.h"
9#include "aio-posix.h"
10
11/* The fd number threshold to switch to epoll */
12#define EPOLL_ENABLE_THRESHOLD 64
13
14void fdmon_epoll_disable(AioContext *ctx)
15{
16 if (ctx->epollfd >= 0) {
17 close(ctx->epollfd);
18 ctx->epollfd = -1;
19 }
20
21 /* Switch back */
22 ctx->fdmon_ops = &fdmon_poll_ops;
23}
24
25static inline int epoll_events_from_pfd(int pfd_events)
26{
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);
31}
32
b321051c
SH
33static void fdmon_epoll_update(AioContext *ctx,
34 AioHandler *old_node,
35 AioHandler *new_node)
1f050a46 36{
b321051c
SH
37 struct epoll_event event = {
38 .data.ptr = new_node,
39 .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0,
40 };
1f050a46 41 int r;
1f050a46 42
b321051c
SH
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);
1f050a46 47 } else {
b321051c 48 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event);
1f050a46
SH
49 }
50
1f050a46
SH
51 if (r) {
52 fdmon_epoll_disable(ctx);
53 }
54}
55
56static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list,
57 int64_t timeout)
58{
59 GPollFD pfd = {
60 .fd = ctx->epollfd,
61 .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR,
62 };
63 AioHandler *node;
64 int i, ret = 0;
65 struct epoll_event events[128];
66
1f050a46
SH
67 if (timeout > 0) {
68 ret = qemu_poll_ns(&pfd, 1, timeout);
69 if (ret > 0) {
70 timeout = 0;
71 }
72 }
73 if (timeout <= 0 || ret > 0) {
74 ret = epoll_wait(ctx->epollfd, events,
75 ARRAY_SIZE(events),
76 timeout);
77 if (ret <= 0) {
78 goto out;
79 }
80 for (i = 0; i < ret; i++) {
81 int ev = events[i].events;
82 int revents = (ev & EPOLLIN ? G_IO_IN : 0) |
83 (ev & EPOLLOUT ? G_IO_OUT : 0) |
84 (ev & EPOLLHUP ? G_IO_HUP : 0) |
85 (ev & EPOLLERR ? G_IO_ERR : 0);
86
87 node = events[i].data.ptr;
88 aio_add_ready_handler(ready_list, node, revents);
89 }
90 }
91out:
92 return ret;
93}
94
95static const FDMonOps fdmon_epoll_ops = {
96 .update = fdmon_epoll_update,
97 .wait = fdmon_epoll_wait,
aa38e19f 98 .need_wait = aio_poll_disabled,
1f050a46
SH
99};
100
101static bool fdmon_epoll_try_enable(AioContext *ctx)
102{
103 AioHandler *node;
104 struct epoll_event event;
105
106 QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) {
107 int r;
108 if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) {
109 continue;
110 }
111 event.events = epoll_events_from_pfd(node->pfd.events);
112 event.data.ptr = node;
113 r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event);
114 if (r) {
115 return false;
116 }
117 }
118
119 ctx->fdmon_ops = &fdmon_epoll_ops;
120 return true;
121}
122
123bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
124{
e62da985
SH
125 bool ok;
126
1f050a46
SH
127 if (ctx->epollfd < 0) {
128 return false;
129 }
130
e62da985
SH
131 if (npfd < EPOLL_ENABLE_THRESHOLD) {
132 return false;
133 }
134
135 /* The list must not change while we add fds to epoll */
136 if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
137 return false;
138 }
139
140 ok = fdmon_epoll_try_enable(ctx);
141
142 qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
143
144 if (!ok) {
145 fdmon_epoll_disable(ctx);
1f050a46 146 }
e62da985 147 return ok;
1f050a46
SH
148}
149
150void fdmon_epoll_setup(AioContext *ctx)
151{
152 ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
153 if (ctx->epollfd == -1) {
154 fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno));
155 }
156}