1*9034ec65Schristos /*	$NetBSD: devpoll.c,v 1.5 2020/05/25 20:47:33 christos Exp $	*/
22b3787f6Schristos 
32b3787f6Schristos /*
42b3787f6Schristos  * Copyright 2000-2009 Niels Provos <provos@citi.umich.edu>
52b3787f6Schristos  * Copyright 2009-2012 Niels Provos and Nick Mathewson
62b3787f6Schristos  *
72b3787f6Schristos  * Redistribution and use in source and binary forms, with or without
82b3787f6Schristos  * modification, are permitted provided that the following conditions
92b3787f6Schristos  * are met:
102b3787f6Schristos  * 1. Redistributions of source code must retain the above copyright
112b3787f6Schristos  *    notice, this list of conditions and the following disclaimer.
122b3787f6Schristos  * 2. Redistributions in binary form must reproduce the above copyright
132b3787f6Schristos  *    notice, this list of conditions and the following disclaimer in the
142b3787f6Schristos  *    documentation and/or other materials provided with the distribution.
152b3787f6Schristos  * 3. The name of the author may not be used to endorse or promote products
162b3787f6Schristos  *    derived from this software without specific prior written permission.
172b3787f6Schristos  *
182b3787f6Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
192b3787f6Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
202b3787f6Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
212b3787f6Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
222b3787f6Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
232b3787f6Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
242b3787f6Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
252b3787f6Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
262b3787f6Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
272b3787f6Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282b3787f6Schristos  */
292b3787f6Schristos #include "event2/event-config.h"
302b3787f6Schristos #include "evconfig-private.h"
312b3787f6Schristos 
322b3787f6Schristos #ifdef EVENT__HAVE_DEVPOLL
332b3787f6Schristos 
342b3787f6Schristos #include <sys/types.h>
352b3787f6Schristos #include <sys/resource.h>
362b3787f6Schristos #ifdef EVENT__HAVE_SYS_TIME_H
372b3787f6Schristos #include <sys/time.h>
382b3787f6Schristos #endif
392b3787f6Schristos #include <sys/queue.h>
402b3787f6Schristos #include <sys/devpoll.h>
412b3787f6Schristos #include <signal.h>
422b3787f6Schristos #include <stdio.h>
432b3787f6Schristos #include <stdlib.h>
442b3787f6Schristos #include <string.h>
452b3787f6Schristos #include <unistd.h>
462b3787f6Schristos #include <fcntl.h>
472b3787f6Schristos #include <errno.h>
482b3787f6Schristos 
492b3787f6Schristos #include "event2/event.h"
502b3787f6Schristos #include "event2/event_struct.h"
512b3787f6Schristos #include "event2/thread.h"
522b3787f6Schristos #include "event-internal.h"
532b3787f6Schristos #include "evsignal-internal.h"
542b3787f6Schristos #include "log-internal.h"
552b3787f6Schristos #include "evmap-internal.h"
562b3787f6Schristos #include "evthread-internal.h"
572b3787f6Schristos 
582b3787f6Schristos struct devpollop {
592b3787f6Schristos 	struct pollfd *events;
602b3787f6Schristos 	int nevents;
612b3787f6Schristos 	int dpfd;
622b3787f6Schristos 	struct pollfd *changes;
632b3787f6Schristos 	int nchanges;
642b3787f6Schristos };
652b3787f6Schristos 
662b3787f6Schristos static void *devpoll_init(struct event_base *);
672b3787f6Schristos static int devpoll_add(struct event_base *, int fd, short old, short events, void *);
682b3787f6Schristos static int devpoll_del(struct event_base *, int fd, short old, short events, void *);
692b3787f6Schristos static int devpoll_dispatch(struct event_base *, struct timeval *);
702b3787f6Schristos static void devpoll_dealloc(struct event_base *);
712b3787f6Schristos 
722b3787f6Schristos const struct eventop devpollops = {
732b3787f6Schristos 	"devpoll",
742b3787f6Schristos 	devpoll_init,
752b3787f6Schristos 	devpoll_add,
762b3787f6Schristos 	devpoll_del,
772b3787f6Schristos 	devpoll_dispatch,
782b3787f6Schristos 	devpoll_dealloc,
792b3787f6Schristos 	1, /* need reinit */
802b3787f6Schristos 	EV_FEATURE_FDS|EV_FEATURE_O1,
812b3787f6Schristos 	0
822b3787f6Schristos };
832b3787f6Schristos 
842b3787f6Schristos #define NEVENT	32000
852b3787f6Schristos 
862b3787f6Schristos static int
devpoll_commit(struct devpollop * devpollop)872b3787f6Schristos devpoll_commit(struct devpollop *devpollop)
882b3787f6Schristos {
892b3787f6Schristos 	/*
902b3787f6Schristos 	 * Due to a bug in Solaris, we have to use pwrite with an offset of 0.
912b3787f6Schristos 	 * Write is limited to 2GB of data, until it will fail.
922b3787f6Schristos 	 */
932b3787f6Schristos 	if (pwrite(devpollop->dpfd, devpollop->changes,
942b3787f6Schristos 		sizeof(struct pollfd) * devpollop->nchanges, 0) == -1)
952b3787f6Schristos 		return (-1);
962b3787f6Schristos 
972b3787f6Schristos 	devpollop->nchanges = 0;
982b3787f6Schristos 	return (0);
992b3787f6Schristos }
1002b3787f6Schristos 
1012b3787f6Schristos static int
devpoll_queue(struct devpollop * devpollop,int fd,int events)1022b3787f6Schristos devpoll_queue(struct devpollop *devpollop, int fd, int events) {
1032b3787f6Schristos 	struct pollfd *pfd;
1042b3787f6Schristos 
1052b3787f6Schristos 	if (devpollop->nchanges >= devpollop->nevents) {
1062b3787f6Schristos 		/*
1072b3787f6Schristos 		 * Change buffer is full, must commit it to /dev/poll before
1082b3787f6Schristos 		 * adding more
1092b3787f6Schristos 		 */
1102b3787f6Schristos 		if (devpoll_commit(devpollop) != 0)
1112b3787f6Schristos 			return (-1);
1122b3787f6Schristos 	}
1132b3787f6Schristos 
1142b3787f6Schristos 	pfd = &devpollop->changes[devpollop->nchanges++];
1152b3787f6Schristos 	pfd->fd = fd;
1162b3787f6Schristos 	pfd->events = events;
1172b3787f6Schristos 	pfd->revents = 0;
1182b3787f6Schristos 
1192b3787f6Schristos 	return (0);
1202b3787f6Schristos }
1212b3787f6Schristos 
1222b3787f6Schristos static void *
devpoll_init(struct event_base * base)1232b3787f6Schristos devpoll_init(struct event_base *base)
1242b3787f6Schristos {
1252b3787f6Schristos 	int dpfd, nfiles = NEVENT;
1262b3787f6Schristos 	struct rlimit rl;
1272b3787f6Schristos 	struct devpollop *devpollop;
1282b3787f6Schristos 
1292b3787f6Schristos 	if (!(devpollop = mm_calloc(1, sizeof(struct devpollop))))
1302b3787f6Schristos 		return (NULL);
1312b3787f6Schristos 
1322b3787f6Schristos 	if (getrlimit(RLIMIT_NOFILE, &rl) == 0 &&
1332b3787f6Schristos 	    rl.rlim_cur != RLIM_INFINITY)
1342b3787f6Schristos 		nfiles = rl.rlim_cur;
1352b3787f6Schristos 
1362b3787f6Schristos 	/* Initialize the kernel queue */
1372b3787f6Schristos 	if ((dpfd = evutil_open_closeonexec_("/dev/poll", O_RDWR, 0)) == -1) {
1382b3787f6Schristos 		event_warn("open: /dev/poll");
1392b3787f6Schristos 		mm_free(devpollop);
1402b3787f6Schristos 		return (NULL);
1412b3787f6Schristos 	}
1422b3787f6Schristos 
1432b3787f6Schristos 	devpollop->dpfd = dpfd;
1442b3787f6Schristos 
1452b3787f6Schristos 	/* Initialize fields */
1462b3787f6Schristos 	/* FIXME: allocating 'nfiles' worth of space here can be
1472b3787f6Schristos 	 * expensive and unnecessary.  See how epoll.c does it instead. */
1482b3787f6Schristos 	devpollop->events = mm_calloc(nfiles, sizeof(struct pollfd));
1492b3787f6Schristos 	if (devpollop->events == NULL) {
1502b3787f6Schristos 		mm_free(devpollop);
1512b3787f6Schristos 		close(dpfd);
1522b3787f6Schristos 		return (NULL);
1532b3787f6Schristos 	}
1542b3787f6Schristos 	devpollop->nevents = nfiles;
1552b3787f6Schristos 
1562b3787f6Schristos 	devpollop->changes = mm_calloc(nfiles, sizeof(struct pollfd));
1572b3787f6Schristos 	if (devpollop->changes == NULL) {
1582b3787f6Schristos 		mm_free(devpollop->events);
1592b3787f6Schristos 		mm_free(devpollop);
1602b3787f6Schristos 		close(dpfd);
1612b3787f6Schristos 		return (NULL);
1622b3787f6Schristos 	}
1632b3787f6Schristos 
1642b3787f6Schristos 	evsig_init_(base);
1652b3787f6Schristos 
1662b3787f6Schristos 	return (devpollop);
1672b3787f6Schristos }
1682b3787f6Schristos 
1692b3787f6Schristos static int
devpoll_dispatch(struct event_base * base,struct timeval * tv)1702b3787f6Schristos devpoll_dispatch(struct event_base *base, struct timeval *tv)
1712b3787f6Schristos {
1722b3787f6Schristos 	struct devpollop *devpollop = base->evbase;
1732b3787f6Schristos 	struct pollfd *events = devpollop->events;
1742b3787f6Schristos 	struct dvpoll dvp;
1752b3787f6Schristos 	int i, res, timeout = -1;
1762b3787f6Schristos 
1772b3787f6Schristos 	if (devpollop->nchanges)
1782b3787f6Schristos 		devpoll_commit(devpollop);
1792b3787f6Schristos 
1802b3787f6Schristos 	if (tv != NULL)
1812b3787f6Schristos 		timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
1822b3787f6Schristos 
1832b3787f6Schristos 	dvp.dp_fds = devpollop->events;
1842b3787f6Schristos 	dvp.dp_nfds = devpollop->nevents;
1852b3787f6Schristos 	dvp.dp_timeout = timeout;
1862b3787f6Schristos 
1872b3787f6Schristos 	EVBASE_RELEASE_LOCK(base, th_base_lock);
1882b3787f6Schristos 
1892b3787f6Schristos 	res = ioctl(devpollop->dpfd, DP_POLL, &dvp);
1902b3787f6Schristos 
1912b3787f6Schristos 	EVBASE_ACQUIRE_LOCK(base, th_base_lock);
1922b3787f6Schristos 
1932b3787f6Schristos 	if (res == -1) {
1942b3787f6Schristos 		if (errno != EINTR) {
1952b3787f6Schristos 			event_warn("ioctl: DP_POLL");
1962b3787f6Schristos 			return (-1);
1972b3787f6Schristos 		}
1982b3787f6Schristos 
1992b3787f6Schristos 		return (0);
2002b3787f6Schristos 	}
2012b3787f6Schristos 
2022b3787f6Schristos 	event_debug(("%s: devpoll_wait reports %d", __func__, res));
2032b3787f6Schristos 
2042b3787f6Schristos 	for (i = 0; i < res; i++) {
2052b3787f6Schristos 		int which = 0;
2062b3787f6Schristos 		int what = events[i].revents;
2072b3787f6Schristos 
2082b3787f6Schristos 		if (what & POLLHUP)
2092b3787f6Schristos 			what |= POLLIN | POLLOUT;
2102b3787f6Schristos 		else if (what & POLLERR)
2112b3787f6Schristos 			what |= POLLIN | POLLOUT;
2122b3787f6Schristos 
2132b3787f6Schristos 		if (what & POLLIN)
2142b3787f6Schristos 			which |= EV_READ;
2152b3787f6Schristos 		if (what & POLLOUT)
2162b3787f6Schristos 			which |= EV_WRITE;
2172b3787f6Schristos 
2182b3787f6Schristos 		if (!which)
2192b3787f6Schristos 			continue;
2202b3787f6Schristos 
2212b3787f6Schristos 		/* XXX(niels): not sure if this works for devpoll */
2222b3787f6Schristos 		evmap_io_active_(base, events[i].fd, which);
2232b3787f6Schristos 	}
2242b3787f6Schristos 
2252b3787f6Schristos 	return (0);
2262b3787f6Schristos }
2272b3787f6Schristos 
2282b3787f6Schristos 
2292b3787f6Schristos static int
devpoll_add(struct event_base * base,int fd,short old,short events,void * p)2302b3787f6Schristos devpoll_add(struct event_base *base, int fd, short old, short events, void *p)
2312b3787f6Schristos {
2322b3787f6Schristos 	struct devpollop *devpollop = base->evbase;
2332b3787f6Schristos 	int res;
2342b3787f6Schristos 	(void)p;
2352b3787f6Schristos 
2362b3787f6Schristos 	/*
2372b3787f6Schristos 	 * It's not necessary to OR the existing read/write events that we
2382b3787f6Schristos 	 * are currently interested in with the new event we are adding.
2392b3787f6Schristos 	 * The /dev/poll driver ORs any new events with the existing events
2402b3787f6Schristos 	 * that it has cached for the fd.
2412b3787f6Schristos 	 */
2422b3787f6Schristos 
2432b3787f6Schristos 	res = 0;
2442b3787f6Schristos 	if (events & EV_READ)
2452b3787f6Schristos 		res |= POLLIN;
2462b3787f6Schristos 	if (events & EV_WRITE)
2472b3787f6Schristos 		res |= POLLOUT;
2482b3787f6Schristos 
2492b3787f6Schristos 	if (devpoll_queue(devpollop, fd, res) != 0)
2502b3787f6Schristos 		return (-1);
2512b3787f6Schristos 
2522b3787f6Schristos 	return (0);
2532b3787f6Schristos }
2542b3787f6Schristos 
2552b3787f6Schristos static int
devpoll_del(struct event_base * base,int fd,short old,short events,void * p)2562b3787f6Schristos devpoll_del(struct event_base *base, int fd, short old, short events, void *p)
2572b3787f6Schristos {
2582b3787f6Schristos 	struct devpollop *devpollop = base->evbase;
2592b3787f6Schristos 	int res;
2602b3787f6Schristos 	(void)p;
2612b3787f6Schristos 
2622b3787f6Schristos 	res = 0;
2632b3787f6Schristos 	if (events & EV_READ)
2642b3787f6Schristos 		res |= POLLIN;
2652b3787f6Schristos 	if (events & EV_WRITE)
2662b3787f6Schristos 		res |= POLLOUT;
2672b3787f6Schristos 
2682b3787f6Schristos 	/*
2692b3787f6Schristos 	 * The only way to remove an fd from the /dev/poll monitored set is
2702b3787f6Schristos 	 * to use POLLREMOVE by itself.  This removes ALL events for the fd
2712b3787f6Schristos 	 * provided so if we care about two events and are only removing one
2722b3787f6Schristos 	 * we must re-add the other event after POLLREMOVE.
2732b3787f6Schristos 	 */
2742b3787f6Schristos 
2752b3787f6Schristos 	if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0)
2762b3787f6Schristos 		return (-1);
2772b3787f6Schristos 
2782b3787f6Schristos 	if ((res & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) {
2792b3787f6Schristos 		/*
2802b3787f6Schristos 		 * We're not deleting all events, so we must resubmit the
2812b3787f6Schristos 		 * event that we are still interested in if one exists.
2822b3787f6Schristos 		 */
2832b3787f6Schristos 
2842b3787f6Schristos 		if ((res & POLLIN) && (old & EV_WRITE)) {
2852b3787f6Schristos 			/* Deleting read, still care about write */
2862b3787f6Schristos 			devpoll_queue(devpollop, fd, POLLOUT);
2872b3787f6Schristos 		} else if ((res & POLLOUT) && (old & EV_READ)) {
2882b3787f6Schristos 			/* Deleting write, still care about read */
2892b3787f6Schristos 			devpoll_queue(devpollop, fd, POLLIN);
2902b3787f6Schristos 		}
2912b3787f6Schristos 	}
2922b3787f6Schristos 
2932b3787f6Schristos 	return (0);
2942b3787f6Schristos }
2952b3787f6Schristos 
2962b3787f6Schristos static void
devpoll_dealloc(struct event_base * base)2972b3787f6Schristos devpoll_dealloc(struct event_base *base)
2982b3787f6Schristos {
2992b3787f6Schristos 	struct devpollop *devpollop = base->evbase;
3002b3787f6Schristos 
3012b3787f6Schristos 	evsig_dealloc_(base);
3022b3787f6Schristos 	if (devpollop->events)
3032b3787f6Schristos 		mm_free(devpollop->events);
3042b3787f6Schristos 	if (devpollop->changes)
3052b3787f6Schristos 		mm_free(devpollop->changes);
3062b3787f6Schristos 	if (devpollop->dpfd >= 0)
3072b3787f6Schristos 		close(devpollop->dpfd);
3082b3787f6Schristos 
3092b3787f6Schristos 	memset(devpollop, 0, sizeof(struct devpollop));
3102b3787f6Schristos 	mm_free(devpollop);
3112b3787f6Schristos }
3122b3787f6Schristos 
3132b3787f6Schristos #endif /* EVENT__HAVE_DEVPOLL */
314