--- old/usr/src/uts/common/fs/portfs/port.c	Wed Apr 29 19:58:34 2020
+++ new/usr/src/uts/common/fs/portfs/port.c	Wed Apr 29 19:58:34 2020
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
+ * Copyright 2020 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -1406,7 +1406,7 @@
 				list_remove(glist, pev);
 				pev->portkev_flags &= ~(PORT_CLEANUP_DONE);
 				if (PORT_FREE_EVENT(pev))
-					port_free_event_local(pev, 0);
+					port_free_event_local(pev, B_TRUE);
 				tnent--;
 				continue;
 			}
@@ -1454,7 +1454,7 @@
 				list_remove(glist, pev);
 				pev->portkev_flags &= ~(PORT_CLEANUP_DONE);
 				if (PORT_FREE_EVENT(pev))
-					port_free_event_local(pev, 0);
+					port_free_event_local(pev, B_TRUE);
 				tnent--;
 				continue;
 			}
@@ -1626,7 +1626,7 @@
 		}
 	}
 	if (free_event)
-		port_free_event_local(pkevp, 0);
+		port_free_event_local(pkevp, B_TRUE);
 	return (0);
 }
 
@@ -1681,7 +1681,7 @@
 		}
 	}
 	if (free_event)
-		port_free_event_local(pkevp, 0);
+		port_free_event_local(pkevp, B_TRUE);
 	return (0);
 }
 #endif	/* _SYSCALL32_IMPL */
--- old/usr/src/uts/common/fs/portfs/port_fop.c	Wed Apr 29 19:58:35 2020
+++ new/usr/src/uts/common/fs/portfs/port_fop.c	Wed Apr 29 19:58:35 2020
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
  */
 
 /*
@@ -257,7 +257,7 @@
 	VOPNAME_READ,		{ .femop_read = port_fop_read },
 	VOPNAME_WRITE,		{ .femop_write = port_fop_write },
 	VOPNAME_MAP,		{ .femop_map = port_fop_map },
-	VOPNAME_SETATTR, 	{ .femop_setattr = port_fop_setattr },
+	VOPNAME_SETATTR,	{ .femop_setattr = port_fop_setattr },
 	VOPNAME_CREATE,		{ .femop_create = port_fop_create },
 	VOPNAME_REMOVE,		{ .femop_remove = port_fop_remove },
 	VOPNAME_LINK,		{ .femop_link = port_fop_link },
@@ -266,7 +266,7 @@
 	VOPNAME_RMDIR,		{ .femop_rmdir = port_fop_rmdir },
 	VOPNAME_READDIR,	{ .femop_readdir = port_fop_readdir },
 	VOPNAME_SYMLINK,	{ .femop_symlink = port_fop_symlink },
-	VOPNAME_SETSECATTR, 	{ .femop_setsecattr = port_fop_setsecattr },
+	VOPNAME_SETSECATTR,	{ .femop_setsecattr = port_fop_setsecattr },
 	VOPNAME_VNEVENT,	{ .femop_vnevent = port_fop_vnevent },
 	NULL,	NULL
 };
@@ -275,7 +275,7 @@
  * Fsem - vfs ops hooks
  */
 const fs_operation_def_t	port_vfssrc_template[] = {
-	VFSNAME_UNMOUNT, 	{ .fsemop_unmount = port_fop_unmount },
+	VFSNAME_UNMOUNT,	{ .fsemop_unmount = port_fop_unmount },
 	NULL,	NULL
 };
 
@@ -479,7 +479,7 @@
 
 	if (pkevp != NULL) {
 		(void) port_remove_done_event(pkevp);
-		port_free_event_local(pkevp, 0);
+		port_free_event_local(pkevp, B_TRUE);
 	}
 
 	port_pcache_delete(pfcp, pfp);
@@ -730,8 +730,8 @@
  * the vnode(s).
  */
 int
