1290001Sglebius/* 2290001Sglebius * Copyright 2000-2009 Niels Provos <provos@citi.umich.edu> 3290001Sglebius * Copyright 2009-2012 Niels Provos and Nick Mathewson 4290001Sglebius * 5290001Sglebius * Redistribution and use in source and binary forms, with or without 6290001Sglebius * modification, are permitted provided that the following conditions 7290001Sglebius * are met: 8290001Sglebius * 1. Redistributions of source code must retain the above copyright 9290001Sglebius * notice, this list of conditions and the following disclaimer. 10290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright 11290001Sglebius * notice, this list of conditions and the following disclaimer in the 12290001Sglebius * documentation and/or other materials provided with the distribution. 13290001Sglebius * 3. The name of the author may not be used to endorse or promote products 14290001Sglebius * derived from this software without specific prior written permission. 15290001Sglebius * 16290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17290001Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18290001Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19290001Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20290001Sglebius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21290001Sglebius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22290001Sglebius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23290001Sglebius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24290001Sglebius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25290001Sglebius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26290001Sglebius */ 27290001Sglebius#include "event2/event-config.h" 28290001Sglebius#include "evconfig-private.h" 29290001Sglebius 30290001Sglebius#ifdef EVENT__HAVE_DEVPOLL 31290001Sglebius 32290001Sglebius#include <sys/types.h> 33290001Sglebius#include <sys/resource.h> 34290001Sglebius#ifdef EVENT__HAVE_SYS_TIME_H 35290001Sglebius#include <sys/time.h> 36290001Sglebius#endif 37290001Sglebius#include <sys/queue.h> 38290001Sglebius#include <sys/devpoll.h> 39290001Sglebius#include <signal.h> 40290001Sglebius#include <stdio.h> 41290001Sglebius#include <stdlib.h> 42290001Sglebius#include <string.h> 43290001Sglebius#include <unistd.h> 44290001Sglebius#include <fcntl.h> 45290001Sglebius#include <errno.h> 46290001Sglebius 47290001Sglebius#include "event2/event.h" 48290001Sglebius#include "event2/event_struct.h" 49290001Sglebius#include "event2/thread.h" 50290001Sglebius#include "event-internal.h" 51290001Sglebius#include "evsignal-internal.h" 52290001Sglebius#include "log-internal.h" 53290001Sglebius#include "evmap-internal.h" 54290001Sglebius#include "evthread-internal.h" 55290001Sglebius 56290001Sglebiusstruct devpollop { 57290001Sglebius struct pollfd *events; 58290001Sglebius int nevents; 59290001Sglebius int dpfd; 60290001Sglebius struct pollfd *changes; 61290001Sglebius int nchanges; 62290001Sglebius}; 63290001Sglebius 64290001Sglebiusstatic void *devpoll_init(struct event_base *); 65290001Sglebiusstatic int devpoll_add(struct event_base *, int fd, short old, short events, void *); 66290001Sglebiusstatic int devpoll_del(struct event_base *, int fd, short old, short events, void *); 67290001Sglebiusstatic int devpoll_dispatch(struct event_base *, struct timeval *); 68290001Sglebiusstatic void devpoll_dealloc(struct event_base *); 69290001Sglebius 70290001Sglebiusconst struct eventop devpollops = { 71290001Sglebius "devpoll", 72290001Sglebius devpoll_init, 73290001Sglebius devpoll_add, 74290001Sglebius devpoll_del, 75290001Sglebius devpoll_dispatch, 76290001Sglebius devpoll_dealloc, 77290001Sglebius 1, /* need reinit */ 78290001Sglebius EV_FEATURE_FDS|EV_FEATURE_O1, 79290001Sglebius 0 80290001Sglebius}; 81290001Sglebius 82290001Sglebius#define NEVENT 32000 83290001Sglebius 84290001Sglebiusstatic int 85290001Sglebiusdevpoll_commit(struct devpollop *devpollop) 86290001Sglebius{ 87290001Sglebius /* 88290001Sglebius * Due to a bug in Solaris, we have to use pwrite with an offset of 0. 89290001Sglebius * Write is limited to 2GB of data, until it will fail. 90290001Sglebius */ 91290001Sglebius if (pwrite(devpollop->dpfd, devpollop->changes, 92290001Sglebius sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) 93290001Sglebius return (-1); 94290001Sglebius 95290001Sglebius devpollop->nchanges = 0; 96290001Sglebius return (0); 97290001Sglebius} 98290001Sglebius 99290001Sglebiusstatic int 100290001Sglebiusdevpoll_queue(struct devpollop *devpollop, int fd, int events) { 101290001Sglebius struct pollfd *pfd; 102290001Sglebius 103290001Sglebius if (devpollop->nchanges >= devpollop->nevents) { 104290001Sglebius /* 105290001Sglebius * Change buffer is full, must commit it to /dev/poll before 106290001Sglebius * adding more 107290001Sglebius */ 108290001Sglebius if (devpoll_commit(devpollop) != 0) 109290001Sglebius return (-1); 110290001Sglebius } 111290001Sglebius 112290001Sglebius pfd = &devpollop->changes[devpollop->nchanges++]; 113290001Sglebius pfd->fd = fd; 114290001Sglebius pfd->events = events; 115290001Sglebius pfd->revents = 0; 116290001Sglebius 117290001Sglebius return (0); 118290001Sglebius} 119290001Sglebius 120290001Sglebiusstatic void * 121290001Sglebiusdevpoll_init(struct event_base *base) 122290001Sglebius{ 123290001Sglebius int dpfd, nfiles = NEVENT; 124290001Sglebius struct rlimit rl; 125290001Sglebius struct devpollop *devpollop; 126290001Sglebius 127290001Sglebius if (!(devpollop = mm_calloc(1, sizeof(struct devpollop)))) 128290001Sglebius return (NULL); 129290001Sglebius 130290001Sglebius if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && 131290001Sglebius rl.rlim_cur != RLIM_INFINITY) 132290001Sglebius nfiles = rl.rlim_cur; 133290001Sglebius 134290001Sglebius /* Initialize the kernel queue */ 135290001Sglebius if ((dpfd = evutil_open_closeonexec_("/dev/poll", O_RDWR, 0)) == -1) { 136290001Sglebius event_warn("open: /dev/poll"); 137290001Sglebius mm_free(devpollop); 138290001Sglebius return (NULL); 139290001Sglebius } 140290001Sglebius 141290001Sglebius devpollop->dpfd = dpfd; 142290001Sglebius 143290001Sglebius /* Initialize fields */ 144290001Sglebius /* FIXME: allocating 'nfiles' worth of space here can be 145290001Sglebius * expensive and unnecessary. See how epoll.c does it instead. */ 146290001Sglebius devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd)); 147290001Sglebius if (devpollop->events == NULL) { 148290001Sglebius mm_free(devpollop); 149290001Sglebius close(dpfd); 150290001Sglebius return (NULL); 151290001Sglebius } 152290001Sglebius devpollop->nevents = nfiles; 153290001Sglebius 154290001Sglebius devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd)); 155290001Sglebius if (devpollop->changes == NULL) { 156290001Sglebius mm_free(devpollop->events); 157290001Sglebius mm_free(devpollop); 158290001Sglebius close(dpfd); 159290001Sglebius return (NULL); 160290001Sglebius } 161290001Sglebius 162290001Sglebius evsig_init_(base); 163290001Sglebius 164290001Sglebius return (devpollop); 165290001Sglebius} 166290001Sglebius 167290001Sglebiusstatic int 168290001Sglebiusdevpoll_dispatch(struct event_base *base, struct timeval *tv) 169290001Sglebius{ 170290001Sglebius struct devpollop *devpollop = base->evbase; 171290001Sglebius struct pollfd *events = devpollop->events; 172290001Sglebius struct dvpoll dvp; 173290001Sglebius int i, res, timeout = -1; 174290001Sglebius 175290001Sglebius if (devpollop->nchanges) 176290001Sglebius devpoll_commit(devpollop); 177290001Sglebius 178290001Sglebius if (tv != NULL) 179290001Sglebius timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; 180290001Sglebius 181290001Sglebius dvp.dp_fds = devpollop->events; 182290001Sglebius dvp.dp_nfds = devpollop->nevents; 183290001Sglebius dvp.dp_timeout = timeout; 184290001Sglebius 185290001Sglebius EVBASE_RELEASE_LOCK(base, th_base_lock); 186290001Sglebius 187290001Sglebius res = ioctl(devpollop->dpfd, DP_POLL, &dvp); 188290001Sglebius 189290001Sglebius EVBASE_ACQUIRE_LOCK(base, th_base_lock); 190290001Sglebius 191290001Sglebius if (res == -1) { 192290001Sglebius if (errno != EINTR) { 193290001Sglebius event_warn("ioctl: DP_POLL"); 194290001Sglebius return (-1); 195290001Sglebius } 196290001Sglebius 197290001Sglebius return (0); 198290001Sglebius } 199290001Sglebius 200290001Sglebius event_debug(("%s: devpoll_wait reports %d", __func__, res)); 201290001Sglebius 202290001Sglebius for (i = 0; i < res; i++) { 203290001Sglebius int which = 0; 204290001Sglebius int what = events[i].revents; 205290001Sglebius 206290001Sglebius if (what & POLLHUP) 207290001Sglebius what |= POLLIN | POLLOUT; 208290001Sglebius else if (what & POLLERR) 209290001Sglebius what |= POLLIN | POLLOUT; 210290001Sglebius 211290001Sglebius if (what & POLLIN) 212290001Sglebius which |= EV_READ; 213290001Sglebius if (what & POLLOUT) 214290001Sglebius which |= EV_WRITE; 215290001Sglebius 216290001Sglebius if (!which) 217290001Sglebius continue; 218290001Sglebius 219290001Sglebius /* XXX(niels): not sure if this works for devpoll */ 220290001Sglebius evmap_io_active_(base, events[i].fd, which); 221290001Sglebius } 222290001Sglebius 223290001Sglebius return (0); 224290001Sglebius} 225290001Sglebius 226290001Sglebius 227290001Sglebiusstatic int 228290001Sglebiusdevpoll_add(struct event_base *base, int fd, short old, short events, void *p) 229290001Sglebius{ 230290001Sglebius struct devpollop *devpollop = base->evbase; 231290001Sglebius int res; 232290001Sglebius (void)p; 233290001Sglebius 234290001Sglebius /* 235290001Sglebius * It's not necessary to OR the existing read/write events that we 236290001Sglebius * are currently interested in with the new event we are adding. 237290001Sglebius * The /dev/poll driver ORs any new events with the existing events 238290001Sglebius * that it has cached for the fd. 239290001Sglebius */ 240290001Sglebius 241290001Sglebius res = 0; 242290001Sglebius if (events & EV_READ) 243290001Sglebius res |= POLLIN; 244290001Sglebius if (events & EV_WRITE) 245290001Sglebius res |= POLLOUT; 246290001Sglebius 247290001Sglebius if (devpoll_queue(devpollop, fd, res) != 0) 248290001Sglebius return (-1); 249290001Sglebius 250290001Sglebius return (0); 251290001Sglebius} 252290001Sglebius 253290001Sglebiusstatic int 254290001Sglebiusdevpoll_del(struct event_base *base, int fd, short old, short events, void *p) 255290001Sglebius{ 256290001Sglebius struct devpollop *devpollop = base->evbase; 257290001Sglebius int res; 258290001Sglebius (void)p; 259290001Sglebius 260290001Sglebius res = 0; 261290001Sglebius if (events & EV_READ) 262290001Sglebius res |= POLLIN; 263290001Sglebius if (events & EV_WRITE) 264290001Sglebius res |= POLLOUT; 265290001Sglebius 266290001Sglebius /* 267290001Sglebius * The only way to remove an fd from the /dev/poll monitored set is 268290001Sglebius * to use POLLREMOVE by itself. This removes ALL events for the fd 269290001Sglebius * provided so if we care about two events and are only removing one 270290001Sglebius * we must re-add the other event after POLLREMOVE. 271290001Sglebius */ 272290001Sglebius 273290001Sglebius if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) 274290001Sglebius return (-1); 275290001Sglebius 276290001Sglebius if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { 277290001Sglebius /* 278290001Sglebius * We're not deleting all events, so we must resubmit the 279290001Sglebius * event that we are still interested in if one exists. 280290001Sglebius */ 281290001Sglebius 282290001Sglebius if ((res & POLLIN) && (old & EV_WRITE)) { 283290001Sglebius /* Deleting read, still care about write */ 284290001Sglebius devpoll_queue(devpollop, fd, POLLOUT); 285290001Sglebius } else if ((res & POLLOUT) && (old & EV_READ)) { 286290001Sglebius /* Deleting write, still care about read */ 287290001Sglebius devpoll_queue(devpollop, fd, POLLIN); 288290001Sglebius } 289290001Sglebius } 290290001Sglebius 291290001Sglebius return (0); 292290001Sglebius} 293290001Sglebius 294290001Sglebiusstatic void 295290001Sglebiusdevpoll_dealloc(struct event_base *base) 296290001Sglebius{ 297290001Sglebius struct devpollop *devpollop = base->evbase; 298290001Sglebius 299290001Sglebius evsig_dealloc_(base); 300290001Sglebius if (devpollop->events) 301290001Sglebius mm_free(devpollop->events); 302290001Sglebius if (devpollop->changes) 303290001Sglebius mm_free(devpollop->changes); 304290001Sglebius if (devpollop->dpfd >= 0) 305290001Sglebius close(devpollop->dpfd); 306290001Sglebius 307290001Sglebius memset(devpollop, 0, sizeof(struct devpollop)); 308290001Sglebius mm_free(devpollop); 309290001Sglebius} 310290001Sglebius 311290001Sglebius#endif /* EVENT__HAVE_DEVPOLL */ 312