]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * IRC - Internet Relay Chat, ircd/engine_epoll.c | |
3 | * Copyright (C) 2003 Michael Poole <mdpoole@troilus.org> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 1, or (at your option) | |
8 | * any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | */ | |
19 | /** @file | |
20 | * @brief Linux epoll_*() event engine. | |
21 | * @version $Id: engine_epoll.c,v 1.12 2005/08/25 01:26:46 entrope Exp $ | |
22 | */ | |
23 | #include "config.h" | |
24 | ||
25 | #include "ircd.h" | |
26 | #include "ircd_events.h" | |
27 | #include "ircd_alloc.h" | |
28 | #include "ircd_features.h" | |
29 | #include "ircd_log.h" | |
30 | #include "s_debug.h" | |
31 | ||
32 | /* #include <assert.h> -- Now using assert in ircd_log.h */ | |
33 | #include <errno.h> | |
34 | #include <sys/types.h> | |
35 | #ifdef HAVE_STDINT_H | |
36 | #include <stdint.h> /* bah */ | |
37 | #endif | |
38 | #include <string.h> | |
39 | #include <sys/epoll.h> | |
40 | #include <sys/socket.h> | |
41 | #include <time.h> | |
42 | #include <linux/unistd.h> | |
43 | ||
44 | /* The GNU C library may have a valid header but stub implementations | |
45 | * of the epoll system calls. If so, provide our own. */ | |
46 | #if defined(__stub_epoll_create) || defined(__stub___epoll_create) || defined(EPOLL_NEED_BODY) | |
47 | ||
48 | /* Oh, did we mention that some glibc releases do not even define the | |
49 | * syscall numbers? */ | |
50 | #if !defined(__NR_epoll_create) | |
51 | #if defined(__ia64__) | |
52 | #define __NR_epoll_create 1243 | |
53 | #define __NR_epoll_ctl 1244 | |
54 | #define __NR_epoll_wait 1245 | |
55 | #elif defined(__x86_64__) | |
56 | #define __NR_epoll_create 214 | |
57 | #define __NR_epoll_ctl 233 | |
58 | #define __NR_epoll_wait 232 | |
59 | #elif defined(__sparc64__) || defined(__sparc__) | |
60 | #define __NR_epoll_create 193 | |
61 | #define __NR_epoll_ctl 194 | |
62 | #define __NR_epoll_wait 195 | |
63 | #elif defined(__s390__) || defined(__m68k__) | |
64 | #define __NR_epoll_create 249 | |
65 | #define __NR_epoll_ctl 250 | |
66 | #define __NR_epoll_wait 251 | |
67 | #elif defined(__ppc64__) || defined(__ppc__) | |
68 | #define __NR_epoll_create 236 | |
69 | #define __NR_epoll_ctl 237 | |
70 | #define __NR_epoll_wait 238 | |
71 | #elif defined(__parisc__) || defined(__arm26__) || defined(__arm__) | |
72 | #define __NR_epoll_create 224 | |
73 | #define __NR_epoll_ctl 225 | |
74 | #define __NR_epoll_wait 226 | |
75 | #elif defined(__alpha__) | |
76 | #define __NR_epoll_create 407 | |
77 | #define __NR_epoll_ctl 408 | |
78 | #define __NR_epoll_wait 409 | |
79 | #elif defined(__sh64__) | |
80 | #define __NR_epoll_create 282 | |
81 | #define __NR_epoll_ctl 283 | |
82 | #define __NR_epoll_wait 284 | |
83 | #elif defined(__i386__) || defined(__sh__) || defined(__m32r__) || defined(__h8300__) || defined(__frv__) | |
84 | #define __NR_epoll_create 254 | |
85 | #define __NR_epoll_ctl 255 | |
86 | #define __NR_epoll_wait 256 | |
87 | #else /* cpu types */ | |
88 | #error No system call numbers defined for epoll family. | |
89 | #endif /* cpu types */ | |
90 | #endif /* !defined(__NR_epoll_create) */ | |
91 | ||
92 | _syscall1(int, epoll_create, int, size) | |
93 | _syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event) | |
94 | _syscall4(int, epoll_wait, int, epfd, struct epoll_event *, pevents, int, maxevents, int, timeout) | |
95 | ||
96 | #endif /* epoll_create defined as stub */ | |
97 | ||
98 | #define EPOLL_ERROR_THRESHOLD 20 /**< after 20 epoll errors, restart */ | |
99 | #define ERROR_EXPIRE_TIME 3600 /**< expire errors after an hour */ | |
100 | ||
101 | /** File descriptor for epoll pseudo-file. */ | |
102 | static int epoll_fd; | |
103 | /** Number of recent epoll errors. */ | |
104 | static int errors; | |
105 | /** Periodic timer to forget errors. */ | |
106 | static struct Timer clear_error; | |
107 | ||
108 | /** Decrement the error count (once per hour). | |
109 | * @param[in] ev Expired timer event (ignored). | |
110 | */ | |
111 | static void | |
112 | error_clear(struct Event *ev) | |
113 | { | |
114 | if (!--errors) | |
115 | timer_del(ev_timer(ev)); | |
116 | } | |
117 | ||
118 | /** Initialize the epoll engine. | |
119 | * @param[in] max_sockets Maximum number of file descriptors to support. | |
120 | * @return Non-zero on success, or zero on failure. | |
121 | */ | |
122 | static int | |
123 | engine_init(int max_sockets) | |
124 | { | |
125 | if ((epoll_fd = epoll_create(max_sockets)) < 0) { | |
126 | log_write(LS_SYSTEM, L_WARNING, 0, | |
127 | "epoll() engine cannot initialize: %m"); | |
128 | return 0; | |
129 | } | |
130 | return 1; | |
131 | } | |
132 | ||
133 | /** Set events for a particular socket. | |
134 | * @param[in] sock Socket to calculate events for. | |
135 | * @param[in] state Current socket state. | |
136 | * @param[in] events User-specified event interest list. | |
137 | * @param[out] evt epoll event structure for socket. | |
138 | */ | |
139 | static void | |
140 | set_events(struct Socket *sock, enum SocketState state, unsigned int events, struct epoll_event *evt) | |
141 | { | |
142 | assert(0 != sock); | |
143 | assert(0 <= s_fd(sock)); | |
144 | memset(evt, 0, sizeof(*evt)); | |
145 | ||
146 | evt->data.ptr = sock; | |
147 | ||
148 | switch (state) { | |
149 | case SS_CONNECTING: | |
150 | evt->events = EPOLLOUT; | |
151 | break; | |
152 | ||
153 | case SS_LISTENING: | |
154 | case SS_NOTSOCK: | |
155 | evt->events = EPOLLIN; | |
156 | break; | |
157 | ||
158 | case SS_CONNECTED: | |
159 | case SS_DATAGRAM: | |
160 | case SS_CONNECTDG: | |
161 | switch (events & SOCK_EVENT_MASK) { | |
162 | case 0: | |
163 | evt->events = 0; | |
164 | break; | |
165 | case SOCK_EVENT_READABLE: | |
166 | evt->events = EPOLLIN; | |
167 | break; | |
168 | case SOCK_EVENT_WRITABLE: | |
169 | evt->events = EPOLLOUT; | |
170 | break; | |
171 | case SOCK_EVENT_READABLE|SOCK_EVENT_WRITABLE: | |
172 | evt->events = EPOLLIN|EPOLLOUT; | |
173 | break; | |
174 | } | |
175 | break; | |
176 | } | |
177 | } | |
178 | ||
179 | /** Add a socket to the event engine. | |
180 | * @param[in] sock Socket to add to engine. | |
181 | * @return Non-zero on success, or zero on error. | |
182 | */ | |
183 | static int | |
184 | engine_add(struct Socket *sock) | |
185 | { | |
186 | struct epoll_event evt; | |
187 | ||
188 | assert(0 != sock); | |
189 | Debug((DEBUG_ENGINE, "epoll: Adding socket %d [%p], state %s, to engine", | |
190 | s_fd(sock), sock, state_to_name(s_state(sock)))); | |
191 | set_events(sock, s_state(sock), s_events(sock), &evt); | |
192 | if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s_fd(sock), &evt) < 0) { | |
193 | event_generate(ET_ERROR, sock, errno); | |
194 | return 0; | |
195 | } | |
196 | return 1; | |
197 | } | |
198 | ||
199 | /** Handle state transition for a socket. | |
200 | * @param[in] sock Socket changing state. | |
201 | * @param[in] new_state New state for socket. | |
202 | */ | |
203 | static void | |
204 | engine_set_state(struct Socket *sock, enum SocketState new_state) | |
205 | { | |
206 | struct epoll_event evt; | |
207 | ||
208 | assert(0 != sock); | |
209 | Debug((DEBUG_ENGINE, "epoll: Changing state for socket %p to %s", | |
210 | sock, state_to_name(new_state))); | |
211 | set_events(sock, new_state, s_events(sock), &evt); | |
212 | if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, s_fd(sock), &evt) < 0) | |
213 | event_generate(ET_ERROR, sock, errno); | |
214 | } | |
215 | ||
216 | /** Handle change to preferred socket events. | |
217 | * @param[in] sock Socket getting new interest list. | |
218 | * @param[in] new_events New set of interesting events for socket. | |
219 | */ | |
220 | static void | |
221 | engine_set_events(struct Socket *sock, unsigned new_events) | |
222 | { | |
223 | struct epoll_event evt; | |
224 | ||
225 | assert(0 != sock); | |
226 | Debug((DEBUG_ENGINE, "epoll: Changing event mask for socket %p to [%s]", | |
227 | sock, sock_flags(new_events))); | |
228 | set_events(sock, s_state(sock), new_events, &evt); | |
229 | if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, s_fd(sock), &evt) < 0) | |
230 | event_generate(ET_ERROR, sock, errno); | |
231 | } | |
232 | ||
233 | /** Remove a socket from the event engine. | |
234 | * @param[in] sock Socket being destroyed. | |
235 | */ | |
236 | static void | |
237 | engine_delete(struct Socket *sock) | |
238 | { | |
239 | assert(0 != sock); | |
240 | Debug((DEBUG_ENGINE, "epoll: Deleting socket %d [%p], state %s", | |
241 | s_fd(sock), sock, state_to_name(s_state(sock)))); | |
242 | /* No action necessary; epoll removes the socket on close(). */ | |
243 | } | |
244 | ||
245 | /** Run engine event loop. | |
246 | * @param[in] gen Lists of generators of various types. | |
247 | */ | |
248 | static void | |
249 | engine_loop(struct Generators *gen) | |
250 | { | |
251 | struct epoll_event *events; | |
252 | struct Socket *sock; | |
253 | socklen_t codesize; | |
254 | int events_count, i, wait, nevs, errcode; | |
255 | ||
256 | if ((events_count = feature_int(FEAT_POLLS_PER_LOOP)) < 20) | |
257 | events_count = 20; | |
258 | events = MyMalloc(sizeof(events[0]) * events_count); | |
259 | while (running) { | |
260 | if ((i = feature_int(FEAT_POLLS_PER_LOOP)) >= 20 && i != events_count) { | |
261 | events = MyRealloc(events, sizeof(events[0]) * i); | |
262 | events_count = i; | |
263 | } | |
264 | ||
265 | wait = timer_next(gen) ? (timer_next(gen) - CurrentTime) * 1000 : -1; | |
266 | Debug((DEBUG_ENGINE, "epoll: delay: %d (%d) %d", timer_next(gen), | |
267 | CurrentTime, wait)); | |
268 | nevs = epoll_wait(epoll_fd, events, events_count, wait); | |
269 | CurrentTime = time(0); | |
270 | ||
271 | if (nevs < 0) { | |
272 | if (errno != EINTR) { | |
273 | log_write(LS_SOCKET, L_ERROR, 0, "epoll() error: %m"); | |
274 | if (!errors++) | |
275 | timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, | |
276 | ERROR_EXPIRE_TIME); | |
277 | else if (errors > EPOLL_ERROR_THRESHOLD) | |
278 | server_restart("too many epoll errors"); | |
279 | } | |
280 | continue; | |
281 | } | |
282 | ||
283 | for (i = 0; i < nevs; i++) { | |
284 | if (!(sock = events[i].data.ptr)) | |
285 | continue; | |
286 | gen_ref_inc(sock); | |
287 | Debug((DEBUG_ENGINE, | |
288 | "epoll: Checking socket %p (fd %d) state %s, events %s", | |
289 | sock, s_fd(sock), state_to_name(s_state(sock)), | |
290 | sock_flags(s_events(sock)))); | |
291 | ||
292 | if (events[i].events & EPOLLERR) { | |
293 | errcode = 0; | |
294 | codesize = sizeof(errcode); | |
295 | if (getsockopt(s_fd(sock), SOL_SOCKET, SO_ERROR, &errcode, | |
296 | &codesize) < 0) | |
297 | errcode = errno; | |
298 | if (errcode) { | |
299 | event_generate(ET_ERROR, sock, errcode); | |
300 | gen_ref_dec(sock); | |
301 | continue; | |
302 | } | |
303 | } else if (events[i].events & EPOLLHUP) { | |
304 | event_generate(ET_EOF, sock, 0); | |
305 | } else switch (s_state(sock)) { | |
306 | case SS_CONNECTING: | |
307 | if (events[i].events & EPOLLOUT) /* connection completed */ | |
308 | event_generate(ET_CONNECT, sock, 0); | |
309 | break; | |
310 | ||
311 | case SS_LISTENING: | |
312 | if (events[i].events & EPOLLIN) /* incoming connection */ | |
313 | event_generate(ET_ACCEPT, sock, 0); | |
314 | break; | |
315 | ||
316 | case SS_NOTSOCK: | |
317 | case SS_CONNECTED: | |
318 | case SS_DATAGRAM: | |
319 | case SS_CONNECTDG: | |
320 | if (events[i].events & EPOLLIN) | |
321 | event_generate(ET_READ, sock, 0); | |
322 | if (events[i].events & EPOLLOUT) | |
323 | event_generate(ET_WRITE, sock, 0); | |
324 | break; | |
325 | } | |
326 | gen_ref_dec(sock); | |
327 | } | |
328 | timer_run(); | |
329 | } | |
330 | MyFree(events); | |
331 | } | |
332 | ||
333 | /** Descriptor for epoll event engine. */ | |
334 | struct Engine engine_epoll = { | |
335 | "epoll()", | |
336 | engine_init, | |
337 | 0, | |
338 | engine_add, | |
339 | engine_set_state, | |
340 | engine_set_events, | |
341 | engine_delete, | |
342 | engine_loop | |
343 | }; |