1: /*
2: * Copyright 2000-2004 Niels Provos <provos@citi.umich.edu>
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. The name of the author may not be used to endorse or promote products
14: * derived from this software without specific prior written permission.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26: */
27: #ifdef HAVE_CONFIG_H
28: #include "config.h"
29: #endif
30:
31: #include <sys/types.h>
32: #include <sys/resource.h>
33: #ifdef HAVE_SYS_TIME_H
34: #include <sys/time.h>
35: #else
36: #include <sys/_libevent_time.h>
37: #endif
38: #include <sys/queue.h>
39: #include <sys/devpoll.h>
40: #include <signal.h>
41: #include <stdio.h>
42: #include <stdlib.h>
43: #include <string.h>
44: #include <unistd.h>
45: #include <fcntl.h>
46: #include <errno.h>
47: #include <assert.h>
48:
49: #include "event.h"
50: #include "event-internal.h"
51: #include "evsignal.h"
52: #include "log.h"
53:
54: /* due to limitations in the devpoll interface, we need to keep track of
55: * all file descriptors outself.
56: */
57: struct evdevpoll {
58: struct event *evread;
59: struct event *evwrite;
60: };
61:
62: struct devpollop {
63: struct evdevpoll *fds;
64: int nfds;
65: struct pollfd *events;
66: int nevents;
67: int dpfd;
68: struct pollfd *changes;
69: int nchanges;
70: };
71:
72: static void *devpoll_init (struct event_base *);
73: static int devpoll_add (void *, struct event *);
74: static int devpoll_del (void *, struct event *);
75: static int devpoll_dispatch (struct event_base *, void *, struct timeval *);
76: static void devpoll_dealloc (struct event_base *, void *);
77:
78: const struct eventop devpollops = {
79: "devpoll",
80: devpoll_init,
81: devpoll_add,
82: devpoll_del,
83: devpoll_dispatch,
84: devpoll_dealloc,
85: 1 /* need reinit */
86: };
87:
88: #define NEVENT 32000
89:
90: static int
91: devpoll_commit(struct devpollop *devpollop)
92: {
93: /*
94: * Due to a bug in Solaris, we have to use pwrite with an offset of 0.
95: * Write is limited to 2GB of data, until it will fail.
96: */
97: if (pwrite(devpollop->dpfd, devpollop->changes,
98: sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
99: return(-1);
100:
101: devpollop->nchanges = 0;
102: return(0);
103: }
104:
105: static int
106: devpoll_queue(struct devpollop *devpollop, int fd, int events) {
107: struct pollfd *pfd;
108:
109: if (devpollop->nchanges >= devpollop->nevents) {
110: /*
111: * Change buffer is full, must commit it to /dev/poll before
112: * adding more
113: */
114: if (devpoll_commit(devpollop) != 0)
115: return(-1);
116: }
117:
118: pfd = &devpollop->changes[devpollop->nchanges++];
119: pfd->fd = fd;
120: pfd->events = events;
121: pfd->revents = 0;
122:
123: return(0);
124: }
125:
126: static void *
127: devpoll_init(struct event_base *base)
128: {
129: int dpfd, nfiles = NEVENT;
130: struct rlimit rl;
131: struct devpollop *devpollop;
132:
133: /* Disable devpoll when this environment variable is set */
134: if (evutil_getenv("EVENT_NODEVPOLL"))
135: return (NULL);
136:
137: if (!(devpollop = calloc(1, sizeof(struct devpollop))))
138: return (NULL);
139:
140: if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
141: rl.rlim_cur != RLIM_INFINITY)
142: nfiles = rl.rlim_cur;
143:
144: /* Initialize the kernel queue */
145: if ((dpfd = open("/dev/poll", O_RDWR)) == -1) {
146: event_warn("open: /dev/poll");
147: free(devpollop);
148: return (NULL);
149: }
150:
151: devpollop->dpfd = dpfd;
152:
153: /* Initialize fields */
154: devpollop->events = calloc(nfiles, sizeof(struct pollfd));
155: if (devpollop->events == NULL) {
156: free(devpollop);
157: close(dpfd);
158: return (NULL);
159: }
160: devpollop->nevents = nfiles;
161:
162: devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll));
163: if (devpollop->fds == NULL) {
164: free(devpollop->events);
165: free(devpollop);
166: close(dpfd);
167: return (NULL);
168: }
169: devpollop->nfds = nfiles;
170:
171: devpollop->changes = calloc(nfiles, sizeof(struct pollfd));
172: if (devpollop->changes == NULL) {
173: free(devpollop->fds);
174: free(devpollop->events);
175: free(devpollop);
176: close(dpfd);
177: return (NULL);
178: }
179:
180: evsignal_init(base);
181:
182: return (devpollop);
183: }
184:
185: static int
186: devpoll_recalc(struct event_base *base, void *arg, int max)
187: {
188: struct devpollop *devpollop = arg;
189:
190: if (max >= devpollop->nfds) {
191: struct evdevpoll *fds;
192: int nfds;
193:
194: nfds = devpollop->nfds;
195: while (nfds <= max)
196: nfds <<= 1;
197:
198: fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll));
199: if (fds == NULL) {
200: event_warn("realloc");
201: return (-1);
202: }
203: devpollop->fds = fds;
204: memset(fds + devpollop->nfds, 0,
205: (nfds - devpollop->nfds) * sizeof(struct evdevpoll));
206: devpollop->nfds = nfds;
207: }
208:
209: return (0);
210: }
211:
212: static int
213: devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
214: {
215: struct devpollop *devpollop = arg;
216: struct pollfd *events = devpollop->events;
217: struct dvpoll dvp;
218: struct evdevpoll *evdp;
219: int i, res, timeout = -1;
220:
221: if (devpollop->nchanges)
222: devpoll_commit(devpollop);
223:
224: if (tv != NULL)
225: timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
226:
227: dvp.dp_fds = devpollop->events;
228: dvp.dp_nfds = devpollop->nevents;
229: dvp.dp_timeout = timeout;
230:
231: res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
232:
233: if (res == -1) {
234: if (errno != EINTR) {
235: event_warn("ioctl: DP_POLL");
236: return (-1);
237: }
238:
239: evsignal_process(base);
240: return (0);
241: } else if (base->sig.evsignal_caught) {
242: evsignal_process(base);
243: }
244:
245: event_debug(("%s: devpoll_wait reports %d", __func__, res));
246:
247: for (i = 0; i < res; i++) {
248: int which = 0;
249: int what = events[i].revents;
250: struct event *evread = NULL, *evwrite = NULL;
251:
252: assert(events[i].fd < devpollop->nfds);
253: evdp = &devpollop->fds[events[i].fd];
254:
255: if (what & POLLHUP)
256: what |= POLLIN | POLLOUT;
257: else if (what & POLLERR)
258: what |= POLLIN | POLLOUT;
259:
260: if (what & POLLIN) {
261: evread = evdp->evread;
262: which |= EV_READ;
263: }
264:
265: if (what & POLLOUT) {
266: evwrite = evdp->evwrite;
267: which |= EV_WRITE;
268: }
269:
270: if (!which)
271: continue;
272:
273: if (evread != NULL && !(evread->ev_events & EV_PERSIST))
274: event_del(evread);
275: if (evwrite != NULL && evwrite != evread &&
276: !(evwrite->ev_events & EV_PERSIST))
277: event_del(evwrite);
278:
279: if (evread != NULL)
280: event_active(evread, EV_READ, 1);
281: if (evwrite != NULL)
282: event_active(evwrite, EV_WRITE, 1);
283: }
284:
285: return (0);
286: }
287:
288:
289: static int
290: devpoll_add(void *arg, struct event *ev)
291: {
292: struct devpollop *devpollop = arg;
293: struct evdevpoll *evdp;
294: int fd, events;
295:
296: if (ev->ev_events & EV_SIGNAL)
297: return (evsignal_add(ev));
298:
299: fd = ev->ev_fd;
300: if (fd >= devpollop->nfds) {
301: /* Extend the file descriptor array as necessary */
302: if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1)
303: return (-1);
304: }
305: evdp = &devpollop->fds[fd];
306:
307: /*
308: * It's not necessary to OR the existing read/write events that we
309: * are currently interested in with the new event we are adding.
310: * The /dev/poll driver ORs any new events with the existing events
311: * that it has cached for the fd.
312: */
313:
314: events = 0;
315: if (ev->ev_events & EV_READ) {
316: if (evdp->evread && evdp->evread != ev) {
317: /* There is already a different read event registered */
318: return(-1);
319: }
320: events |= POLLIN;
321: }
322:
323: if (ev->ev_events & EV_WRITE) {
324: if (evdp->evwrite && evdp->evwrite != ev) {
325: /* There is already a different write event registered */
326: return(-1);
327: }
328: events |= POLLOUT;
329: }
330:
331: if (devpoll_queue(devpollop, fd, events) != 0)
332: return(-1);
333:
334: /* Update events responsible */
335: if (ev->ev_events & EV_READ)
336: evdp->evread = ev;
337: if (ev->ev_events & EV_WRITE)
338: evdp->evwrite = ev;
339:
340: return (0);
341: }
342:
343: static int
344: devpoll_del(void *arg, struct event *ev)
345: {
346: struct devpollop *devpollop = arg;
347: struct evdevpoll *evdp;
348: int fd, events;
349: int needwritedelete = 1, needreaddelete = 1;
350:
351: if (ev->ev_events & EV_SIGNAL)
352: return (evsignal_del(ev));
353:
354: fd = ev->ev_fd;
355: if (fd >= devpollop->nfds)
356: return (0);
357: evdp = &devpollop->fds[fd];
358:
359: events = 0;
360: if (ev->ev_events & EV_READ)
361: events |= POLLIN;
362: if (ev->ev_events & EV_WRITE)
363: events |= POLLOUT;
364:
365: /*
366: * The only way to remove an fd from the /dev/poll monitored set is
367: * to use POLLREMOVE by itself. This removes ALL events for the fd
368: * provided so if we care about two events and are only removing one
369: * we must re-add the other event after POLLREMOVE.
370: */
371:
372: if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
373: return(-1);
374:
375: if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
376: /*
377: * We're not deleting all events, so we must resubmit the
378: * event that we are still interested in if one exists.
379: */
380:
381: if ((events & POLLIN) && evdp->evwrite != NULL) {
382: /* Deleting read, still care about write */
383: devpoll_queue(devpollop, fd, POLLOUT);
384: needwritedelete = 0;
385: } else if ((events & POLLOUT) && evdp->evread != NULL) {
386: /* Deleting write, still care about read */
387: devpoll_queue(devpollop, fd, POLLIN);
388: needreaddelete = 0;
389: }
390: }
391:
392: if (needreaddelete)
393: evdp->evread = NULL;
394: if (needwritedelete)
395: evdp->evwrite = NULL;
396:
397: return (0);
398: }
399:
400: static void
401: devpoll_dealloc(struct event_base *base, void *arg)
402: {
403: struct devpollop *devpollop = arg;
404:
405: evsignal_dealloc(base);
406: if (devpollop->fds)
407: free(devpollop->fds);
408: if (devpollop->events)
409: free(devpollop->events);
410: if (devpollop->changes)
411: free(devpollop->changes);
412: if (devpollop->dpfd >= 0)
413: close(devpollop->dpfd);
414:
415: memset(devpollop, 0, sizeof(struct devpollop));
416: free(devpollop);
417: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>