xref: /illumos-gate/usr/src/cmd/bhyve/mevent.c (revision 84659b24)
14c87aefeSPatrick Mooney /*-
24c87aefeSPatrick Mooney  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
34c87aefeSPatrick Mooney  *
44c87aefeSPatrick Mooney  * Copyright (c) 2011 NetApp, Inc.
54c87aefeSPatrick Mooney  * All rights reserved.
64c87aefeSPatrick Mooney  *
74c87aefeSPatrick Mooney  * Redistribution and use in source and binary forms, with or without
84c87aefeSPatrick Mooney  * modification, are permitted provided that the following conditions
94c87aefeSPatrick Mooney  * are met:
104c87aefeSPatrick Mooney  * 1. Redistributions of source code must retain the above copyright
114c87aefeSPatrick Mooney  *    notice, this list of conditions and the following disclaimer.
124c87aefeSPatrick Mooney  * 2. Redistributions in binary form must reproduce the above copyright
134c87aefeSPatrick Mooney  *    notice, this list of conditions and the following disclaimer in the
144c87aefeSPatrick Mooney  *    documentation and/or other materials provided with the distribution.
154c87aefeSPatrick Mooney  *
164c87aefeSPatrick Mooney  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
174c87aefeSPatrick Mooney  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184c87aefeSPatrick Mooney  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194c87aefeSPatrick Mooney  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
204c87aefeSPatrick Mooney  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214c87aefeSPatrick Mooney  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224c87aefeSPatrick Mooney  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234c87aefeSPatrick Mooney  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244c87aefeSPatrick Mooney  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254c87aefeSPatrick Mooney  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264c87aefeSPatrick Mooney  * SUCH DAMAGE.
274c87aefeSPatrick Mooney  *
284c87aefeSPatrick Mooney  * $FreeBSD$
294c87aefeSPatrick Mooney  */
304c87aefeSPatrick Mooney 
314c87aefeSPatrick Mooney /*
324c87aefeSPatrick Mooney  * Copyright 2018 Joyent, Inc.
334c87aefeSPatrick Mooney  */
344c87aefeSPatrick Mooney 
354c87aefeSPatrick Mooney /*
364c87aefeSPatrick Mooney  * Micro event library for FreeBSD, designed for a single i/o thread
374c87aefeSPatrick Mooney  * using kqueue, and having events be persistent by default.
384c87aefeSPatrick Mooney  */
394c87aefeSPatrick Mooney 
404c87aefeSPatrick Mooney #include <sys/cdefs.h>
414c87aefeSPatrick Mooney __FBSDID("$FreeBSD$");
424c87aefeSPatrick Mooney 
434c87aefeSPatrick Mooney #include <assert.h>
444c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM
454c87aefeSPatrick Mooney #include <capsicum_helpers.h>
464c87aefeSPatrick Mooney #endif
474c87aefeSPatrick Mooney #include <err.h>
484c87aefeSPatrick Mooney #include <errno.h>
494c87aefeSPatrick Mooney #include <stdlib.h>
504c87aefeSPatrick Mooney #include <stdio.h>
514c87aefeSPatrick Mooney #include <string.h>
524c87aefeSPatrick Mooney #include <sysexits.h>
534c87aefeSPatrick Mooney #include <unistd.h>
544c87aefeSPatrick Mooney 
554c87aefeSPatrick Mooney #include <sys/types.h>
564c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM
574c87aefeSPatrick Mooney #include <sys/capsicum.h>
584c87aefeSPatrick Mooney #endif
594c87aefeSPatrick Mooney #ifdef __FreeBSD__
604c87aefeSPatrick Mooney #include <sys/event.h>
614c87aefeSPatrick Mooney #else
624c87aefeSPatrick Mooney #include <port.h>
634c87aefeSPatrick Mooney #include <sys/poll.h>
644c87aefeSPatrick Mooney #include <sys/siginfo.h>
654c87aefeSPatrick Mooney #include <sys/queue.h>
664c87aefeSPatrick Mooney #endif
674c87aefeSPatrick Mooney #include <sys/time.h>
684c87aefeSPatrick Mooney 
694c87aefeSPatrick Mooney #include <pthread.h>
704c87aefeSPatrick Mooney #include <pthread_np.h>
714c87aefeSPatrick Mooney 
724c87aefeSPatrick Mooney #include "mevent.h"
734c87aefeSPatrick Mooney 
744c87aefeSPatrick Mooney #define	MEVENT_MAX	64
754c87aefeSPatrick Mooney 
764c87aefeSPatrick Mooney #define	MEV_ADD		1
774c87aefeSPatrick Mooney #define	MEV_ENABLE	2
784c87aefeSPatrick Mooney #define	MEV_DISABLE	3
794c87aefeSPatrick Mooney #define	MEV_DEL_PENDING	4
804c87aefeSPatrick Mooney 
814c87aefeSPatrick Mooney extern char *vmname;
824c87aefeSPatrick Mooney 
834c87aefeSPatrick Mooney static pthread_t mevent_tid;
844c87aefeSPatrick Mooney static int mevent_timid = 43;
854c87aefeSPatrick Mooney static int mevent_pipefd[2];
864c87aefeSPatrick Mooney static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER;
874c87aefeSPatrick Mooney 
884c87aefeSPatrick Mooney struct mevent {
894c87aefeSPatrick Mooney 	void	(*me_func)(int, enum ev_type, void *);
904c87aefeSPatrick Mooney #define me_msecs me_fd
914c87aefeSPatrick Mooney 	int	me_fd;
924c87aefeSPatrick Mooney #ifdef __FreeBSD__
934c87aefeSPatrick Mooney 	int	me_timid;
944c87aefeSPatrick Mooney #else
954c87aefeSPatrick Mooney 	timer_t me_timid;
964c87aefeSPatrick Mooney #endif
974c87aefeSPatrick Mooney 	enum ev_type me_type;
984c87aefeSPatrick Mooney 	void    *me_param;
994c87aefeSPatrick Mooney 	int	me_cq;
1004c87aefeSPatrick Mooney 	int	me_state;
1014c87aefeSPatrick Mooney 	int	me_closefd;
1024c87aefeSPatrick Mooney #ifndef __FreeBSD__
1034c87aefeSPatrick Mooney 	port_notify_t	me_notify;
1044c87aefeSPatrick Mooney 	struct sigevent	me_sigev;
1054c87aefeSPatrick Mooney 	boolean_t	me_auto_requeue;
1064c87aefeSPatrick Mooney #endif
1074c87aefeSPatrick Mooney 	LIST_ENTRY(mevent) me_list;
1084c87aefeSPatrick Mooney };
1094c87aefeSPatrick Mooney 
1104c87aefeSPatrick Mooney static LIST_HEAD(listhead, mevent) global_head, change_head;
1114c87aefeSPatrick Mooney 
1124c87aefeSPatrick Mooney static void
1134c87aefeSPatrick Mooney mevent_qlock(void)
1144c87aefeSPatrick Mooney {
1154c87aefeSPatrick Mooney 	pthread_mutex_lock(&mevent_lmutex);
1164c87aefeSPatrick Mooney }
1174c87aefeSPatrick Mooney 
1184c87aefeSPatrick Mooney static void
1194c87aefeSPatrick Mooney mevent_qunlock(void)
1204c87aefeSPatrick Mooney {
1214c87aefeSPatrick Mooney 	pthread_mutex_unlock(&mevent_lmutex);
1224c87aefeSPatrick Mooney }
1234c87aefeSPatrick Mooney 
1244c87aefeSPatrick Mooney static void
1254c87aefeSPatrick Mooney mevent_pipe_read(int fd, enum ev_type type, void *param)
1264c87aefeSPatrick Mooney {
1274c87aefeSPatrick Mooney 	char buf[MEVENT_MAX];
1284c87aefeSPatrick Mooney 	int status;
1294c87aefeSPatrick Mooney 
1304c87aefeSPatrick Mooney 	/*
1314c87aefeSPatrick Mooney 	 * Drain the pipe read side. The fd is non-blocking so this is
1324c87aefeSPatrick Mooney 	 * safe to do.
1334c87aefeSPatrick Mooney 	 */
1344c87aefeSPatrick Mooney 	do {
1354c87aefeSPatrick Mooney 		status = read(fd, buf, sizeof(buf));
1364c87aefeSPatrick Mooney 	} while (status == MEVENT_MAX);
1374c87aefeSPatrick Mooney }
1384c87aefeSPatrick Mooney 
1394c87aefeSPatrick Mooney static void
1404c87aefeSPatrick Mooney mevent_notify(void)
1414c87aefeSPatrick Mooney {
142*84659b24SMichael Zeller 	char c = '\0';
1434c87aefeSPatrick Mooney 
1444c87aefeSPatrick Mooney 	/*
1454c87aefeSPatrick Mooney 	 * If calling from outside the i/o thread, write a byte on the
1464c87aefeSPatrick Mooney 	 * pipe to force the i/o thread to exit the blocking kevent call.
1474c87aefeSPatrick Mooney 	 */
1484c87aefeSPatrick Mooney 	if (mevent_pipefd[1] != 0 && pthread_self() != mevent_tid) {
1494c87aefeSPatrick Mooney 		write(mevent_pipefd[1], &c, 1);
1504c87aefeSPatrick Mooney 	}
1514c87aefeSPatrick Mooney }
1524c87aefeSPatrick Mooney #ifdef __FreeBSD__
1534c87aefeSPatrick Mooney static int
1544c87aefeSPatrick Mooney mevent_kq_filter(struct mevent *mevp)
1554c87aefeSPatrick Mooney {
1564c87aefeSPatrick Mooney 	int retval;
1574c87aefeSPatrick Mooney 
1584c87aefeSPatrick Mooney 	retval = 0;
1594c87aefeSPatrick Mooney 
1604c87aefeSPatrick Mooney 	if (mevp->me_type == EVF_READ)
1614c87aefeSPatrick Mooney 		retval = EVFILT_READ;
1624c87aefeSPatrick Mooney 
1634c87aefeSPatrick Mooney 	if (mevp->me_type == EVF_WRITE)
1644c87aefeSPatrick Mooney 		retval = EVFILT_WRITE;
1654c87aefeSPatrick Mooney 
1664c87aefeSPatrick Mooney 	if (mevp->me_type == EVF_TIMER)
1674c87aefeSPatrick Mooney 		retval = EVFILT_TIMER;
1684c87aefeSPatrick Mooney 
1694c87aefeSPatrick Mooney 	if (mevp->me_type == EVF_SIGNAL)
1704c87aefeSPatrick Mooney 		retval = EVFILT_SIGNAL;
1714c87aefeSPatrick Mooney 
1724c87aefeSPatrick Mooney 	return (retval);
1734c87aefeSPatrick Mooney }
1744c87aefeSPatrick Mooney 
1754c87aefeSPatrick Mooney static int
1764c87aefeSPatrick Mooney mevent_kq_flags(struct mevent *mevp)
1774c87aefeSPatrick Mooney {
1784c87aefeSPatrick Mooney 	int ret;
1794c87aefeSPatrick Mooney 
1804c87aefeSPatrick Mooney 	switch (mevp->me_state) {
1814c87aefeSPatrick Mooney 	case MEV_ADD:
1824c87aefeSPatrick Mooney 		ret = EV_ADD;		/* implicitly enabled */
1834c87aefeSPatrick Mooney 		break;
1844c87aefeSPatrick Mooney 	case MEV_ENABLE:
1854c87aefeSPatrick Mooney 		ret = EV_ENABLE;
1864c87aefeSPatrick Mooney 		break;
1874c87aefeSPatrick Mooney 	case MEV_DISABLE:
1884c87aefeSPatrick Mooney 		ret = EV_DISABLE;
1894c87aefeSPatrick Mooney 		break;
1904c87aefeSPatrick Mooney 	case MEV_DEL_PENDING:
1914c87aefeSPatrick Mooney 		ret = EV_DELETE;
1924c87aefeSPatrick Mooney 		break;
1934c87aefeSPatrick Mooney 	default:
1944c87aefeSPatrick Mooney 		assert(0);
1954c87aefeSPatrick Mooney 		break;
1964c87aefeSPatrick Mooney 	}
1974c87aefeSPatrick Mooney 
1984c87aefeSPatrick Mooney 	return (ret);
1994c87aefeSPatrick Mooney }
2004c87aefeSPatrick Mooney 
2014c87aefeSPatrick Mooney static int
2024c87aefeSPatrick Mooney mevent_kq_fflags(struct mevent *mevp)
2034c87aefeSPatrick Mooney {
2044c87aefeSPatrick Mooney 	/* XXX nothing yet, perhaps EV_EOF for reads ? */
2054c87aefeSPatrick Mooney 	return (0);
2064c87aefeSPatrick Mooney }
2074c87aefeSPatrick Mooney 
2084c87aefeSPatrick Mooney static int
2094c87aefeSPatrick Mooney mevent_build(int mfd, struct kevent *kev)
2104c87aefeSPatrick Mooney {
2114c87aefeSPatrick Mooney 	struct mevent *mevp, *tmpp;
2124c87aefeSPatrick Mooney 	int i;
2134c87aefeSPatrick Mooney 
2144c87aefeSPatrick Mooney 	i = 0;
2154c87aefeSPatrick Mooney 
2164c87aefeSPatrick Mooney 	mevent_qlock();
2174c87aefeSPatrick Mooney 
2184c87aefeSPatrick Mooney 	LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) {
2194c87aefeSPatrick Mooney 		if (mevp->me_closefd) {
2204c87aefeSPatrick Mooney 			/*
2214c87aefeSPatrick Mooney 			 * A close of the file descriptor will remove the
2224c87aefeSPatrick Mooney 			 * event
2234c87aefeSPatrick Mooney 			 */
2244c87aefeSPatrick Mooney 			close(mevp->me_fd);
2254c87aefeSPatrick Mooney 		} else {
2264c87aefeSPatrick Mooney 			if (mevp->me_type == EVF_TIMER) {
2274c87aefeSPatrick Mooney 				kev[i].ident = mevp->me_timid;
2284c87aefeSPatrick Mooney 				kev[i].data = mevp->me_msecs;
2294c87aefeSPatrick Mooney 			} else {
2304c87aefeSPatrick Mooney 				kev[i].ident = mevp->me_fd;
2314c87aefeSPatrick Mooney 				kev[i].data = 0;
2324c87aefeSPatrick Mooney 			}
2334c87aefeSPatrick Mooney 			kev[i].filter = mevent_kq_filter(mevp);
2344c87aefeSPatrick Mooney 			kev[i].flags = mevent_kq_flags(mevp);
2354c87aefeSPatrick Mooney 			kev[i].fflags = mevent_kq_fflags(mevp);
2364c87aefeSPatrick Mooney 			kev[i].udata = mevp;
2374c87aefeSPatrick Mooney 			i++;
2384c87aefeSPatrick Mooney 		}
2394c87aefeSPatrick Mooney 
2404c87aefeSPatrick Mooney 		mevp->me_cq = 0;
2414c87aefeSPatrick Mooney 		LIST_REMOVE(mevp, me_list);
2424c87aefeSPatrick Mooney 
2434c87aefeSPatrick Mooney 		if (mevp->me_state == MEV_DEL_PENDING) {
2444c87aefeSPatrick Mooney 			free(mevp);
2454c87aefeSPatrick Mooney 		} else {
2464c87aefeSPatrick Mooney 			LIST_INSERT_HEAD(&global_head, mevp, me_list);
2474c87aefeSPatrick Mooney 		}
2484c87aefeSPatrick Mooney 
2494c87aefeSPatrick Mooney 		assert(i < MEVENT_MAX);
2504c87aefeSPatrick Mooney 	}
2514c87aefeSPatrick Mooney 
2524c87aefeSPatrick Mooney 	mevent_qunlock();
2534c87aefeSPatrick Mooney 
2544c87aefeSPatrick Mooney 	return (i);
2554c87aefeSPatrick Mooney }
2564c87aefeSPatrick Mooney 
2574c87aefeSPatrick Mooney static void
2584c87aefeSPatrick Mooney mevent_handle(struct kevent *kev, int numev)
2594c87aefeSPatrick Mooney {
2604c87aefeSPatrick Mooney 	struct mevent *mevp;
2614c87aefeSPatrick Mooney 	int i;
2624c87aefeSPatrick Mooney 
2634c87aefeSPatrick Mooney 	for (i = 0; i < numev; i++) {
2644c87aefeSPatrick Mooney 		mevp = kev[i].udata;
2654c87aefeSPatrick Mooney 
2664c87aefeSPatrick Mooney 		/* XXX check for EV_ERROR ? */
2674c87aefeSPatrick Mooney 
2684c87aefeSPatrick Mooney 		(*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param);
2694c87aefeSPatrick Mooney 	}
2704c87aefeSPatrick Mooney }
2714c87aefeSPatrick Mooney 
2724c87aefeSPatrick Mooney #else /* __FreeBSD__ */
2734c87aefeSPatrick Mooney 
2744c87aefeSPatrick Mooney static void
2754c87aefeSPatrick Mooney mevent_update_one(struct mevent *mevp)
2764c87aefeSPatrick Mooney {
2774c87aefeSPatrick Mooney 	int portfd = mevp->me_notify.portnfy_port;
2784c87aefeSPatrick Mooney 
2794c87aefeSPatrick Mooney 	switch (mevp->me_type) {
2804c87aefeSPatrick Mooney 	case EVF_READ:
2814c87aefeSPatrick Mooney 	case EVF_WRITE:
2824c87aefeSPatrick Mooney 		mevp->me_auto_requeue = B_FALSE;
2834c87aefeSPatrick Mooney 
2844c87aefeSPatrick Mooney 		switch (mevp->me_state) {
2854c87aefeSPatrick Mooney 		case MEV_ADD:
2864c87aefeSPatrick Mooney 		case MEV_ENABLE:
2874c87aefeSPatrick Mooney 		{
2884c87aefeSPatrick Mooney 			int events;
2894c87aefeSPatrick Mooney 
2904c87aefeSPatrick Mooney 			events = (mevp->me_type == EVF_READ) ? POLLIN : POLLOUT;
2914c87aefeSPatrick Mooney 
2924c87aefeSPatrick Mooney 			if (port_associate(portfd, PORT_SOURCE_FD, mevp->me_fd,
2934c87aefeSPatrick Mooney 			    events, mevp) != 0) {
2944c87aefeSPatrick Mooney 				(void) fprintf(stderr,
2954c87aefeSPatrick Mooney 				    "port_associate fd %d %p failed: %s\n",
2964c87aefeSPatrick Mooney 				    mevp->me_fd, mevp, strerror(errno));
2974c87aefeSPatrick Mooney 			}
2984c87aefeSPatrick Mooney 			return;
2994c87aefeSPatrick Mooney 		}
3004c87aefeSPatrick Mooney 		case MEV_DISABLE:
3014c87aefeSPatrick Mooney 		case MEV_DEL_PENDING:
3024c87aefeSPatrick Mooney 			/*
3034c87aefeSPatrick Mooney 			 * A disable that comes in while an event is being
3044c87aefeSPatrick Mooney 			 * handled will result in an ENOENT.
3054c87aefeSPatrick Mooney 			 */
3064c87aefeSPatrick Mooney 			if (port_dissociate(portfd, PORT_SOURCE_FD,
3074c87aefeSPatrick Mooney 			    mevp->me_fd) != 0 && errno != ENOENT) {
3084c87aefeSPatrick Mooney 				(void) fprintf(stderr, "port_dissociate "
3094c87aefeSPatrick Mooney 				    "portfd %d fd %d mevp %p failed: %s\n",
3104c87aefeSPatrick Mooney 				    portfd, mevp->me_fd, mevp, strerror(errno));
3114c87aefeSPatrick Mooney 			}
3124c87aefeSPatrick Mooney 			return;
3134c87aefeSPatrick Mooney 		default:
3144c87aefeSPatrick Mooney 			goto abort;
3154c87aefeSPatrick Mooney 		}
3164c87aefeSPatrick Mooney 
3174c87aefeSPatrick Mooney 	case EVF_TIMER:
3184c87aefeSPatrick Mooney 		mevp->me_auto_requeue = B_TRUE;
3194c87aefeSPatrick Mooney 
3204c87aefeSPatrick Mooney 		switch (mevp->me_state) {
3214c87aefeSPatrick Mooney 		case MEV_ADD:
3224c87aefeSPatrick Mooney 		case MEV_ENABLE:
3234c87aefeSPatrick Mooney 		{
3244c87aefeSPatrick Mooney 			struct itimerspec it = { 0 };
3254c87aefeSPatrick Mooney 
3264c87aefeSPatrick Mooney 			mevp->me_sigev.sigev_notify = SIGEV_PORT;
3274c87aefeSPatrick Mooney 			mevp->me_sigev.sigev_value.sival_ptr = &mevp->me_notify;
3284c87aefeSPatrick Mooney 
3294c87aefeSPatrick Mooney 			if (timer_create(CLOCK_REALTIME, &mevp->me_sigev,
3304c87aefeSPatrick Mooney 			    &mevp->me_timid) != 0) {
3314c87aefeSPatrick Mooney 				(void) fprintf(stderr,
3324c87aefeSPatrick Mooney 				    "timer_create failed: %s", strerror(errno));
3334c87aefeSPatrick Mooney 				return;
3344c87aefeSPatrick Mooney 			}
3354c87aefeSPatrick Mooney 
3364c87aefeSPatrick Mooney 			/* The first timeout */
3374c87aefeSPatrick Mooney 			it.it_value.tv_sec = mevp->me_msecs / MILLISEC;
3384c87aefeSPatrick Mooney 			it.it_value.tv_nsec =
3394c87aefeSPatrick Mooney 				MSEC2NSEC(mevp->me_msecs % MILLISEC);
3404c87aefeSPatrick Mooney 			/* Repeat at the same interval */
3414c87aefeSPatrick Mooney 			it.it_interval = it.it_value;
3424c87aefeSPatrick Mooney 
3434c87aefeSPatrick Mooney 			if (timer_settime(mevp->me_timid, 0, &it, NULL) != 0) {
3444c87aefeSPatrick Mooney 				(void) fprintf(stderr, "timer_settime failed: "
3454c87aefeSPatrick Mooney 				    "%s", strerror(errno));
3464c87aefeSPatrick Mooney 			}
3474c87aefeSPatrick Mooney 			return;
3484c87aefeSPatrick Mooney 		}
3494c87aefeSPatrick Mooney 		case MEV_DISABLE:
3504c87aefeSPatrick Mooney 		case MEV_DEL_PENDING:
3514c87aefeSPatrick Mooney 			if (timer_delete(mevp->me_timid) != 0) {
3524c87aefeSPatrick Mooney 				(void) fprintf(stderr, "timer_delete failed: "
3534c87aefeSPatrick Mooney 				    "%s", strerror(errno));
3544c87aefeSPatrick Mooney 			}
3554c87aefeSPatrick Mooney 			return;
3564c87aefeSPatrick Mooney 		default:
3574c87aefeSPatrick Mooney 			goto abort;
3584c87aefeSPatrick Mooney 		}
3594c87aefeSPatrick Mooney 	default:
3604c87aefeSPatrick Mooney 		/* EVF_SIGNAL not yet implemented. */
3614c87aefeSPatrick Mooney 		goto abort;
3624c87aefeSPatrick Mooney 	}
3634c87aefeSPatrick Mooney 
3644c87aefeSPatrick Mooney abort:
3654c87aefeSPatrick Mooney 	(void) fprintf(stderr, "%s: unhandled type %d state %d\n", __func__,
3664c87aefeSPatrick Mooney 	    mevp->me_type, mevp->me_state);
3674c87aefeSPatrick Mooney 	abort();
3684c87aefeSPatrick Mooney }
3694c87aefeSPatrick Mooney 
3704c87aefeSPatrick Mooney static void
3714c87aefeSPatrick Mooney mevent_update_pending(int portfd)
3724c87aefeSPatrick Mooney {
3734c87aefeSPatrick Mooney 	struct mevent *mevp, *tmpp;
3744c87aefeSPatrick Mooney 
3754c87aefeSPatrick Mooney 	mevent_qlock();
3764c87aefeSPatrick Mooney 
3774c87aefeSPatrick Mooney 	LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) {
3784c87aefeSPatrick Mooney 		mevp->me_notify.portnfy_port = portfd;
3794c87aefeSPatrick Mooney 		mevp->me_notify.portnfy_user = mevp;
3804c87aefeSPatrick Mooney 		if (mevp->me_closefd) {
3814c87aefeSPatrick Mooney 			/*
3824c87aefeSPatrick Mooney 			 * A close of the file descriptor will remove the
3834c87aefeSPatrick Mooney 			 * event
3844c87aefeSPatrick Mooney 			 */
3854c87aefeSPatrick Mooney 			(void) close(mevp->me_fd);
3864c87aefeSPatrick Mooney 			mevp->me_fd = -1;
3874c87aefeSPatrick Mooney 		} else {
3884c87aefeSPatrick Mooney 			mevent_update_one(mevp);
3894c87aefeSPatrick Mooney 		}
3904c87aefeSPatrick Mooney 
3914c87aefeSPatrick Mooney 		mevp->me_cq = 0;
3924c87aefeSPatrick Mooney 		LIST_REMOVE(mevp, me_list);
3934c87aefeSPatrick Mooney 
3944c87aefeSPatrick Mooney 		if (mevp->me_state == MEV_DEL_PENDING) {
3954c87aefeSPatrick Mooney 			free(mevp);
3964c87aefeSPatrick Mooney 		} else {
3974c87aefeSPatrick Mooney 			LIST_INSERT_HEAD(&global_head, mevp, me_list);
3984c87aefeSPatrick Mooney 		}
3994c87aefeSPatrick Mooney 	}
4004c87aefeSPatrick Mooney 
4014c87aefeSPatrick Mooney 	mevent_qunlock();
4024c87aefeSPatrick Mooney }
4034c87aefeSPatrick Mooney 
4044c87aefeSPatrick Mooney static void
4054c87aefeSPatrick Mooney mevent_handle_pe(port_event_t *pe)
4064c87aefeSPatrick Mooney {
4074c87aefeSPatrick Mooney 	struct mevent *mevp = pe->portev_user;
4084c87aefeSPatrick Mooney 
4094c87aefeSPatrick Mooney 	mevent_qunlock();
4104c87aefeSPatrick Mooney 
4114c87aefeSPatrick Mooney 	(*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param);
4124c87aefeSPatrick Mooney 
4134c87aefeSPatrick Mooney 	mevent_qlock();
4144c87aefeSPatrick Mooney 	if (!mevp->me_cq && !mevp->me_auto_requeue) {
4154c87aefeSPatrick Mooney 		mevent_update_one(mevp);
4164c87aefeSPatrick Mooney 	}
4174c87aefeSPatrick Mooney 	mevent_qunlock();
4184c87aefeSPatrick Mooney }
4194c87aefeSPatrick Mooney #endif
4204c87aefeSPatrick Mooney 
4214c87aefeSPatrick Mooney struct mevent *
4224c87aefeSPatrick Mooney mevent_add(int tfd, enum ev_type type,
4234c87aefeSPatrick Mooney 	   void (*func)(int, enum ev_type, void *), void *param)
4244c87aefeSPatrick Mooney {
4254c87aefeSPatrick Mooney 	struct mevent *lp, *mevp;
4264c87aefeSPatrick Mooney 
4274c87aefeSPatrick Mooney 	if (tfd < 0 || func == NULL) {
4284c87aefeSPatrick Mooney 		return (NULL);
4294c87aefeSPatrick Mooney 	}
4304c87aefeSPatrick Mooney 
4314c87aefeSPatrick Mooney 	mevp = NULL;
4324c87aefeSPatrick Mooney 
4334c87aefeSPatrick Mooney 	mevent_qlock();
4344c87aefeSPatrick Mooney 
4354c87aefeSPatrick Mooney 	/*
4364c87aefeSPatrick Mooney 	 * Verify that the fd/type tuple is not present in any list
4374c87aefeSPatrick Mooney 	 */
4384c87aefeSPatrick Mooney 	LIST_FOREACH(lp, &global_head, me_list) {
4394c87aefeSPatrick Mooney 		if (type != EVF_TIMER && lp->me_fd == tfd &&
4404c87aefeSPatrick Mooney 		    lp->me_type == type) {
4414c87aefeSPatrick Mooney 			goto exit;
4424c87aefeSPatrick Mooney 		}
4434c87aefeSPatrick Mooney 	}
4444c87aefeSPatrick Mooney 
4454c87aefeSPatrick Mooney 	LIST_FOREACH(lp, &change_head, me_list) {
4464c87aefeSPatrick Mooney 		if (type != EVF_TIMER && lp->me_fd == tfd &&
4474c87aefeSPatrick Mooney 		    lp->me_type == type) {
4484c87aefeSPatrick Mooney 			goto exit;
4494c87aefeSPatrick Mooney 		}
4504c87aefeSPatrick Mooney 	}
4514c87aefeSPatrick Mooney 
4524c87aefeSPatrick Mooney 	/*
4534c87aefeSPatrick Mooney 	 * Allocate an entry, populate it, and add it to the change list.
4544c87aefeSPatrick Mooney 	 */
4554c87aefeSPatrick Mooney 	mevp = calloc(1, sizeof(struct mevent));
4564c87aefeSPatrick Mooney 	if (mevp == NULL) {
4574c87aefeSPatrick Mooney 		goto exit;
4584c87aefeSPatrick Mooney 	}
4594c87aefeSPatrick Mooney 
4604c87aefeSPatrick Mooney 	if (type == EVF_TIMER) {
4614c87aefeSPatrick Mooney 		mevp->me_msecs = tfd;
4624c87aefeSPatrick Mooney 		mevp->me_timid = mevent_timid++;
4634c87aefeSPatrick Mooney 	} else
4644c87aefeSPatrick Mooney 		mevp->me_fd = tfd;
4654c87aefeSPatrick Mooney 	mevp->me_type = type;
4664c87aefeSPatrick Mooney 	mevp->me_func = func;
4674c87aefeSPatrick Mooney 	mevp->me_param = param;
4684c87aefeSPatrick Mooney 
4694c87aefeSPatrick Mooney 	LIST_INSERT_HEAD(&change_head, mevp, me_list);
4704c87aefeSPatrick Mooney 	mevp->me_cq = 1;
4714c87aefeSPatrick Mooney 	mevp->me_state = MEV_ADD;
4724c87aefeSPatrick Mooney 	mevent_notify();
4734c87aefeSPatrick Mooney 
4744c87aefeSPatrick Mooney exit:
4754c87aefeSPatrick Mooney 	mevent_qunlock();
4764c87aefeSPatrick Mooney 
4774c87aefeSPatrick Mooney 	return (mevp);
4784c87aefeSPatrick Mooney }
4794c87aefeSPatrick Mooney 
4804c87aefeSPatrick Mooney static int
4814c87aefeSPatrick Mooney mevent_update(struct mevent *evp, int newstate)
4824c87aefeSPatrick Mooney {
4834c87aefeSPatrick Mooney 	/*
4844c87aefeSPatrick Mooney 	 * It's not possible to enable/disable a deleted event
4854c87aefeSPatrick Mooney 	 */
4864c87aefeSPatrick Mooney 	if (evp->me_state == MEV_DEL_PENDING)
4874c87aefeSPatrick Mooney 		return (EINVAL);
4884c87aefeSPatrick Mooney 
4894c87aefeSPatrick Mooney 	/*
4904c87aefeSPatrick Mooney 	 * No update needed if state isn't changing
4914c87aefeSPatrick Mooney 	 */
4924c87aefeSPatrick Mooney 	if (evp->me_state == newstate)
4934c87aefeSPatrick Mooney 		return (0);
4944c87aefeSPatrick Mooney 
4954c87aefeSPatrick Mooney 	mevent_qlock();
4964c87aefeSPatrick Mooney 
4974c87aefeSPatrick Mooney 	evp->me_state = newstate;
4984c87aefeSPatrick Mooney 
4994c87aefeSPatrick Mooney 	/*
5004c87aefeSPatrick Mooney 	 * Place the entry onto the changed list if not already there.
5014c87aefeSPatrick Mooney 	 */
5024c87aefeSPatrick Mooney 	if (evp->me_cq == 0) {
5034c87aefeSPatrick Mooney 		evp->me_cq = 1;
5044c87aefeSPatrick Mooney 		LIST_REMOVE(evp, me_list);
5054c87aefeSPatrick Mooney 		LIST_INSERT_HEAD(&change_head, evp, me_list);
5064c87aefeSPatrick Mooney 		mevent_notify();
5074c87aefeSPatrick Mooney 	}
5084c87aefeSPatrick Mooney 
5094c87aefeSPatrick Mooney 	mevent_qunlock();
5104c87aefeSPatrick Mooney 
5114c87aefeSPatrick Mooney 	return (0);
5124c87aefeSPatrick Mooney }
5134c87aefeSPatrick Mooney 
5144c87aefeSPatrick Mooney int
5154c87aefeSPatrick Mooney mevent_enable(struct mevent *evp)
5164c87aefeSPatrick Mooney {
5174c87aefeSPatrick Mooney 
5184c87aefeSPatrick Mooney 	return (mevent_update(evp, MEV_ENABLE));
5194c87aefeSPatrick Mooney }
5204c87aefeSPatrick Mooney 
5214c87aefeSPatrick Mooney int
5224c87aefeSPatrick Mooney mevent_disable(struct mevent *evp)
5234c87aefeSPatrick Mooney {
5244c87aefeSPatrick Mooney 
5254c87aefeSPatrick Mooney 	return (mevent_update(evp, MEV_DISABLE));
5264c87aefeSPatrick Mooney }
5274c87aefeSPatrick Mooney 
5284c87aefeSPatrick Mooney static int
5294c87aefeSPatrick Mooney mevent_delete_event(struct mevent *evp, int closefd)
5304c87aefeSPatrick Mooney {
5314c87aefeSPatrick Mooney 	mevent_qlock();
5324c87aefeSPatrick Mooney 
5334c87aefeSPatrick Mooney 	/*
5344c87aefeSPatrick Mooney          * Place the entry onto the changed list if not already there, and
5354c87aefeSPatrick Mooney 	 * mark as to be deleted.
5364c87aefeSPatrick Mooney          */
5374c87aefeSPatrick Mooney         if (evp->me_cq == 0) {
5384c87aefeSPatrick Mooney 		evp->me_cq = 1;
5394c87aefeSPatrick Mooney 		LIST_REMOVE(evp, me_list);
5404c87aefeSPatrick Mooney 		LIST_INSERT_HEAD(&change_head, evp, me_list);
5414c87aefeSPatrick Mooney 		mevent_notify();
5424c87aefeSPatrick Mooney         }
5434c87aefeSPatrick Mooney 	evp->me_state = MEV_DEL_PENDING;
5444c87aefeSPatrick Mooney 
5454c87aefeSPatrick Mooney 	if (closefd)
5464c87aefeSPatrick Mooney 		evp->me_closefd = 1;
5474c87aefeSPatrick Mooney 
5484c87aefeSPatrick Mooney 	mevent_qunlock();
5494c87aefeSPatrick Mooney 
5504c87aefeSPatrick Mooney 	return (0);
5514c87aefeSPatrick Mooney }
5524c87aefeSPatrick Mooney 
5534c87aefeSPatrick Mooney int
5544c87aefeSPatrick Mooney mevent_delete(struct mevent *evp)
5554c87aefeSPatrick Mooney {
5564c87aefeSPatrick Mooney 
5574c87aefeSPatrick Mooney 	return (mevent_delete_event(evp, 0));
5584c87aefeSPatrick Mooney }
5594c87aefeSPatrick Mooney 
5604c87aefeSPatrick Mooney int
5614c87aefeSPatrick Mooney mevent_delete_close(struct mevent *evp)
5624c87aefeSPatrick Mooney {
5634c87aefeSPatrick Mooney 
5644c87aefeSPatrick Mooney 	return (mevent_delete_event(evp, 1));
5654c87aefeSPatrick Mooney }
5664c87aefeSPatrick Mooney 
5674c87aefeSPatrick Mooney static void
5684c87aefeSPatrick Mooney mevent_set_name(void)
5694c87aefeSPatrick Mooney {
5704c87aefeSPatrick Mooney 
5714c87aefeSPatrick Mooney 	pthread_set_name_np(mevent_tid, "mevent");
5724c87aefeSPatrick Mooney }
5734c87aefeSPatrick Mooney 
5744c87aefeSPatrick Mooney void
5754c87aefeSPatrick Mooney mevent_dispatch(void)
5764c87aefeSPatrick Mooney {
5774c87aefeSPatrick Mooney #ifdef __FreeBSD__
5784c87aefeSPatrick Mooney 	struct kevent changelist[MEVENT_MAX];
5794c87aefeSPatrick Mooney 	struct kevent eventlist[MEVENT_MAX];
5804c87aefeSPatrick Mooney 	struct mevent *pipev;
5814c87aefeSPatrick Mooney 	int mfd;
5824c87aefeSPatrick Mooney 	int numev;
5834c87aefeSPatrick Mooney #else
5844c87aefeSPatrick Mooney 	struct mevent *pipev;
5854c87aefeSPatrick Mooney 	int portfd;
5864c87aefeSPatrick Mooney #endif
5874c87aefeSPatrick Mooney 	int ret;
5884c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM
5894c87aefeSPatrick Mooney 	cap_rights_t rights;
5904c87aefeSPatrick Mooney #endif
5914c87aefeSPatrick Mooney 
5924c87aefeSPatrick Mooney 	mevent_tid = pthread_self();
5934c87aefeSPatrick Mooney 	mevent_set_name();
5944c87aefeSPatrick Mooney 
5954c87aefeSPatrick Mooney #ifdef __FreeBSD__
5964c87aefeSPatrick Mooney 	mfd = kqueue();
5974c87aefeSPatrick Mooney 	assert(mfd > 0);
5984c87aefeSPatrick Mooney #else
5994c87aefeSPatrick Mooney 	portfd = port_create();
6004c87aefeSPatrick Mooney 	assert(portfd >= 0);
6014c87aefeSPatrick Mooney #endif
6024c87aefeSPatrick Mooney 
6034c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM
6044c87aefeSPatrick Mooney 	cap_rights_init(&rights, CAP_KQUEUE);
6054c87aefeSPatrick Mooney 	if (caph_rights_limit(mfd, &rights) == -1)
6064c87aefeSPatrick Mooney 		errx(EX_OSERR, "Unable to apply rights for sandbox");
6074c87aefeSPatrick Mooney #endif
6084c87aefeSPatrick Mooney 
6094c87aefeSPatrick Mooney 	/*
6104c87aefeSPatrick Mooney 	 * Open the pipe that will be used for other threads to force
6114c87aefeSPatrick Mooney 	 * the blocking kqueue call to exit by writing to it. Set the
6124c87aefeSPatrick Mooney 	 * descriptor to non-blocking.
6134c87aefeSPatrick Mooney 	 */
6144c87aefeSPatrick Mooney 	ret = pipe(mevent_pipefd);
6154c87aefeSPatrick Mooney 	if (ret < 0) {
6164c87aefeSPatrick Mooney 		perror("pipe");
6174c87aefeSPatrick Mooney 		exit(0);
6184c87aefeSPatrick Mooney 	}
6194c87aefeSPatrick Mooney 
6204c87aefeSPatrick Mooney #ifndef WITHOUT_CAPSICUM
6214c87aefeSPatrick Mooney 	cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
6224c87aefeSPatrick Mooney 	if (caph_rights_limit(mevent_pipefd[0], &rights) == -1)
6234c87aefeSPatrick Mooney 		errx(EX_OSERR, "Unable to apply rights for sandbox");
6244c87aefeSPatrick Mooney 	if (caph_rights_limit(mevent_pipefd[1], &rights) == -1)
6254c87aefeSPatrick Mooney 		errx(EX_OSERR, "Unable to apply rights for sandbox");
6264c87aefeSPatrick Mooney #endif
6274c87aefeSPatrick Mooney 
6284c87aefeSPatrick Mooney 	/*
6294c87aefeSPatrick Mooney 	 * Add internal event handler for the pipe write fd
6304c87aefeSPatrick Mooney 	 */
6314c87aefeSPatrick Mooney 	pipev = mevent_add(mevent_pipefd[0], EVF_READ, mevent_pipe_read, NULL);
6324c87aefeSPatrick Mooney 	assert(pipev != NULL);
6334c87aefeSPatrick Mooney 
6344c87aefeSPatrick Mooney 	for (;;) {
6354c87aefeSPatrick Mooney #ifdef __FreeBSD__
6364c87aefeSPatrick Mooney 		/*
6374c87aefeSPatrick Mooney 		 * Build changelist if required.
6384c87aefeSPatrick Mooney 		 * XXX the changelist can be put into the blocking call
6394c87aefeSPatrick Mooney 		 * to eliminate the extra syscall. Currently better for
6404c87aefeSPatrick Mooney 		 * debug.
6414c87aefeSPatrick Mooney 		 */
6424c87aefeSPatrick Mooney 		numev = mevent_build(mfd, changelist);
6434c87aefeSPatrick Mooney 		if (numev) {
6444c87aefeSPatrick Mooney 			ret = kevent(mfd, changelist, numev, NULL, 0, NULL);
6454c87aefeSPatrick Mooney 			if (ret == -1) {
6464c87aefeSPatrick Mooney 				perror("Error return from kevent change");
6474c87aefeSPatrick Mooney 			}
6484c87aefeSPatrick Mooney 		}
6494c87aefeSPatrick Mooney 
6504c87aefeSPatrick Mooney 		/*
6514c87aefeSPatrick Mooney 		 * Block awaiting events
6524c87aefeSPatrick Mooney 		 */
6534c87aefeSPatrick Mooney 		ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL);
6544c87aefeSPatrick Mooney 		if (ret == -1 && errno != EINTR) {
6554c87aefeSPatrick Mooney 			perror("Error return from kevent monitor");
6564c87aefeSPatrick Mooney 		}
6574c87aefeSPatrick Mooney 
6584c87aefeSPatrick Mooney 		/*
6594c87aefeSPatrick Mooney 		 * Handle reported events
6604c87aefeSPatrick Mooney 		 */
6614c87aefeSPatrick Mooney 		mevent_handle(eventlist, ret);
6624c87aefeSPatrick Mooney 
6634c87aefeSPatrick Mooney #else /* __FreeBSD__ */
6644c87aefeSPatrick Mooney 		port_event_t pev;
6654c87aefeSPatrick Mooney 
6664c87aefeSPatrick Mooney 		/* Handle any pending updates */
6674c87aefeSPatrick Mooney 		mevent_update_pending(portfd);
6684c87aefeSPatrick Mooney 
6694c87aefeSPatrick Mooney 		/* Block awaiting events */
6704c87aefeSPatrick Mooney 		ret = port_get(portfd, &pev, NULL);
6714c87aefeSPatrick Mooney 		if (ret != 0 && errno != EINTR) {
6724c87aefeSPatrick Mooney 			perror("Error return from port_get");
6734c87aefeSPatrick Mooney 			continue;
6744c87aefeSPatrick Mooney 		}
6754c87aefeSPatrick Mooney 
6764c87aefeSPatrick Mooney 		/* Handle reported event */
6774c87aefeSPatrick Mooney 		mevent_handle_pe(&pev);
6784c87aefeSPatrick Mooney #endif /* __FreeBSD__ */
6794c87aefeSPatrick Mooney 	}
6804c87aefeSPatrick Mooney }
681