]>
Commit | Line | Data |
---|---|---|
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 | ||
14 | void 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 | ||
25 | static 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 |
33 | static 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 | ||
56 | static 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 | } | |
91 | out: | |
92 | return ret; | |
93 | } | |
94 | ||
95 | static 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 | ||
101 | static 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 | ||
123 | bool 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 | ||
150 | void 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 | } |