1114881Sjulian/* 2114881Sjulian * Copyright 2000-2009 Niels Provos <provos@citi.umich.edu> 3114881Sjulian * Copyright 2009-2012 Niels Provos and Nick Mathewson 4114881Sjulian * 5114881Sjulian * Redistribution and use in source and binary forms, with or without 6114881Sjulian * modification, are permitted provided that the following conditions 7114881Sjulian * are met: 8114881Sjulian * 1. Redistributions of source code must retain the above copyright 9114881Sjulian * notice, this list of conditions and the following disclaimer. 10114881Sjulian * 2. Redistributions in binary form must reproduce the above copyright 11114881Sjulian * notice, this list of conditions and the following disclaimer in the 12114881Sjulian * documentation and/or other materials provided with the distribution. 13114881Sjulian * 3. The name of the author may not be used to endorse or promote products 14114881Sjulian * derived from this software without specific prior written permission. 15114881Sjulian * 16114881Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17114881Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18114881Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19114881Sjulian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20114881Sjulian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21114881Sjulian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22114881Sjulian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23114881Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24114881Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25114881Sjulian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26114881Sjulian */ 27114881Sjulian#include "event2/event-config.h" 28114881Sjulian#include "evconfig-private.h" 29114881Sjulian 30114881Sjulian#ifdef EVENT__HAVE_DEVPOLL 31114881Sjulian 32114881Sjulian#include <sys/types.h> 33114881Sjulian#include <sys/resource.h> 34114881Sjulian#ifdef EVENT__HAVE_SYS_TIME_H 35114881Sjulian#include <sys/time.h> 36114881Sjulian#endif 37114881Sjulian#include <sys/queue.h> 38188945Sthompsa#include <sys/devpoll.h> 39114881Sjulian#include <signal.h> 40114881Sjulian#include <stdio.h> 41114881Sjulian#include <stdlib.h> 42114881Sjulian#include <string.h> 43114881Sjulian#include <unistd.h> 44114881Sjulian#include <fcntl.h> 45114881Sjulian#include <errno.h> 46114881Sjulian 47114881Sjulian#include "event2/event.h" 48114881Sjulian#include "event2/event_struct.h" 49114881Sjulian#include "event2/thread.h" 50114881Sjulian#include "event-internal.h" 51114881Sjulian#include "evsignal-internal.h" 52114881Sjulian#include "log-internal.h" 53114881Sjulian#include "evmap-internal.h" 54131120Simp#include "evthread-internal.h" 55131120Simp 56131120Simpstruct devpollop { 57114881Sjulian struct pollfd *events; 58114881Sjulian int nevents; 59114881Sjulian int dpfd; 60114881Sjulian struct pollfd *changes; 61114881Sjulian int nchanges; 62114881Sjulian}; 63114881Sjulian 64114881Sjulianstatic void *devpoll_init(struct event_base *); 65114881Sjulianstatic int devpoll_add(struct event_base *, int fd, short old, short events, void *); 66114881Sjulianstatic int devpoll_del(struct event_base *, int fd, short old, short events, void *); 67114881Sjulianstatic int devpoll_dispatch(struct event_base *, struct timeval *); 68114881Sjulianstatic void devpoll_dealloc(struct event_base *); 69114881Sjulian 70114881Sjulianconst struct eventop devpollops = { 71114881Sjulian "devpoll", 72114881Sjulian devpoll_init, 73114881Sjulian devpoll_add, 74114881Sjulian devpoll_del, 75114881Sjulian devpoll_dispatch, 76114881Sjulian devpoll_dealloc, 77114881Sjulian 1, /* need reinit */ 78114881Sjulian EV_FEATURE_FDS|EV_FEATURE_O1, 79114881Sjulian 0 80114881Sjulian}; 81114881Sjulian 82114881Sjulian#define NEVENT 32000 83114881Sjulian 84114881Sjulianstatic int 85114881Sjuliandevpoll_commit(struct devpollop *devpollop) 86114881Sjulian{ 87114881Sjulian /* 88114881Sjulian * Due to a bug in Solaris, we have to use pwrite with an offset of 0. 89114881Sjulian * Write is limited to 2GB of data, until it will fail. 90114881Sjulian */ 91114881Sjulian if (pwrite(devpollop->dpfd, devpollop->changes, 92114881Sjulian sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) 93114881Sjulian return (-1); 94114881Sjulian 95114881Sjulian devpollop->nchanges = 0; 96114881Sjulian return (0); 97114881Sjulian} 98114881Sjulian 99114881Sjulianstatic int 100114881Sjuliandevpoll_queue(struct devpollop *devpollop, int fd, int events) { 101114881Sjulian struct pollfd *pfd; 102114881Sjulian 103114881Sjulian if (devpollop->nchanges >= devpollop->nevents) { 104114881Sjulian /* 105114881Sjulian * Change buffer is full, must commit it to /dev/poll before 106114881Sjulian * adding more 107114881Sjulian */ 108114881Sjulian if (devpoll_commit(devpollop) != 0) 109114881Sjulian return (-1); 110114881Sjulian } 111114881Sjulian 112114881Sjulian pfd = &devpollop->changes[devpollop->nchanges++]; 113114881Sjulian pfd->fd = fd; 114114881Sjulian pfd->events = events; 115114881Sjulian pfd->revents = 0; 116114881Sjulian 117114881Sjulian return (0); 118114881Sjulian} 119114881Sjulian 120114881Sjulianstatic void * 121114881Sjuliandevpoll_init(struct event_base *base) 122114881Sjulian{ 123114881Sjulian int dpfd, nfiles = NEVENT; 124114881Sjulian struct rlimit rl; 125114881Sjulian struct devpollop *devpollop; 126114881Sjulian 127114881Sjulian if (!(devpollop = mm_calloc(1, sizeof(struct devpollop)))) 128114881Sjulian return (NULL); 129114881Sjulian 130114881Sjulian if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && 131114881Sjulian rl.rlim_cur != RLIM_INFINITY) 132114881Sjulian nfiles = rl.rlim_cur; 133114881Sjulian 134114881Sjulian /* Initialize the kernel queue */ 135114881Sjulian if ((dpfd = evutil_open_closeonexec_("/dev/poll", O_RDWR, 0)) == -1) { 136114881Sjulian event_warn("open: /dev/poll"); 137114881Sjulian mm_free(devpollop); 138114881Sjulian return (NULL); 139131120Simp } 140114881Sjulian 141114881Sjulian devpollop->dpfd = dpfd; 142114881Sjulian 143114881Sjulian /* Initialize fields */ 144114881Sjulian /* FIXME: allocating 'nfiles' worth of space here can be 145114881Sjulian * expensive and unnecessary. See how epoll.c does it instead. */ 146114881Sjulian devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd)); 147114881Sjulian if (devpollop->events == NULL) { 148114881Sjulian mm_free(devpollop); 149114881Sjulian close(dpfd); 150114881Sjulian return (NULL); 151114881Sjulian } 152114881Sjulian devpollop->nevents = nfiles; 153114881Sjulian 154114881Sjulian devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd)); 155114881Sjulian if (devpollop->changes == NULL) { 156114881Sjulian mm_free(devpollop->events); 157114881Sjulian mm_free(devpollop); 158114881Sjulian close(dpfd); 159114881Sjulian return (NULL); 160114881Sjulian } 161114881Sjulian 162114881Sjulian evsig_init_(base); 163114881Sjulian 164114881Sjulian return (devpollop); 165114881Sjulian} 166114881Sjulian 167114881Sjulianstatic int 168114881Sjuliandevpoll_dispatch(struct event_base *base, struct timeval *tv) 169114881Sjulian{ 170114881Sjulian struct devpollop *devpollop = base->evbase; 171114881Sjulian struct pollfd *events = devpollop->events; 172114881Sjulian struct dvpoll dvp; 173114881Sjulian int i, res, timeout = -1; 174114881Sjulian 175114881Sjulian if (devpollop->nchanges) 176114881Sjulian devpoll_commit(devpollop); 177114881Sjulian 178114881Sjulian if (tv != NULL) 179114881Sjulian timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; 180114881Sjulian 181114881Sjulian dvp.dp_fds = devpollop->events; 182114881Sjulian dvp.dp_nfds = devpollop->nevents; 183114881Sjulian dvp.dp_timeout = timeout; 184114881Sjulian 185114881Sjulian EVBASE_RELEASE_LOCK(base, th_base_lock); 186114881Sjulian 187114881Sjulian res = ioctl(devpollop->dpfd, DP_POLL, &dvp); 188114881Sjulian 189114881Sjulian EVBASE_ACQUIRE_LOCK(base, th_base_lock); 190114881Sjulian 191114881Sjulian if (res == -1) { 192114881Sjulian if (errno != EINTR) { 193114881Sjulian event_warn("ioctl: DP_POLL"); 194114881Sjulian return (-1); 195114881Sjulian } 196114881Sjulian 197114881Sjulian return (0); 198114881Sjulian } 199114881Sjulian 200114881Sjulian event_debug(("%s: devpoll_wait reports %d", __func__, res)); 201114881Sjulian 202114881Sjulian for (i = 0; i < res; i++) { 203114881Sjulian int which = 0; 204114881Sjulian int what = events[i].revents; 205114881Sjulian 206114881Sjulian if (what & POLLHUP) 207114881Sjulian what |= POLLIN | POLLOUT; 208114881Sjulian else if (what & POLLERR) 209114881Sjulian what |= POLLIN | POLLOUT; 210114881Sjulian 211114881Sjulian if (what & POLLIN) 212114881Sjulian which |= EV_READ; 213114881Sjulian if (what & POLLOUT) 214114881Sjulian which |= EV_WRITE; 215114881Sjulian 216114881Sjulian if (!which) 217114881Sjulian continue; 218114881Sjulian 219114881Sjulian /* XXX(niels): not sure if this works for devpoll */ 220114881Sjulian evmap_io_active_(base, events[i].fd, which); 221114881Sjulian } 222114881Sjulian 223114881Sjulian return (0); 224114881Sjulian} 225114881Sjulian 226114881Sjulian 227114881Sjulianstatic int 228114881Sjuliandevpoll_add(struct event_base *base, int fd, short old, short events, void *p) 229114881Sjulian{ 230114881Sjulian struct devpollop *devpollop = base->evbase; 231114881Sjulian int res; 232114881Sjulian (void)p; 233114881Sjulian 234114881Sjulian /* 235114881Sjulian * It's not necessary to OR the existing read/write events that we 236114881Sjulian * are currently interested in with the new event we are adding. 237114881Sjulian * The /dev/poll driver ORs any new events with the existing events 238114881Sjulian * that it has cached for the fd. 239114881Sjulian */ 240114881Sjulian 241114881Sjulian res = 0; 242114881Sjulian if (events & EV_READ) 243114881Sjulian res |= POLLIN; 244114881Sjulian if (events & EV_WRITE) 245114881Sjulian res |= POLLOUT; 246114881Sjulian 247114881Sjulian if (devpoll_queue(devpollop, fd, res) != 0) 248114881Sjulian return (-1); 249114881Sjulian 250114881Sjulian return (0); 251114881Sjulian} 252114881Sjulian 253114881Sjulianstatic int 254114881Sjuliandevpoll_del(struct event_base *base, int fd, short old, short events, void *p) 255114881Sjulian{ 256114881Sjulian struct devpollop *devpollop = base->evbase; 257114881Sjulian int res; 258114881Sjulian (void)p; 259114881Sjulian 260114881Sjulian res = 0; 261114881Sjulian if (events & EV_READ) 262114881Sjulian res |= POLLIN; 263114881Sjulian if (events & EV_WRITE) 264114881Sjulian res |= POLLOUT; 265114881Sjulian 266114881Sjulian /* 267114881Sjulian * The only way to remove an fd from the /dev/poll monitored set is 268114881Sjulian * to use POLLREMOVE by itself. This removes ALL events for the fd 269114881Sjulian * provided so if we care about two events and are only removing one 270114881Sjulian * we must re-add the other event after POLLREMOVE. 271114881Sjulian */ 272114881Sjulian 273114881Sjulian if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) 274114881Sjulian return (-1); 275114881Sjulian 276114881Sjulian if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { 277114881Sjulian /* 278114881Sjulian * We're not deleting all events, so we must resubmit the 279114881Sjulian * event that we are still interested in if one exists. 280114881Sjulian */ 281114881Sjulian 282114881Sjulian if ((res & POLLIN) && (old & EV_WRITE)) { 283114881Sjulian /* Deleting read, still care about write */ 284114881Sjulian devpoll_queue(devpollop, fd, POLLOUT); 285114881Sjulian } else if ((res & POLLOUT) && (old & EV_READ)) { 286114881Sjulian /* Deleting write, still care about read */ 287114881Sjulian devpoll_queue(devpollop, fd, POLLIN); 288114881Sjulian } 289114881Sjulian } 290114881Sjulian 291114881Sjulian return (0); 292114881Sjulian} 293114881Sjulian 294114881Sjulianstatic void 295114881Sjuliandevpoll_dealloc(struct event_base *base) 296114881Sjulian{ 297114881Sjulian struct devpollop *devpollop = base->evbase; 298114881Sjulian 299114881Sjulian evsig_dealloc_(base); 300114881Sjulian if (devpollop->events) 301114881Sjulian mm_free(devpollop->events); 302114881Sjulian if (devpollop->changes) 303114881Sjulian mm_free(devpollop->changes); 304114881Sjulian if (devpollop->dpfd >= 0) 305114881Sjulian close(devpollop->dpfd); 306114881Sjulian 307114881Sjulian memset(devpollop, 0, sizeof(struct devpollop)); 308114881Sjulian mm_free(devpollop); 309} 310 311#endif /* EVENT__HAVE_DEVPOLL */ 312