2 * ircd-ratbox: A slightly useful ircd.
3 * epoll.c: Linux epoll compatible network routines.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2001 Adrian Chadd <adrian@creative.net.au>
8 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * Copyright (C) 2002 Aaron Sethman <androsyn@ratbox.org>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
30 #include <librb_config.h>
32 #include <commio-int.h>
33 #include <event-int.h>
34 #if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H)
37 #include <sys/epoll.h>
39 #if defined(HAVE_SIGNALFD) && defined(HAVE_SYS_SIGNALFD_H) && defined(USE_TIMER_CREATE)
41 #include <sys/signalfd.h>
43 #define EPOLL_SCHED_EVENT 1
46 #ifdef USE_TIMERFD_CREATE
47 #include <sys/timerfd.h>
50 #define RTSIGNAL SIGRTMIN
54 struct epoll_event
*pfd
;
58 static struct epoll_info
*ep_info
;
59 static int can_do_event
;
60 static int can_do_timerfd
;
65 * This is a needed exported function which will be called to initialise
66 * the network loop code.
69 rb_init_netio_epoll(void)
71 can_do_event
= 0; /* shut up gcc */
73 ep_info
= rb_malloc(sizeof(struct epoll_info
));
74 ep_info
->pfd_size
= getdtablesize();
75 ep_info
->ep
= epoll_create(ep_info
->pfd_size
);
80 rb_open(ep_info
->ep
, RB_FD_UNKNOWN
, "epoll file descriptor");
81 ep_info
->pfd
= rb_malloc(sizeof(struct epoll_event
) * ep_info
->pfd_size
);
87 rb_setup_fd_epoll(rb_fde_t
*F
__attribute__((unused
)))
96 * This is a needed exported function which will be called to register
97 * and deregister interest in a pending IO state for a given FD.
100 rb_setselect_epoll(rb_fde_t
*F
, unsigned int type
, PF
* handler
, void *client_data
)
102 struct epoll_event ep_event
;
103 int old_flags
= F
->pflags
;
106 lrb_assert(IsFDOpen(F
));
108 /* Update the list, even though we're not using it .. */
109 if(type
& RB_SELECT_READ
)
112 F
->pflags
|= EPOLLIN
;
114 F
->pflags
&= ~EPOLLIN
;
115 F
->read_handler
= handler
;
116 F
->read_data
= client_data
;
119 if(type
& RB_SELECT_WRITE
)
122 F
->pflags
|= EPOLLOUT
;
124 F
->pflags
&= ~EPOLLOUT
;
125 F
->write_handler
= handler
;
126 F
->write_data
= client_data
;
129 if(old_flags
== 0 && F
->pflags
== 0)
131 else if(F
->pflags
<= 0)
133 else if(old_flags
== 0 && F
->pflags
> 0)
135 else if(F
->pflags
!= old_flags
)
141 ep_event
.events
= F
->pflags
;
142 ep_event
.data
.ptr
= F
;
144 if(op
== EPOLL_CTL_ADD
|| op
== EPOLL_CTL_MOD
)
145 ep_event
.events
|= EPOLLET
;
147 if(epoll_ctl(ep_info
->ep
, op
, F
->fd
, &ep_event
) != 0)
149 rb_lib_log("rb_setselect_epoll(): epoll_ctl failed: %s", strerror(errno
));
159 * Called to do the new-style IO, courtesy of squid (like most of this
160 * new IO code). This routine handles the stuff we've hidden in
161 * rb_setselect and fd_table[] and calls callbacks for IO ready
166 rb_select_epoll(long delay
)
168 int num
, i
, flags
, old_flags
, op
;
169 struct epoll_event ep_event
;
173 num
= epoll_wait(ep_info
->ep
, ep_info
->pfd
, ep_info
->pfd_size
, delay
);
175 /* save errno as rb_set_time() will likely clobber it */
180 if(num
< 0 && !rb_ignore_errno(o_errno
))
186 for(i
= 0; i
< num
; i
++)
189 rb_fde_t
*F
= ep_info
->pfd
[i
].data
.ptr
;
190 old_flags
= F
->pflags
;
191 if(ep_info
->pfd
[i
].events
& (EPOLLIN
| EPOLLHUP
| EPOLLERR
))
193 hdl
= F
->read_handler
;
195 F
->read_handler
= NULL
;
205 if(ep_info
->pfd
[i
].events
& (EPOLLOUT
| EPOLLHUP
| EPOLLERR
))
207 hdl
= F
->write_handler
;
208 data
= F
->write_data
;
209 F
->write_handler
= NULL
;
210 F
->write_data
= NULL
;
223 if(F
->read_handler
!= NULL
)
225 if(F
->write_handler
!= NULL
)
228 if(old_flags
!= flags
)
234 F
->pflags
= ep_event
.events
= flags
;
235 ep_event
.data
.ptr
= F
;
236 if(op
== EPOLL_CTL_MOD
|| op
== EPOLL_CTL_ADD
)
237 ep_event
.events
|= EPOLLET
;
239 if(epoll_ctl(ep_info
->ep
, op
, F
->fd
, &ep_event
) != 0)
241 rb_lib_log("rb_select_epoll(): epoll_ctl failed: %s",
250 #ifdef EPOLL_SCHED_EVENT
252 rb_epoll_supports_event(void)
254 /* try to detect at runtime if everything we need actually works */
261 if(can_do_event
== 1)
263 if(can_do_event
== -1)
266 /* Check for openvz..it has a broken timerfd.. */
267 if(stat("/proc/user_beancounters", &st
) == 0)
273 #ifdef USE_TIMERFD_CREATE
274 if((fd
= timerfd_create(CLOCK_REALTIME
, 0)) >= 0)
283 ev
.sigev_signo
= SIGVTALRM
;
284 ev
.sigev_notify
= SIGEV_SIGNAL
;
285 if(timer_create(CLOCK_REALTIME
, &ev
, &timer
) != 0)
292 fd
= signalfd(-1, &set
, 0);
304 /* bleh..work around a glibc header bug on 32bit systems */
305 struct our_signalfd_siginfo
327 #define SIGFDIOV_COUNT 16
329 signalfd_handler(rb_fde_t
*F
, void *data
__attribute__((unused
)))
331 static struct our_signalfd_siginfo fdsig
[SIGFDIOV_COUNT
];
332 static struct iovec iov
[SIGFDIOV_COUNT
];
336 for(x
= 0; x
< SIGFDIOV_COUNT
; x
++)
338 iov
[x
].iov_base
= &fdsig
[x
];
339 iov
[x
].iov_len
= sizeof(struct our_signalfd_siginfo
);
344 ret
= readv(rb_get_fd(F
), iov
, SIGFDIOV_COUNT
);
346 if(ret
== 0 || (ret
< 0 && !rb_ignore_errno(errno
)))
349 rb_epoll_init_event();
355 rb_setselect(F
, RB_SELECT_READ
, signalfd_handler
, NULL
);
358 for(x
= 0; x
< ret
/ (int)sizeof(struct our_signalfd_siginfo
); x
++)
360 #if __WORDSIZE == 32 && defined(__sparc__)
361 uint32_t *q
= (uint32_t *)&fdsig
[x
].svptr
;
362 ev
= (struct ev_entry
*)q
[0];
364 ev
= (struct ev_entry
*)(uintptr_t)(fdsig
[x
].svptr
);
369 rb_run_one_event(ev
);
375 rb_epoll_init_event(void)
381 rb_epoll_supports_event();
385 sigaddset(&ss
, RTSIGNAL
);
386 sigprocmask(SIG_BLOCK
, &ss
, 0);
388 sigaddset(&ss
, RTSIGNAL
);
389 sfd
= signalfd(-1, &ss
, 0);
395 F
= rb_open(sfd
, RB_FD_UNKNOWN
, "signalfd");
397 signalfd_handler(F
, NULL
);
402 rb_epoll_sched_event_signalfd(struct ev_entry
*event
, int when
)
406 struct itimerspec ts
;
408 memset(&ev
, 0, sizeof(ev
));
409 event
->comm_ptr
= rb_malloc(sizeof(timer_t
));
410 id
= event
->comm_ptr
;
411 ev
.sigev_notify
= SIGEV_SIGNAL
;
412 ev
.sigev_signo
= RTSIGNAL
;
413 ev
.sigev_value
.sival_ptr
= event
;
415 if(timer_create(CLOCK_REALTIME
, &ev
, id
) < 0)
417 rb_lib_log("timer_create: %s\n", strerror(errno
));
420 memset(&ts
, 0, sizeof(ts
));
421 ts
.it_value
.tv_sec
= when
;
422 ts
.it_value
.tv_nsec
= 0;
423 if(event
->frequency
!= 0)
424 ts
.it_interval
= ts
.it_value
;
426 if(timer_settime(*id
, 0, &ts
, NULL
) < 0)
428 rb_lib_log("timer_settime: %s\n", strerror(errno
));
434 #ifdef USE_TIMERFD_CREATE
436 rb_read_timerfd(rb_fde_t
*F
, void *data
)
438 struct ev_entry
*event
= (struct ev_entry
*)data
;
448 retlen
= rb_read(F
, &count
, sizeof(count
));
450 if(retlen
== 0 || (retlen
< 0 && !rb_ignore_errno(errno
)))
453 rb_lib_log("rb_read_timerfd: timerfd[%s] closed on error: %s", event
->name
,
457 rb_setselect(F
, RB_SELECT_READ
, rb_read_timerfd
, event
);
458 rb_run_one_event(event
);
463 rb_epoll_sched_event_timerfd(struct ev_entry
*event
, int when
)
465 struct itimerspec ts
;
466 static char buf
[FD_DESC_SZ
+ 8];
470 if((fd
= timerfd_create(CLOCK_REALTIME
, 0)) < 0)
472 rb_lib_log("timerfd_create: %s\n", strerror(errno
));
476 memset(&ts
, 0, sizeof(ts
));
477 ts
.it_value
.tv_sec
= when
;
478 ts
.it_value
.tv_nsec
= 0;
479 if(event
->frequency
!= 0)
480 ts
.it_interval
= ts
.it_value
;
482 if(timerfd_settime(fd
, 0, &ts
, NULL
) < 0)
484 rb_lib_log("timerfd_settime: %s\n", strerror(errno
));
488 snprintf(buf
, sizeof(buf
), "timerfd: %s", event
->name
);
489 F
= rb_open(fd
, RB_FD_UNKNOWN
, buf
);
492 rb_setselect(F
, RB_SELECT_READ
, rb_read_timerfd
, event
);
500 rb_epoll_sched_event(struct ev_entry
*event
, int when
)
502 #ifdef USE_TIMERFD_CREATE
505 return rb_epoll_sched_event_timerfd(event
, when
);
508 return rb_epoll_sched_event_signalfd(event
, when
);
512 rb_epoll_unsched_event(struct ev_entry
*event
)
514 #ifdef USE_TIMERFD_CREATE
517 rb_close((rb_fde_t
*)event
->comm_ptr
);
518 event
->comm_ptr
= NULL
;
522 timer_delete(*((timer_t
*) event
->comm_ptr
));
523 rb_free(event
->comm_ptr
);
524 event
->comm_ptr
= NULL
;
526 #endif /* EPOLL_SCHED_EVENT */
528 #else /* epoll not supported here */
530 rb_init_netio_epoll(void)
536 rb_setselect_epoll(rb_fde_t
*F
__attribute__((unused
)), unsigned int type
__attribute__((unused
)), PF
* handler
__attribute__((unused
)), void *client_data
__attribute__((unused
)))
543 rb_select_epoll(long delay
__attribute__((unused
)))
550 rb_setup_fd_epoll(rb_fde_t
*F
__attribute__((unused
)))
559 #if !defined(USING_EPOLL) || !defined(EPOLL_SCHED_EVENT)
561 rb_epoll_init_event(void)
567 rb_epoll_sched_event(struct ev_entry
*event
__attribute__((unused
)), int when
__attribute__((unused
)))
574 rb_epoll_unsched_event(struct ev_entry
*event
__attribute__((unused
)))
580 rb_epoll_supports_event(void)
585 #endif /* !USING_EPOLL || !EPOLL_SCHED_EVENT */