-port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp,
-	char **cname, int *len, int follow)
+port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp, char **cname,
+    int *len, int follow)
 {
 	int error = 0;
 	struct pathname pn;
@@ -815,7 +815,7 @@
  */
 static void
 port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
-	portfop_t *pfp, void *objptr, uintptr_t object)
+    portfop_t *pfp, void *objptr, uintptr_t object)
 {
 	vattr_t		vatt;
 	portfop_vp_t	*pvp = vp->v_fopdata;
@@ -1102,8 +1102,8 @@
  */
 int
 port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
-	uintptr_t object, int events, void *user, char *cname, int clen,
-	vnode_t *dvp)
+    uintptr_t object, int events, void *user, char *cname, int clen,
+    vnode_t *dvp)
 {
 	portfop_t	*pfp = NULL;
 	port_kevent_t	*pkevp;
@@ -1610,7 +1610,7 @@
 	portfop_t	*pfpnext;
 	int		index, i;
 	port_source_t	*pse;
-	vnode_t 	*tdvp = NULL;
+	vnode_t	*tdvp = NULL;
 	vnode_t		*vpl[PORTFOP_NVP];
 
 	pse = port_getsrc(pp, PORT_SOURCE_FILE);
@@ -1980,7 +1980,7 @@
 		event  |= FILE_TRUNC;
 	}
 	if (event) {
-		port_fop_sendevent(vp, 	event, NULL, NULL);
+		port_fop_sendevent(vp, event, NULL, NULL);
 	}
 }
 
--- old/usr/src/uts/common/fs/portfs/port_vnops.c	Wed Apr 29 19:58:35 2020
+++ new/usr/src/uts/common/fs/portfs/port_vnops.c	Wed Apr 29 19:58:35 2020
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2020 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -115,6 +115,8 @@
 
 	mutex_enter(&portq->portq_mutex);
 	while (pkevp = list_head(&portq->portq_list)) {
+		port_t *pp = pkevp->portkev_port;
+
 		portq->portq_nent--;
 		list_remove(&portq->portq_list, pkevp);
 		if (pkevp->portkev_callback) {
@@ -122,8 +124,16 @@
 			    &events, pkevp->portkev_pid, PORT_CALLBACK_CLOSE,
 			    pkevp);
 		}
-		mutex_exit(&portq->portq_mutex);
-		port_free_event_local(pkevp, 0);
+		/*
+		 * Don't drop the portq_mutex, but instead perform the
+		 * decrement of port_curr in advance of calling
+		 * port_free_event_local().  We do need to reacquire
+		 * portq_mutex so we can properly wait for any
+		 * pollwakeup()-signalled threads to finish up.
+		 */
+		if (--pp->port_curr < pp->port_max_events)
+			cv_signal(&pp->port_cv);
+		port_free_event_local(pkevp, B_FALSE);
 		mutex_enter(&portq->portq_mutex);
 	}
 
@@ -344,8 +354,8 @@
 static void
 port_inactive(struct vnode *vp, cred_t *cr, caller_context_t *ct)
 {
-	port_t 	*pp = VTOEP(vp);
-	extern 	port_kstat_t port_kstat;
+	port_t	*pp = VTOEP(vp);
+	extern	port_kstat_t port_kstat;
 
 	mutex_enter(&port_control.pc_mutex);
 	port_control.pc_nents--;
--- old/usr/src/uts/common/os/port_subr.c	Wed Apr 29 19:58:35 2020
+++ new/usr/src/uts/common/os/port_subr.c	Wed Apr 29 19:58:35 2020
@@ -24,7 +24,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright 2020 Joyent, Inc.
+ */
 
 /*
  * This file containts all the functions required for interactions of
@@ -204,10 +206,15 @@
 		/*
 		 * Need to save port_t for calling pollwakeup since port_getn()
 		 * may end up freeing pkevp once portq_mutex is dropped.
+		 *
+		 * For that matter, if PORTQ_CLOSE is set, all of the port_t
+		 * may be freed upon dropping portq_mutex.
 		 */
-		pp = pkevp->portkev_port;
+		pp = (portq->portq_flags & PORTQ_CLOSE) ?
+		    NULL : pkevp->portkev_port;
 		mutex_exit(&portq->portq_mutex);
-		pollwakeup(&pp->port_pollhd, POLLIN);
+		if (pp != NULL)
+			pollwakeup(&pp->port_pollhd, POLLIN);
 	} else {
 		mutex_exit(&portq->portq_mutex);
 	}
@@ -250,6 +257,7 @@
 	port_t		*pp;
 	file_t		*fp;
 	port_kevent_t	*pkevp;
+	int		err;
 
 	if ((fp = getf(port)) == NULL)
 		return (EBADF);
