Fixes to improve portability (especially to OS X, Solaris, OpenBSD).
[ircu2.10.12-pk.git] / ircd / engine_epoll.c
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  * $Id$
20  */
21 #include "config.h"
22
23 #include "ircd.h"
24 #include "ircd_events.h"
25 #include "ircd_alloc.h"
26 #include "ircd_features.h"
27 #include "ircd_log.h"
28 #include "s_debug.h"
29
30 #include <assert.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #ifdef HAVE_STDINT_H
34 #include <stdint.h> /* bah */
35 #endif
36 #include <string.h>
37 #include <sys/epoll.h>
38 #include <sys/socket.h>
39 #include <time.h>
40 #include <linux/unistd.h>
41
42 /* The GNU C library may have a valid header but stub implementations
43  * of the epoll system calls.  If so, provide our own. */
44 #if defined(__stub_epoll_create) || defined(__stub___epoll_create) || defined(EPOLL_NEED_BODY)
45
46 /* Oh, did we mention that some glibc releases do not even define the
47  * syscall numbers? */
48 #if !defined(__NR_epoll_create)
49 #if defined(__i386__)
50 #define __NR_epoll_create 254
51 #define __NR_epoll_ctl 255
52 #define __NR_epoll_wait 256
53 #elif defined(__ia64__)
54 #define __NR_epoll_create 1243
55 #define __NR_epoll_ctl 1244
56 #define __NR_epoll_wait 1245
57 #elif defined(__x86_64__)
58 #define __NR_epoll_create 214
59 #define __NR_epoll_ctl 233
60 #define __NR_epoll_wait 232
61 #else /* cpu types */
62 #error No system call numbers defined for epoll family.
63 #endif /* cpu types */
64 #endif /* !defined(__NR_epoll_create) */
65
66 _syscall1(int, epoll_create, int, size)
67 _syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event)
68 _syscall4(int, epoll_wait, int, epfd, struct epoll_event *, pevents, int, maxevents, int, timeout)
69
70 #endif /* epoll_create defined as stub */
71
72 #define EPOLL_ERROR_THRESHOLD 20   /* after 20 epoll errors, restart */
73 #define ERROR_EXPIRE_TIME     3600 /* expire errors after an hour */
74
75 static int epoll_fd;
76 static int errors;
77 static struct Timer clear_error;
78
79 /* decrements the error count once per hour */
80 static void
81 error_clear(struct Event *ev)
82 {
83   if (!--errors)
84     timer_del(ev_timer(ev));
85 }
86
87 /* initialize the epoll engine */
88 static int
89 engine_init(int max_sockets)
90 {
91   if ((epoll_fd = epoll_create(max_sockets)) < 0) {
92     log_write(LS_SYSTEM, L_WARNING, 0,
93               "epoll() engine cannot initialize: %m");
94     return 0;
95   }
96   return 1;
97 }
98
99 static void
100 set_events(struct Socket *sock, enum SocketState state, unsigned int events, struct epoll_event *evt)
101 {
102   assert(0 != sock);
103   assert(0 <= s_fd(sock));
104   memset(evt, 0, sizeof(*evt));
105
106   evt->data.ptr = sock;
107
108   switch (state) {
109   case SS_CONNECTING:
110     evt->events = EPOLLOUT;
111     break;
112
113   case SS_LISTENING:
114   case SS_NOTSOCK:
115     evt->events = EPOLLIN;
116     break;
117
118   case SS_CONNECTED:
119   case SS_DATAGRAM:
120   case SS_CONNECTDG:
121     switch (events & SOCK_EVENT_MASK) {
122     case 0:
123       evt->events = 0;
124       break;
125     case SOCK_EVENT_READABLE:
126       evt->events = EPOLLIN;
127       break;
128     case SOCK_EVENT_WRITABLE:
129       evt->events = EPOLLOUT;
130       break;
131     case SOCK_EVENT_READABLE|SOCK_EVENT_WRITABLE:
132       evt->events = EPOLLIN|EPOLLOUT;
133       break;
134     }
135     break;
136   }
137 }
138
139 static int
140 engine_add(struct Socket *sock)
141 {
142   struct epoll_event evt;
143
144   assert(0 != sock);
145   Debug((DEBUG_ENGINE, "epoll: Adding socket %d [%p], state %s, to engine",
146          s_fd(sock), sock, state_to_name(s_state(sock))));
147   set_events(sock, s_state(sock), s_events(sock), &evt);
148   if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s_fd(sock), &evt) < 0) {
149     event_generate(ET_ERROR, sock, errno);
150     return 0;
151   }
152   return 1;
153 }
154
155 static void
156 engine_set_state(struct Socket *sock, enum SocketState new_state)
157 {
158   struct epoll_event evt;
159
160   assert(0 != sock);
161   Debug((DEBUG_ENGINE, "epoll: Changing state for socket %p to %s",
162          sock, state_to_name(new_state)));
163   set_events(sock, new_state, s_events(sock), &evt);
164   if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, s_fd(sock), &evt) < 0)
165     event_generate(ET_ERROR, sock, errno);
166 }
167
168 static void
169 engine_set_events(struct Socket *sock, unsigned new_events)
170 {
171   struct epoll_event evt;
172
173   assert(0 != sock);
174   Debug((DEBUG_ENGINE, "epoll: Changing event mask for socket %p to [%s]",
175          sock, sock_flags(new_events)));
176   set_events(sock, s_state(sock), new_events, &evt);
177   if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, s_fd(sock), &evt) < 0)
178     event_generate(ET_ERROR, sock, errno);
179 }
180
181 static void
182 engine_delete(struct Socket *sock)
183 {
184   struct epoll_event evt;
185
186   assert(0 != sock);
187   memset(&evt, 0, sizeof(evt));
188   Debug((DEBUG_ENGINE, "epoll: Deleting socket %d [%p], state %s",
189          s_fd(sock), sock, state_to_name(s_state(sock))));
190   if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, s_fd(sock), &evt) < 0)
191     log_write(LS_SOCKET, L_WARNING, 0,
192               "Unable to delete epoll item for socket %d", s_fd(sock));
193 }
194
195 static void
196 engine_loop(struct Generators *gen)
197 {
198   struct epoll_event *events;
199   struct Socket *sock;
200   size_t codesize;
201   int events_count, i, wait, nevs, errcode;
202
203   if ((events_count = feature_int(FEAT_POLLS_PER_LOOP)) < 20)
204     events_count = 20;
205   events = MyMalloc(sizeof(events[0]) * events_count);
206   while (running) {
207     if ((i = feature_int(FEAT_POLLS_PER_LOOP)) >= 20 && i != events_count) {
208       events = MyRealloc(events, sizeof(events[0]) * i);
209       events_count = i;
210     }
211
212     wait = timer_next(gen) ? (timer_next(gen) - CurrentTime) * 1000 : -1;
213     Debug((DEBUG_INFO, "epoll: delay: %d (%d) %d", timer_next(gen),
214            CurrentTime, wait));
215     nevs = epoll_wait(epoll_fd, events, events_count, wait);
216     CurrentTime = time(0);
217
218     if (nevs < 0) {
219       if (errno != EINTR) {
220         log_write(LS_SOCKET, L_ERROR, 0, "epoll() error: %m");
221         if (!errors++)
222           timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC,
223                     ERROR_EXPIRE_TIME);
224         else if (errors > EPOLL_ERROR_THRESHOLD)
225           server_restart("too many epoll errors");
226       }
227       continue;
228     }
229
230     for (i = 0; i < nevs; i++) {
231       if (!(sock = events[i].data.ptr))
232         continue;
233       gen_ref_inc(sock);
234       Debug((DEBUG_ENGINE,
235              "epoll: Checking socket %p (fd %d) state %s, events %s",
236              sock, s_fd(sock), state_to_name(s_state(sock)),
237              sock_flags(s_events(sock))));
238
239       if (events[i].events & EPOLLERR) {
240         errcode = 0;
241         codesize = sizeof(errcode);
242         if (getsockopt(s_fd(sock), SOL_SOCKET, SO_ERROR, &errcode,
243                        &codesize) < 0)
244           errcode = errno;
245         if (errcode) {
246           event_generate(ET_ERROR, sock, errcode);
247           gen_ref_dec(sock);
248           continue;
249         }
250       }
251
252       switch (s_state(sock)) {
253       case SS_CONNECTING:
254         if (events[i].events & EPOLLOUT) /* connection completed */
255           event_generate(ET_CONNECT, sock, 0);
256         break;
257
258       case SS_LISTENING:
259         if (events[i].events & EPOLLIN) /* incoming connection */
260           event_generate(ET_ACCEPT, sock, 0);
261         break;
262
263       case SS_NOTSOCK:
264       case SS_CONNECTED:
265         if (events[i].events & EPOLLIN)
266           event_generate((events[i].events & EPOLLHUP) ? ET_EOF : ET_READ, sock, 0);
267         if (events[i].events & EPOLLOUT)
268           event_generate(ET_WRITE, sock, 0);
269         break;
270
271       case SS_DATAGRAM:
272       case SS_CONNECTDG:
273         if (events[i].events & EPOLLIN)
274           event_generate(ET_READ, sock, 0);
275         if (events[i].events & EPOLLOUT)
276           event_generate(ET_WRITE, sock, 0);
277         break;
278       }
279       gen_ref_dec(sock);
280     }
281     timer_run();
282   }
283 }
284
285 struct Engine engine_epoll = {
286   "epoll()",
287   engine_init,
288   0,
289   engine_add,
290   engine_set_state,
291   engine_set_events,
292   engine_delete,
293   engine_loop
294 };