]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/engine_epoll.c
Should be unsigned long for A
[irc/quakenet/snircd.git] / ircd / engine_epoll.c
CommitLineData
189935b1 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. */
102static int epoll_fd;
103/** Number of recent epoll errors. */
104static int errors;
105/** Periodic timer to forget errors. */
106static struct Timer clear_error;
107
108/** Decrement the error count (once per hour).
109 * @param[in] ev Expired timer event (ignored).
110 */
111static void
112error_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 */
122static int
123engine_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 */
139static void
140set_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 */
183static int
184engine_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 */
203static void
204engine_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 */
220static void
221engine_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 */
236static void
237engine_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 */
248static void
249engine_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. */
334struct 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};