@@ -271,11 +279,20 @@
 	 */
 	pp = VTOEP(fp->f_vnode);
 	mutex_enter(&pp->port_queue.portq_mutex);
-	if (pp->port_curr >= pp->port_max_events) {
+
+	/* Two possible error conditions. */
+	if (pp->port_queue.portq_flags & PORTQ_CLOSE)
+		err = EBADFD;	/* Mid-close. */
+	else if (pp->port_curr >= pp->port_max_events)
+		err = EAGAIN;	/* Resources unavailabile. */
+	else
+		err = 0;	/* Good to go! */
+
+	if (err != 0) {
 		mutex_exit(&pp->port_queue.portq_mutex);
 		kmem_cache_free(port_control.pc_cache, pkevp);
 		releasef(port);
-		return (EAGAIN);
+		return (err);
 	}
 	pp->port_curr++;
 	mutex_exit(&pp->port_queue.portq_mutex);
@@ -319,6 +336,7 @@
     port_kevent_t **pkevpp)
 {
 	port_kevent_t	*pkevp;
+	int		err;
 
 	pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
 	if (pkevp == NULL)
@@ -325,10 +343,19 @@
 		return (ENOMEM);
 
 	mutex_enter(&pp->port_queue.portq_mutex);
-	if (pp->port_curr >= pp->port_max_events) {
+
+	/* Two possible error conditions. */
+	if (pp->port_queue.portq_flags & PORTQ_CLOSE)
+		err = EBADFD;	/* Mid-close. */
+	else if (pp->port_curr >= pp->port_max_events)
+		err = EAGAIN;	/* Resources unavailable. */
+	else
+		err = 0;	/* Good to go! */
+
+	if (err != 0) {
 		mutex_exit(&pp->port_queue.portq_mutex);
 		kmem_cache_free(port_control.pc_cache, pkevp);
-		return (EAGAIN);
+		return (err);
 	}
 	pp->port_curr++;
 	mutex_exit(&pp->port_queue.portq_mutex);
@@ -358,6 +385,16 @@
 	    kmem_cache_alloc(port_control.pc_cache, KM_SLEEP);
 
 	mutex_enter(&pp->port_queue.portq_mutex);
+	/*
+	 * Mid-close check needs to happen immediately before any
+	 * resource checking or cv_wait()-ing of any sort.
+	 */
+	if (pp->port_queue.portq_flags & PORTQ_CLOSE) {
+		mutex_exit(&pp->port_queue.portq_mutex);
+		kmem_cache_free(port_control.pc_cache, pkevp);
+		return (EBADFD);
+	}
+
 	while (pp->port_curr >= pp->port_max_events) {
 		if (!cv_wait_sig(&pp->port_cv, &pp->port_queue.portq_mutex)) {
 			/* signal detected */
@@ -365,6 +402,13 @@
 			kmem_cache_free(port_control.pc_cache, pkevp);
 			return (EINTR);
 		}
+
+		/* Oh, and we have to re-check the close state. */
+		if (pp->port_queue.portq_flags & PORTQ_CLOSE) {
+			mutex_exit(&pp->port_queue.portq_mutex);
+			kmem_cache_free(port_control.pc_cache, pkevp);
+			return (EBADFD);
+		}
 	}
 	pp->port_curr++;
 	mutex_exit(&pp->port_queue.portq_mutex);
@@ -442,7 +486,7 @@
 	if (pp == NULL)
 		return;
 	if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) {
-		port_free_event_local(pkevp, 0);
+		port_free_event_local(pkevp, B_TRUE);
 		return;
 	}
 
@@ -476,8 +520,12 @@
 		if (pp->port_curr <= portq->portq_nent)
 			cv_signal(&portq->portq_closecv);
 	}
-	mutex_exit(&portq->portq_mutex);
-	port_free_event_local(pkevp, 1);
+
+	/*
+	 * We're holding portq_mutex and we decremented port_curr, so don't
+	 * have port_free_event_local() do it for us.
+	 */
+	port_free_event_local(pkevp, B_FALSE);
 }
 
 /*
@@ -484,9 +532,13 @@
  * This event port internal function is used by port_free_event() and
  * other port internal functions to return event structures back to the
  * kmem_cache.
+ *
+ * If the caller already holds the portq_mutex, indicate by setting
+ * lock_and_decrement to B_FALSE.  This function MUST drop portq_mutex as
+ * it can call pollwakeup() at its end.
  */
 void
