1 /* 2 * Copyright 2000-2009 Niels Provos <[email protected]> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 #ifdef HAVE_CONFIG_H 27 #include "event-config.h" 28 #endif 29 30 #include <sys/types.h> 31 #include <sys/resource.h> 32 #ifdef _EVENT_HAVE_SYS_TIME_H 33 #include <sys/time.h> 34 #else 35 #include <sys/_time.h> 36 #endif 37 #include <sys/queue.h> 38 #include <sys/devpoll.h> 39 #include <signal.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <errno.h> 46 #include <assert.h> 47 48 #include "event2/event.h" 49 #include "event2/event_struct.h" 50 #include "event-internal.h" 51 #include "evsignal-internal.h" 52 #include "log-internal.h" 53 #include "evmap-internal.h" 54 55 struct devpollop { 56 struct pollfd *events; 57 int nevents; 58 int dpfd; 59 struct pollfd *changes; 60 int nchanges; 61 }; 62 63 static void *devpoll_init (struct event_base *); 64 static int devpoll_add(struct event_base *, int fd, short old, short events, void *); 65 static int devpoll_del(struct event_base *, int fd, short old, short events, void *); 66 static int devpoll_dispatch (struct event_base *, struct timeval *); 67 static void devpoll_dealloc (struct event_base *); 68 69 const struct eventop devpollops = { 70 "devpoll", 71 devpoll_init, 72 devpoll_add, 73 devpoll_del, 74 devpoll_dispatch, 75 devpoll_dealloc, 76 1, /* need reinit */ 77 EV_FEATURE_FDS|EV_FEATURE_O1, 78 0 79 }; 80 81 #define NEVENT 32000 82 83 static int 84 devpoll_commit(struct devpollop *devpollop) 85 { 86 /* 87 * Due to a bug in Solaris, we have to use pwrite with an offset of 0. 88 * Write is limited to 2GB of data, until it will fail. 89 */ 90 if (pwrite(devpollop->dpfd, devpollop->changes, 91 sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) 92 return(-1); 93 94 devpollop->nchanges = 0; 95 return(0); 96 } 97 98 static int 99 devpoll_queue(struct devpollop *devpollop, int fd, int events) { 100 struct pollfd *pfd; 101 102 if (devpollop->nchanges >= devpollop->nevents) { 103 /* 104 * Change buffer is full, must commit it to /dev/poll before 105 * adding more 106 */ 107 if (devpoll_commit(devpollop) != 0) 108 return(-1); 109 } 110 111 pfd = &devpollop->changes[devpollop->nchanges++]; 112 pfd->fd = fd; 113 pfd->events = events; 114 pfd->revents = 0; 115 116 return(0); 117 } 118 119 static void * 120 devpoll_init(struct event_base *base) 121 { 122 int dpfd, nfiles = NEVENT; 123 struct rlimit rl; 124 struct devpollop *devpollop; 125 126 if (!(devpollop = mm_calloc(1, sizeof(struct devpollop)))) 127 return (NULL); 128 129 if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && 130 rl.rlim_cur != RLIM_INFINITY) 131 nfiles = rl.rlim_cur; 132 133 /* Initialize the kernel queue */ 134 if ((dpfd = open("/dev/poll", O_RDWR)) == -1) { 135 event_warn("open: /dev/poll"); 136 mm_free(devpollop); 137 return (NULL); 138 } 139 140 devpollop->dpfd = dpfd; 141 142 /* Initialize fields */ 143 devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd)); 144 if (devpollop->events == NULL) { 145 mm_free(devpollop); 146 close(dpfd); 147 return (NULL); 148 } 149 devpollop->nevents = nfiles; 150 151 devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd)); 152 if (devpollop->changes == NULL) { 153 mm_free(devpollop->events); 154 mm_free(devpollop); 155 close(dpfd); 156 return (NULL); 157 } 158 159 evsig_init(base); 160 161 return (devpollop); 162 } 163 164 static int 165 devpoll_dispatch(struct event_base *base, struct timeval *tv) 166 { 167 struct devpollop *devpollop = base->evbase; 168 struct pollfd *events = devpollop->events; 169 struct dvpoll dvp; 170 int i, res, timeout = -1; 171 172 if (devpollop->nchanges) 173 devpoll_commit(devpollop); 174 175 if (tv != NULL) 176 timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; 177 178 dvp.dp_fds = devpollop->events; 179 dvp.dp_nfds = devpollop->nevents; 180 dvp.dp_timeout = timeout; 181 182 res = ioctl(devpollop->dpfd, DP_POLL, &dvp); 183 184 if (res == -1) { 185 if (errno != EINTR) { 186 event_warn("ioctl: DP_POLL"); 187 return (-1); 188 } 189 190 evsig_process(base); 191 return (0); 192 } else if (base->sig.evsig_caught) { 193 evsig_process(base); 194 } 195 196 event_debug(("%s: devpoll_wait reports %d", __func__, res)); 197 198 for (i = 0; i < res; i++) { 199 int which = 0; 200 int what = events[i].revents; 201 202 if (what & POLLHUP) 203 what |= POLLIN | POLLOUT; 204 else if (what & POLLERR) 205 what |= POLLIN | POLLOUT; 206 207 if (what & POLLIN) 208 which |= EV_READ; 209 if (what & POLLOUT) 210 which |= EV_WRITE; 211 212 if (!which) 213 continue; 214 215 /* XXX(niels): not sure if this works for devpoll */ 216 evmap_io_active(base, events[i].fd, which); 217 } 218 219 return (0); 220 } 221 222 223 static int 224 devpoll_add(struct event_base *base, int fd, short old, short events, void *p) 225 { 226 struct devpollop *devpollop = base->evbase; 227 int res; 228 (void)p; 229 230 /* 231 * It's not necessary to OR the existing read/write events that we 232 * are currently interested in with the new event we are adding. 233 * The /dev/poll driver ORs any new events with the existing events 234 * that it has cached for the fd. 235 */ 236 237 res = 0; 238 if (events & EV_READ) 239 res |= POLLIN; 240 if (events & EV_WRITE) 241 res |= POLLOUT; 242 243 if (devpoll_queue(devpollop, fd, res) != 0) 244 return(-1); 245 246 return (0); 247 } 248 249 static int 250 devpoll_del(struct event_base *base, int fd, short old, short events, void *p) 251 { 252 struct devpollop *devpollop = base->evbase; 253 int res; 254 (void)p; 255 256 res = 0; 257 if (events & EV_READ) 258 res |= POLLIN; 259 if (events & EV_WRITE) 260 res |= POLLOUT; 261 262 /* 263 * The only way to remove an fd from the /dev/poll monitored set is 264 * to use POLLREMOVE by itself. This removes ALL events for the fd 265 * provided so if we care about two events and are only removing one 266 * we must re-add the other event after POLLREMOVE. 267 */ 268 269 if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) 270 return(-1); 271 272 if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { 273 /* 274 * We're not deleting all events, so we must resubmit the 275 * event that we are still interested in if one exists. 276 */ 277 278 if ((res & POLLIN) && (old & EV_WRITE)) { 279 /* Deleting read, still care about write */ 280 devpoll_queue(devpollop, fd, POLLOUT); 281 } else if ((res & POLLOUT) && (old & EV_READ)) { 282 /* Deleting write, still care about read */ 283 devpoll_queue(devpollop, fd, POLLIN); 284 } 285 } 286 287 return (0); 288 } 289 290 static void 291 devpoll_dealloc(struct event_base *base) 292 { 293 struct devpollop *devpollop = base->evbase; 294 295 evsig_dealloc(base); 296 if (devpollop->events) 297 mm_free(devpollop->events); 298 if (devpollop->changes) 299 mm_free(devpollop->changes); 300 if (devpollop->dpfd >= 0) 301 close(devpollop->dpfd); 302 303 memset(devpollop, 0, sizeof(struct devpollop)); 304 mm_free(devpollop); 305 } 306