ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/branches/newio/src/ioengine_devpoll.c
Revision: 2385
Committed: Sat Jul 6 20:31:15 2013 UTC (10 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 13530 byte(s)
Log Message:
- Add various socket engines

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 2001 Kevin L. Mitchell <klmitch@mit.edu>
5 * Copyright (C) 2013 by the Hybrid Development Team.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 */
22
23 /*! \file ioengine_devpoll.c
24 * \brief Solaris /dev/poll event engine.
25 * \version $Id: ioengine_devpoll.c 2297 2013-06-19 11:57:38Z michael $
26 */
27
28 #include "stdinc.h"
29 #include "ioengine.h"
30 #include "ircd.h"
31 #include "memory.h"
32 #include "log.h"
33 #include "restart.h"
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/devpoll.h>
38 #include <sys/poll.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43
44 #define DEVPOLL_ERROR_THRESHOLD 20 /**< after 20 devpoll errors, restart */
45 #define ERROR_EXPIRE_TIME 3600 /**< expire errors after an hour */
46
47 /* Figure out what bits to set for read */
48 #if defined(POLLMSG) && defined(POLLIN) && defined(POLLRDNORM)
49 # define POLLREADFLAGS (POLLMSG|POLLIN|POLLRDNORM)
50 #elif defined(POLLIN) && defined(POLLRDNORM)
51 # define POLLREADFLAGS (POLLIN|POLLRDNORM)
52 #elif defined(POLLIN)
53 # define POLLREADFLAGS POLLIN
54 #elif defined(POLLRDNORM)
55 # define POLLREADFLAGS POLLRDNORM
56 #endif
57
58 /* Figure out what bits to set for write */
59 #if defined(POLLOUT) && defined(POLLWRNORM)
60 # define POLLWRITEFLAGS (POLLOUT|POLLWRNORM)
61 #elif defined(POLLOUT)
62 # define POLLWRITEFLAGS POLLOUT
63 #elif defined(POLLWRNORM)
64 # define POLLWRITEFLAGS POLLWRNORM
65 #endif
66
67 /** Array of active Socket structures, indexed by file descriptor. */
68 static struct Socket **sockList;
69 /** Maximum file descriptor supported, plus one. */
70 static int devpoll_max;
71 /** File descriptor for /dev/poll device. */
72 static int devpoll_fd;
73
74 /** Number of recent errors from /dev/poll. */
75 static int errors;
76 /** Periodic timer to forget errors. */
77 static struct Timer clear_error;
78
79 /** Decrement the error count (once per hour).
80 * @param[in] ev Expired timer event (ignored).
81 */
82 static void
83 error_clear(struct Event *ev)
84 {
85 if (!--errors) /* Remove timer when error count reaches 0 */
86 timer_del(ev_timer(ev));
87 }
88
89 /** Initialize the /dev/poll engine.
90 * @param[in] max_sockets Maximum number of file descriptors to support.
91 * @return Non-zero on success, or zero on failure.
92 */
93 static int
94 engine_init(int max_sockets)
95 {
96 int i;
97
98 if ((devpoll_fd = open("/dev/poll", O_RDWR)) < 0)
99 {
100 ilog(LOG_TYPE_DEBUG, "/dev/poll engine cannot open device: %m");
101 return 0; /* engine cannot be initialized; defer */
102 }
103
104 /* allocate necessary memory */
105 sockList = (struct Socket **)MyMalloc(sizeof(struct Socket *) * max_sockets);
106
107 /* initialize the data */
108 for (i = 0; i < max_sockets; ++i)
109 sockList[i] = 0;
110
111 devpoll_max = max_sockets; /* Number of sockets allocated */
112
113 return 1;
114 }
115
116 /** Figure out what events go with a given state.
117 * @param[in] state %Socket state to consider.
118 * @param[in] events User-specified preferred event set.
119 * @return Actual set of preferred events.
120 */
121 static unsigned int
122 state_to_events(enum SocketState state, unsigned int events)
123 {
124 switch (state)
125 {
126 case SS_CONNECTING: /* connecting socket */
127 return SOCK_EVENT_WRITABLE;
128 break;
129
130 case SS_LISTENING: /* listening socket */
131 case SS_NOTSOCK: /* our signal socket */
132 return SOCK_EVENT_READABLE;
133 break;
134
135 case SS_CONNECTED:
136 case SS_DATAGRAM:
137 case SS_CONNECTDG:
138 return events; /* ordinary socket */
139 break;
140 }
141
142 /*NOTREACHED*/
143 return 0;
144 }
145
146 /** Set the desired events for a socket.
147 * @param[in,out] sock Socket to operate on.
148 * @param[in] events User-specified preferred event set.
149 */
150 static void
151 set_events(struct Socket *sock, unsigned int events)
152 {
153 struct pollfd pfd;
154
155 pfd.fd = s_fd(sock);
156
157 if (s_ed_int(sock))
158 {
159 /* is one in /dev/poll already? */
160 pfd.events = POLLREMOVE; /* First, remove old pollfd */
161
162 ilog(LOG_TYPE_DEBUG, "devpoll: Removing old entry for socket %d [%p]",
163 s_fd(sock), sock);
164
165 if (write(devpoll_fd, &pfd, sizeof(pfd)) != sizeof(pfd))
166 {
167 event_generate(ET_ERROR, sock, errno); /* report error */
168 return;
169 }
170
171 s_ed_int(sock) = 0; /* mark that it's gone */
172 }
173
174 if (!(events & SOCK_EVENT_MASK)) /* no events, so stop here */
175 return;
176
177 pfd.events = 0; /* Now, set up new pollfd... */
178 if (events & SOCK_EVENT_READABLE)
179 pfd.events |= POLLREADFLAGS; /* look for readable conditions */
180 if (events & SOCK_EVENT_WRITABLE)
181 pfd.events |= POLLWRITEFLAGS; /* look for writable conditions */
182
183 ilog(LOG_TYPE_DEBUG, "devpoll: Registering interest on %d [%p] (state %s, "
184 "mask [%s])", s_fd(sock), sock, state_to_name(s_state(sock)),
185 sock_flags(s_events(sock)));
186
187 if (write(devpoll_fd, &pfd, sizeof(pfd)) != sizeof(pfd))
188 {
189 event_generate(ET_ERROR, sock, errno); /* report error */
190 return;
191 }
192
193 s_ed_int(sock) = 1; /* mark that we've added a pollfd */
194 }
195
196 /** Add a socket to the event engine.
197 * @param[in] sock Socket to add to engine.
198 * @return Non-zero on success, or zero on error.
199 */
200 static int
201 engine_add(struct Socket* sock)
202 {
203 assert(0 != sock);
204 assert(0 == sockList[s_fd(sock)]);
205
206 /* bounds-check... */
207 if (s_fd(sock) >= devpoll_max)
208 {
209 ilog(LOG_TYPE_DEBUG,
210 "Attempt to add socket %d (> %d) to event engine",
211 s_fd(sock), devpoll_max);
212 return 0;
213 }
214
215 sockList[s_fd(sock)] = sock; /* add to list */
216
217 ilog(LOG_TYPE_DEBUG, "devpoll: Adding socket %d [%p], state %s, to engine",
218 s_fd(sock), sock, state_to_name(s_state(sock)));
219
220 /* set the correct events */
221 set_events(sock, state_to_events(s_state(sock), s_events(sock)));
222
223 return 1; /* success */
224 }
225
226 /** Handle state transition for a socket.
227 * @param[in] sock Socket changing state.
228 * @param[in] new_state New state for socket.
229 */
230 static void
231 engine_state(struct Socket* sock, enum SocketState new_state)
232 {
233 assert(0 != sock);
234 assert(sock == sockList[s_fd(sock)]);
235
236 ilog(LOG_TYPE_DEBUG, "devpoll: Changing state for socket %p to %s", sock,
237 state_to_name(new_state));
238
239 /* set the correct events */
240 set_events(sock, state_to_events(new_state, s_events(sock)));
241 }
242
243 /** Handle change to preferred socket events.
244 * @param[in] sock Socket getting new interest list.
245 * @param[in] new_events New set of interesting events for socket.
246 */
247 static void
248 engine_events(struct Socket* sock, unsigned int new_events)
249 {
250 assert(0 != sock);
251 assert(sock == sockList[s_fd(sock)]);
252
253 ilog(LOG_TYPE_DEBUG,"devpoll: Changing event mask for socket %p to [%s]",
254 sock, sock_flags(new_events));
255
256 /* set the correct events */
257 set_events(sock, state_to_events(s_state(sock), new_events));
258 }
259
260 /** Remove a socket from the event engine.
261 * @param[in] sock Socket being destroyed.
262 */
263 static void
264 engine_delete(struct Socket* sock)
265 {
266 assert(0 != sock);
267 assert(sock == sockList[s_fd(sock)]);
268
269 ilog(LOG_TYPE_DEBUG, "devpoll: Deleting socket %d [%p], state %s",
270 s_fd(sock), sock, state_to_name(s_state(sock)));
271
272 set_events(sock, 0); /* get rid of the socket */
273
274 sockList[s_fd(sock)] = 0; /* zero the socket list entry */
275 }
276
277 /** Run engine event loop.
278 * @param[in] gen Lists of generators of various types.
279 */
280 static void
281 engine_loop(struct Generators* gen)
282 {
283 struct dvpoll dopoll;
284 struct pollfd *polls;
285 int polls_count;
286 struct Socket* sock;
287 int nfds;
288 int i;
289 int errcode;
290 size_t codesize;
291
292 if ((polls_count = 64) < 20) /* XXX */
293 polls_count = 20;
294 polls = MyMalloc(sizeof(struct pollfd) * polls_count);
295
296 while (running)
297 {
298 if ((i = 64 /* XXX */) >= 20 && i != polls_count)
299 {
300 polls = MyRealloc(polls, sizeof(struct pollfd) * i);
301 polls_count = i;
302 }
303
304 dopoll.dp_fds = polls; /* Set up the struct dvpoll */
305 dopoll.dp_nfds = polls_count;
306
307 /* calculate the proper timeout */
308 dopoll.dp_timeout = timer_next(gen) ? (timer_next(gen) - CurrentTime) * 1000 : -1;
309
310 ilog(LOG_TYPE_DEBUG, "devpoll: delay: %Tu (%Tu) %d", timer_next(gen),
311 CurrentTime, dopoll.dp_timeout);
312
313 /* check for active files */
314 nfds = ioctl(devpoll_fd, DP_POLL, &dopoll);
315
316 CurrentTime = time(0); /* set current time... */
317
318 if (nfds < 0)
319 {
320 if (errno != EINTR)
321 {
322 /* ignore interrupts */
323 /* Log the poll error */
324 ilog(LOG_TYPE_DEBUG, "ioctl(DP_POLL) error: %m");
325
326 if (!errors++)
327 timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME);
328 else if (errors > DEVPOLL_ERROR_THRESHOLD) /* Too many errors... */
329 restart("too many /dev/poll errors");
330 }
331
332 /*
333 * Old code did a sleep(1) here; with usage these days,
334 * that may be too expensive
335 */
336 continue;
337 }
338
339 for (i = 0; i < nfds; i++)
340 {
341 assert(-1 < polls[i].fd);
342
343 sock = sockList[polls[i].fd];
344 if (!sock) /* slots may become empty while processing events */
345 continue;
346
347 assert(s_fd(sock) == polls[i].fd);
348
349 gen_ref_inc(sock); /* can't have it going away on us */
350
351 ilog(LOG_TYPE_DEBUG, "devpoll: Checking socket %p (fd %d) state %s, "
352 "events %s", sock, s_fd(sock), state_to_name(s_state(sock)),
353 sock_flags(s_events(sock)));
354
355 if (s_state(sock) != SS_NOTSOCK)
356 {
357 errcode = 0; /* check for errors on socket */
358 codesize = sizeof(errcode);
359
360 if (getsockopt(s_fd(sock), SOL_SOCKET, SO_ERROR, &errcode, &codesize) < 0)
361 errcode = errno; /* work around Solaris implementation */
362
363 if (errcode)
364 {
365 /* an error occurred; generate an event */
366 ilog(LOG_TYPE_DEBUG, "devpoll: Error %d on fd %d, socket %p",
367 errcode, s_fd(sock), sock);
368 event_generate(ET_ERROR, sock, errcode);
369 gen_ref_dec(sock); /* careful not to leak reference counts */
370 continue;
371 }
372 }
373
374 assert(!(polls[i].revents & POLLERR));
375
376 #ifdef POLLHUP
377 if (polls[i].revents & POLLHUP)
378 {
379 /* hang-up on socket */
380 ilog(LOG_TYPE_DEBUG, "devpoll: EOF from client (POLLHUP)");
381 event_generate(ET_EOF, sock, 0);
382 nfds--;
383 continue;
384 }
385 #endif /* POLLHUP */
386
387 switch (s_state(sock))
388 {
389 case SS_CONNECTING:
390 if (polls[i].revents & POLLWRITEFLAGS)
391 {
392 /* connection completed */
393 ilog(LOG_TYPE_DEBUG, "devpoll: Connection completed");
394 event_generate(ET_CONNECT, sock, 0);
395 }
396 break;
397
398 case SS_LISTENING:
399 if (polls[i].revents & POLLREADFLAGS)
400 {
401 /* connect. to be accept. */
402 ilog(LOG_TYPE_DEBUG, "devpoll: Ready for accept");
403 event_generate(ET_ACCEPT, sock, 0);
404 }
405 break;
406
407 case SS_NOTSOCK:
408 if (polls[i].revents & POLLREADFLAGS)
409 {
410 /* data on socket */
411 /* can't peek; it's not a socket */
412 ilog(LOG_TYPE_DEBUG, "devpoll: non-socket readable");
413 event_generate(ET_READ, sock, 0);
414 }
415 break;
416
417 case SS_CONNECTED:
418 if (polls[i].revents & POLLREADFLAGS)
419 {
420 /* data on socket */
421 char c;
422
423 switch (recv(s_fd(sock), &c, 1, MSG_PEEK))
424 {
425 /* check EOF */
426 case -1: /* error occurred?!? */
427 if (errno == EAGAIN)
428 {
429 ilog(LOG_TYPE_DEBUG, "devpoll: Resource temporarily "
430 "unavailable?");
431 continue;
432 }
433
434 ilog(LOG_TYPE_DEBUG, "devpoll: Uncaught error!");
435 event_generate(ET_ERROR, sock, errno);
436 break;
437
438 case 0: /* EOF from client */
439 ilog(LOG_TYPE_DEBUG, "devpoll: EOF from client");
440 event_generate(ET_EOF, sock, 0);
441 break;
442
443 default: /* some data can be read */
444 ilog(LOG_TYPE_DEBUG, "devpoll: Data to be read");
445 event_generate(ET_READ, sock, 0);
446 break;
447 }
448 }
449
450 if (polls[i].revents & POLLWRITEFLAGS)
451 {
452 /* socket writable */
453 ilog(LOG_TYPE_DEBUG, "devpoll: Data can be written");
454 event_generate(ET_WRITE, sock, 0);
455 }
456 break;
457
458 case SS_DATAGRAM: case SS_CONNECTDG:
459 if (polls[i].revents & POLLREADFLAGS)
460 {
461 /* socket readable */
462 ilog(LOG_TYPE_DEBUG, "devpoll: Datagram to be read");
463 event_generate(ET_READ, sock, 0);
464 }
465
466 if (polls[i].revents & POLLWRITEFLAGS)
467 {
468 /* socket writable */
469 ilog(LOG_TYPE_DEBUG, "devpoll: Datagram can be written");
470 event_generate(ET_WRITE, sock, 0);
471 }
472 break;
473 }
474
475 gen_ref_dec(sock); /* we're done with it */
476 }
477
478 timer_run(); /* execute any pending timers */
479 }
480 }
481
482 /** Descriptor for /dev/poll event engine. */
483 struct Engine engine_devpoll =
484 {
485 "/dev/poll", /* Engine name */
486 engine_init, /* Engine initialization function */
487 0, /* Engine signal registration function */
488 engine_add, /* Engine socket registration function */
489 engine_state, /* Engine socket state change function */
490 engine_events, /* Engine socket events mask function */
491 engine_delete, /* Engine socket deletion function */
492 engine_loop /* Core engine event loop */
493 };