-port_free_event_local(port_kevent_t *pkevp, int counter)
+port_free_event_local(port_kevent_t *pkevp, boolean_t lock_and_decrement)
 {
 	port_t *pp = pkevp->portkev_port;
 	port_queue_t *portq = &pp->port_queue;
@@ -498,12 +550,26 @@
 	mutex_destroy(&pkevp->portkev_lock);
 	kmem_cache_free(port_control.pc_cache, pkevp);
 
-	mutex_enter(&portq->portq_mutex);
-	if (counter == 0) {
+	if (lock_and_decrement) {
+		/*
+		 * If we're entering here from outside port_event_free(),
+		 * grab the mutex.  We enter here if we hadn't already
+		 * decremented-and-signaled port_curr.
+		 */
+		mutex_enter(&portq->portq_mutex);
 		if (--pp->port_curr < pp->port_max_events)
 			cv_signal(&pp->port_cv);
 	}
-	wakeup = (portq->portq_flags & PORTQ_POLLOUT);
+	ASSERT(MUTEX_HELD(&portq->portq_mutex));
+
+	/*
+	 * Don't send the POLLOUT if we're mid-close.  We can only send it if
+	 * we've dropped the mutex, and if we're closing, dropping the mutex
+	 * could lead to another thread freeing pp->port_pollhd and the rest
+	 * of *pp.
+	 */
+	wakeup = ((portq->portq_flags & (PORTQ_POLLOUT | PORTQ_CLOSE)) ==
+	    PORTQ_POLLOUT);
 	portq->portq_flags &= ~PORTQ_POLLOUT;
 	mutex_exit(&portq->portq_mutex);
 
@@ -650,7 +716,7 @@
 		    &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE,
 		    pkevp);
 	}
-	port_free_event_local(pkevp, 0);
+	port_free_event_local(pkevp, B_TRUE);
 
 	/* remove polldat struct */
 	port_pcache_remove_fd(pcp, pfd);
@@ -711,6 +777,17 @@
 	pp = VTOEP(fp->f_vnode);
 
 	mutex_enter(&pp->port_queue.portq_source_mutex);
+	if (pp->port_queue.portq_scache == NULL) {
+		/*
+		 * This event-port is mid-close.
+		 * By the time portq_scache is freed, PORTQ_CLOSE was set.
+		 */
+		ASSERT(pp->port_queue.portq_flags & PORTQ_CLOSE);
+		mutex_exit(&pp->port_queue.portq_source_mutex);
+		releasef(port);
+		/* Equivalent of a bad file descriptor at this point. */
+		return (EBADFD);
+	}
 	ps = &pp->port_queue.portq_scache[PORT_SHASH(source)];
 	for (pse = *ps; pse != NULL; pse = pse->portsrc_next) {
 		if (pse->portsrc_source == source)
--- old/usr/src/uts/common/sys/port_kernel.h	Wed Apr 29 19:58:35 2020
+++ new/usr/src/uts/common/sys/port_kernel.h	Wed Apr 29 19:58:35 2020
@@ -24,11 +24,13 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright 2020 Joyent, Inc.
+ */
+
 #ifndef	_SYS_PORT_KERNEL_H
 #define	_SYS_PORT_KERNEL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/vnode.h>
 #include <sys/list.h>
 
@@ -52,7 +54,7 @@
 typedef	struct	port_kevent {
 	kmutex_t	portkev_lock;	/* used by PORT_SOURCE_FD source */
 	int	portkev_source;		/* event: source */
-	int	portkev_events; 	/* event: data */
+	int	portkev_events;		/* event: data */
 	int	portkev_flags;		/* internal flags */
 	pid_t	portkev_pid;		/* pid of process using this struct */
 	long	portkev_object;		/* event: object */
@@ -168,7 +170,7 @@
 int	port_dissociate_fop(struct port *, uintptr_t);
 
 /* misc functions */
-void	port_free_event_local(port_kevent_t *, int counter);
+void	port_free_event_local(port_kevent_t *, boolean_t);
 int	port_alloc_event_local(struct port *, int, int, port_kevent_t **);
 void	port_close_pfd(struct portfd *);
 
