xref: /illumos-gate/usr/src/uts/common/fs/portfs/port_fd.c (revision 2c76d751)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
59642cd39Spraks  * Common Development and Distribution License (the "License").
69642cd39Spraks  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
2134709573Sraf 
227c478bd9Sstevel@tonic-gate /*
237c88c50eSPramod Batni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*2c76d751SPatrick Mooney  * Copyright 2022 Oxide Computer Company
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/systm.h>
317c478bd9Sstevel@tonic-gate #include <sys/stat.h>
327c478bd9Sstevel@tonic-gate #include <sys/errno.h>
337c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
347c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
357c478bd9Sstevel@tonic-gate #include <sys/debug.h>
367c478bd9Sstevel@tonic-gate #include <sys/poll_impl.h>
377c478bd9Sstevel@tonic-gate #include <sys/port_impl.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #define	PORTHASH_START	256	/* start cache space for events */
407c478bd9Sstevel@tonic-gate #define	PORTHASH_MULT	2	/* growth threshold and factor */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /* local functions */
437c478bd9Sstevel@tonic-gate static int	port_fd_callback(void *, int *, pid_t, int, void *);
447c478bd9Sstevel@tonic-gate static int	port_bind_pollhead(pollhead_t **, polldat_t *, short *);
457c478bd9Sstevel@tonic-gate static void	port_close_sourcefd(void *, int, pid_t, int);
467c478bd9Sstevel@tonic-gate static void	port_cache_insert_fd(port_fdcache_t *, polldat_t *);
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate /*
497c478bd9Sstevel@tonic-gate  * port_fd_callback()
507c478bd9Sstevel@tonic-gate  * The event port framework uses callback functions to notify associated
517c478bd9Sstevel@tonic-gate  * event sources about actions on source specific objects.
527c478bd9Sstevel@tonic-gate  * The source itself defines the "arg" required to identify the object with
537c478bd9Sstevel@tonic-gate  * events. In the port_fd_callback() case the "arg" is a pointer to portfd_t
547c478bd9Sstevel@tonic-gate  * structure. The portfd_t structure is specific for PORT_SOURCE_FD source.
557c478bd9Sstevel@tonic-gate  * The port_fd_callback() function is notified in three cases:
567c478bd9Sstevel@tonic-gate  * - PORT_CALLBACK_DEFAULT
577c478bd9Sstevel@tonic-gate  *	The object (fd) will be delivered to the application.
587c478bd9Sstevel@tonic-gate  * - PORT_CALLBACK_DISSOCIATE
597c478bd9Sstevel@tonic-gate  *	The object (fd) will be dissociated from  the port.
607c478bd9Sstevel@tonic-gate  * - PORT_CALLBACK_CLOSE
617c478bd9Sstevel@tonic-gate  *	The object (fd) will be dissociated from the port because the port
627c478bd9Sstevel@tonic-gate  *	is being closed.
637c478bd9Sstevel@tonic-gate  * A fd is shareable between processes only when
647c478bd9Sstevel@tonic-gate  * - processes have the same fd id and
657c478bd9Sstevel@tonic-gate  * - processes have the same fp.
667c478bd9Sstevel@tonic-gate  * A fd becomes shareable:
677c478bd9Sstevel@tonic-gate  * - on fork() across parent and child process and
687c478bd9Sstevel@tonic-gate  * - when I_SENDFD is used to pass file descriptors between parent and child
697c478bd9Sstevel@tonic-gate  *   immediately after fork() (the sender and receiver must get the same
707c478bd9Sstevel@tonic-gate  *   file descriptor id).
717c478bd9Sstevel@tonic-gate  * If a fd is shared between processes, all involved processes will get
727c478bd9Sstevel@tonic-gate  * the same rights related to re-association of the fd with the port and
737c478bd9Sstevel@tonic-gate  * retrieve of events from that fd.
747c478bd9Sstevel@tonic-gate  * The process which associated the fd with a port for the first time
757c478bd9Sstevel@tonic-gate  * becomes also the owner of the association. Only the owner of the
767c478bd9Sstevel@tonic-gate  * association is allowed to dissociate the fd from the port.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate /* ARGSUSED */
797c478bd9Sstevel@tonic-gate static int
port_fd_callback(void * arg,int * events,pid_t pid,int flag,void * evp)807c478bd9Sstevel@tonic-gate port_fd_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
817c478bd9Sstevel@tonic-gate {
827c478bd9Sstevel@tonic-gate 	portfd_t	*pfd = (portfd_t *)arg;
837c478bd9Sstevel@tonic-gate 	polldat_t	*pdp = PFTOD(pfd);
847c478bd9Sstevel@tonic-gate 	port_fdcache_t	*pcp;
857c478bd9Sstevel@tonic-gate 	file_t		*fp;
867c478bd9Sstevel@tonic-gate 	int		error;
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	ASSERT((pdp != NULL) && (events != NULL));
897c478bd9Sstevel@tonic-gate 	switch (flag) {
907c478bd9Sstevel@tonic-gate 	case PORT_CALLBACK_DEFAULT:
917c478bd9Sstevel@tonic-gate 		if (curproc->p_pid != pid) {
927c478bd9Sstevel@tonic-gate 			/*
937c478bd9Sstevel@tonic-gate 			 * Check if current process is allowed to retrieve
947c478bd9Sstevel@tonic-gate 			 * events from this fd.
957c478bd9Sstevel@tonic-gate 			 */
967c478bd9Sstevel@tonic-gate 			fp = getf(pdp->pd_fd);
977c478bd9Sstevel@tonic-gate 			if (fp == NULL) {
987c478bd9Sstevel@tonic-gate 				error = EACCES; /* deny delivery of events */
997c478bd9Sstevel@tonic-gate 				break;
1007c478bd9Sstevel@tonic-gate 			}
1017c478bd9Sstevel@tonic-gate 			releasef(pdp->pd_fd);
1027c478bd9Sstevel@tonic-gate 			if (fp != pdp->pd_fp) {
1037c478bd9Sstevel@tonic-gate 				error = EACCES; /* deny delivery of events */
1047c478bd9Sstevel@tonic-gate 				break;
1057c478bd9Sstevel@tonic-gate 			}
1067c478bd9Sstevel@tonic-gate 		}
1077c478bd9Sstevel@tonic-gate 		*events = pdp->pd_portev->portkev_events; /* update events */
1087c478bd9Sstevel@tonic-gate 		error = 0;
1097c478bd9Sstevel@tonic-gate 		break;
1107c478bd9Sstevel@tonic-gate 	case PORT_CALLBACK_DISSOCIATE:
1117c478bd9Sstevel@tonic-gate 		error = 0;
1127c478bd9Sstevel@tonic-gate 		break;
1137c478bd9Sstevel@tonic-gate 	case PORT_CALLBACK_CLOSE:
1147c478bd9Sstevel@tonic-gate 		/* remove polldat/portfd struct */
1157c478bd9Sstevel@tonic-gate 		pdp->pd_portev = NULL;
1167c478bd9Sstevel@tonic-gate 		pcp = (port_fdcache_t *)pdp->pd_pcache;
1177c478bd9Sstevel@tonic-gate 		mutex_enter(&pcp->pc_lock);
1187c478bd9Sstevel@tonic-gate 		pdp->pd_fp = NULL;
1197c478bd9Sstevel@tonic-gate 		pdp->pd_events = 0;
120*2c76d751SPatrick Mooney 		polldat_disassociate(pdp);
1217c478bd9Sstevel@tonic-gate 		port_pcache_remove_fd(pcp, pfd);
1227c478bd9Sstevel@tonic-gate 		mutex_exit(&pcp->pc_lock);
1237c478bd9Sstevel@tonic-gate 		error = 0;
1247c478bd9Sstevel@tonic-gate 		break;
1257c478bd9Sstevel@tonic-gate 	default:
1267c478bd9Sstevel@tonic-gate 		error = EINVAL;
1277c478bd9Sstevel@tonic-gate 		break;
1287c478bd9Sstevel@tonic-gate 	}
1297c478bd9Sstevel@tonic-gate 	return (error);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate  * This routine returns a pointer to a cached poll fd entry, or NULL if it
1347c478bd9Sstevel@tonic-gate  * does not find it in the hash table.
1357c478bd9Sstevel@tonic-gate  * The fd is used as index.
1367c478bd9Sstevel@tonic-gate  * The fd and the fp are used to detect a valid entry.
1377c478bd9Sstevel@tonic-gate  * This function returns a pointer to a valid portfd_t structure only when
1387c478bd9Sstevel@tonic-gate  * the fd and the fp in the args match the entries in polldat_t.
1397c478bd9Sstevel@tonic-gate  */
1407c478bd9Sstevel@tonic-gate portfd_t *
port_cache_lookup_fp(port_fdcache_t * pcp,int fd,file_t * fp)1417c478bd9Sstevel@tonic-gate port_cache_lookup_fp(port_fdcache_t *pcp, int fd, file_t *fp)
1427c478bd9Sstevel@tonic-gate {
1437c478bd9Sstevel@tonic-gate 	polldat_t	*pdp;
1447c478bd9Sstevel@tonic-gate 	portfd_t	**bucket;
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
1477c478bd9Sstevel@tonic-gate 	bucket = PORT_FD_BUCKET(pcp, fd);
1487c478bd9Sstevel@tonic-gate 	pdp = PFTOD(*bucket);
1497c478bd9Sstevel@tonic-gate 	while (pdp != NULL) {
1507c478bd9Sstevel@tonic-gate 		if (pdp->pd_fd == fd && pdp->pd_fp == fp)
1517c478bd9Sstevel@tonic-gate 			break;
1527c478bd9Sstevel@tonic-gate 		pdp = pdp->pd_hashnext;
1537c478bd9Sstevel@tonic-gate 	}
1547c478bd9Sstevel@tonic-gate 	return (PDTOF(pdp));
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * port_associate_fd()
1597c478bd9Sstevel@tonic-gate  * This function associates new file descriptors with a port or
1607c478bd9Sstevel@tonic-gate  * reactivate already associated file descriptors.
1617c478bd9Sstevel@tonic-gate  * The reactivation also updates the events types to be checked and the
1627c478bd9Sstevel@tonic-gate  * attached user pointer.
1637c478bd9Sstevel@tonic-gate  * Per port a cache is used to store associated file descriptors.
1647c478bd9Sstevel@tonic-gate  * Internally the VOP_POLL interface is used to poll for existing events.
1657c478bd9Sstevel@tonic-gate  * The VOP_POLL interface can also deliver a pointer to a pollhead_t structure
1667c478bd9Sstevel@tonic-gate  * which is used to enqueue polldat_t structures with pending events.
1677c478bd9Sstevel@tonic-gate  * If VOP_POLL immediately returns valid events (revents) then those events
1687c478bd9Sstevel@tonic-gate  * will be submitted to the event port with port_send_event().
1697c478bd9Sstevel@tonic-gate  * Otherwise VOP_POLL does not return events but it delivers a pointer to a
1707c478bd9Sstevel@tonic-gate  * pollhead_t structure. In such a case the corresponding file system behind
171da6c28aaSamw  * VOP_POLL will use the pollwakeup() function to notify about existing
1727c478bd9Sstevel@tonic-gate  * events.
1737c478bd9Sstevel@tonic-gate  */
1747c478bd9Sstevel@tonic-gate int
port_associate_fd(port_t * pp,int source,uintptr_t object,int events,void * user)1757c478bd9Sstevel@tonic-gate port_associate_fd(port_t *pp, int source, uintptr_t object, int events,
1767c478bd9Sstevel@tonic-gate     void *user)
1777c478bd9Sstevel@tonic-gate {
1787c478bd9Sstevel@tonic-gate 	port_fdcache_t	*pcp;
1797c478bd9Sstevel@tonic-gate 	int		fd;
1807c478bd9Sstevel@tonic-gate 	struct pollhead	*php = NULL;
1817c478bd9Sstevel@tonic-gate 	portfd_t	*pfd;
1827c478bd9Sstevel@tonic-gate 	polldat_t	*pdp;
1837c478bd9Sstevel@tonic-gate 	file_t		*fp;
1847c478bd9Sstevel@tonic-gate 	port_kevent_t	*pkevp;
1857c478bd9Sstevel@tonic-gate 	short		revents;
1867c478bd9Sstevel@tonic-gate 	int		error = 0;
1879f23d599Spraks 	int		active;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	pcp = pp->port_queue.portq_pcp;
1907c478bd9Sstevel@tonic-gate 	if (object > (uintptr_t)INT_MAX)
1917c478bd9Sstevel@tonic-gate 		return (EBADFD);
1927c478bd9Sstevel@tonic-gate 
1937c478bd9Sstevel@tonic-gate 	fd = object;
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	if ((fp = getf(fd)) == NULL)
1967c478bd9Sstevel@tonic-gate 		return (EBADFD);
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	mutex_enter(&pcp->pc_lock);
1997c88c50eSPramod Batni 
2007c478bd9Sstevel@tonic-gate 	if (pcp->pc_hash == NULL) {
2017c478bd9Sstevel@tonic-gate 		/*
2027c478bd9Sstevel@tonic-gate 		 * This is the first time that a fd is being associated with
2037c478bd9Sstevel@tonic-gate 		 * the current port:
2047c478bd9Sstevel@tonic-gate 		 * - create PORT_SOURCE_FD cache
2057c478bd9Sstevel@tonic-gate 		 * - associate PORT_SOURCE_FD source with the port
2067c478bd9Sstevel@tonic-gate 		 */
2077c478bd9Sstevel@tonic-gate 		error = port_associate_ksource(pp->port_fd, PORT_SOURCE_FD,
2087c478bd9Sstevel@tonic-gate 		    NULL, port_close_sourcefd, pp, NULL);
2097c478bd9Sstevel@tonic-gate 		if (error) {
2107c478bd9Sstevel@tonic-gate 			mutex_exit(&pcp->pc_lock);
2117c478bd9Sstevel@tonic-gate 			releasef(fd);
2127c478bd9Sstevel@tonic-gate 			return (error);
2137c478bd9Sstevel@tonic-gate 		}
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 		/* create polldat cache */
2167c478bd9Sstevel@tonic-gate 		pcp->pc_hashsize = PORTHASH_START;
2177c478bd9Sstevel@tonic-gate 		pcp->pc_hash = kmem_zalloc(pcp->pc_hashsize *
2187c478bd9Sstevel@tonic-gate 		    sizeof (portfd_t *), KM_SLEEP);
2197c478bd9Sstevel@tonic-gate 		pfd = NULL;
2207c478bd9Sstevel@tonic-gate 	} else {
2217c478bd9Sstevel@tonic-gate 		/* Check if the fd/fp is already associated with the port */
2227c478bd9Sstevel@tonic-gate 		pfd = port_cache_lookup_fp(pcp, fd, fp);
2237c478bd9Sstevel@tonic-gate 	}
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	if (pfd == NULL) {
2267c478bd9Sstevel@tonic-gate 		/*
2277c478bd9Sstevel@tonic-gate 		 * new entry
2287c478bd9Sstevel@tonic-gate 		 * Allocate a polldat_t structure per fd
2297c478bd9Sstevel@tonic-gate 		 * The use of the polldat_t structure to cache file descriptors
2307c478bd9Sstevel@tonic-gate 		 * is required to be able to share the pollwakeup() function
231bbf21555SRichard Lowe 		 * with poll(2) and devpoll(4D).
2327c478bd9Sstevel@tonic-gate 		 */
2337c478bd9Sstevel@tonic-gate 		pfd = kmem_zalloc(sizeof (portfd_t), KM_SLEEP);
2347c478bd9Sstevel@tonic-gate 		pdp = PFTOD(pfd);
2357c478bd9Sstevel@tonic-gate 		pdp->pd_fd = fd;
2367c478bd9Sstevel@tonic-gate 		pdp->pd_fp = fp;
2377c478bd9Sstevel@tonic-gate 		pdp->pd_pcache = (void *)pcp;
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 		/* Allocate a port event structure per fd */
2407c478bd9Sstevel@tonic-gate 		error = port_alloc_event_local(pp, source, PORT_ALLOC_CACHED,
2417c478bd9Sstevel@tonic-gate 		    &pdp->pd_portev);
2427c478bd9Sstevel@tonic-gate 		if (error) {
2437c478bd9Sstevel@tonic-gate 			kmem_free(pfd, sizeof (portfd_t));
2447c478bd9Sstevel@tonic-gate 			releasef(fd);
2457c478bd9Sstevel@tonic-gate 			mutex_exit(&pcp->pc_lock);
2467c478bd9Sstevel@tonic-gate 			return (error);
2477c478bd9Sstevel@tonic-gate 		}
2487c478bd9Sstevel@tonic-gate 		pkevp = pdp->pd_portev;
2497c478bd9Sstevel@tonic-gate 		pkevp->portkev_callback = port_fd_callback;
2507c478bd9Sstevel@tonic-gate 		pkevp->portkev_arg = pfd;
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		/* add portfd_t entry  to the cache */
2537c478bd9Sstevel@tonic-gate 		port_cache_insert_fd(pcp, pdp);
2547c478bd9Sstevel@tonic-gate 		pkevp->portkev_object = fd;
2557c478bd9Sstevel@tonic-gate 		pkevp->portkev_user = user;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 		/*
2587c478bd9Sstevel@tonic-gate 		 * Add current port to the file descriptor interested list
2597c478bd9Sstevel@tonic-gate 		 * The members of the list are notified when the file descriptor
2607c478bd9Sstevel@tonic-gate 		 * is closed.
2617c478bd9Sstevel@tonic-gate 		 */
2627c478bd9Sstevel@tonic-gate 		addfd_port(fd, pfd);
2637c478bd9Sstevel@tonic-gate 	} else {
2647c478bd9Sstevel@tonic-gate 		/*
2657c478bd9Sstevel@tonic-gate 		 * The file descriptor is already associated with the port
2667c478bd9Sstevel@tonic-gate 		 */
2677c478bd9Sstevel@tonic-gate 		pdp = PFTOD(pfd);
2687c478bd9Sstevel@tonic-gate 		pkevp = pdp->pd_portev;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 		/*
2717c478bd9Sstevel@tonic-gate 		 * Check if the re-association happens before the last
2727c478bd9Sstevel@tonic-gate 		 * submitted event of the file descriptor was retrieved.
27361b4b1efSpraks 		 * Clear the PORT_KEV_VALID flag if set. No new events
27461b4b1efSpraks 		 * should get submitted after this flag is cleared.
2757c478bd9Sstevel@tonic-gate 		 */
27661b4b1efSpraks 		mutex_enter(&pkevp->portkev_lock);
27761b4b1efSpraks 		if (pkevp->portkev_flags & PORT_KEV_VALID) {
27861b4b1efSpraks 			pkevp->portkev_flags &= ~PORT_KEV_VALID;
27961b4b1efSpraks 		}
2807c478bd9Sstevel@tonic-gate 		if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
28161b4b1efSpraks 			mutex_exit(&pkevp->portkev_lock);
2827c478bd9Sstevel@tonic-gate 			/*
28361b4b1efSpraks 			 * Remove any events that where already fired
28461b4b1efSpraks 			 * for this fd and are still in the port queue.
2857c478bd9Sstevel@tonic-gate 			 */
286df2381bfSpraks 			(void) port_remove_done_event(pkevp);
28761b4b1efSpraks 		} else {
28861b4b1efSpraks 			mutex_exit(&pkevp->portkev_lock);
2897c478bd9Sstevel@tonic-gate 		}
2907c478bd9Sstevel@tonic-gate 		pkevp->portkev_user = user;
2917c478bd9Sstevel@tonic-gate 	}
2927c478bd9Sstevel@tonic-gate 
2937c88c50eSPramod Batni 	pfd->pfd_thread = curthread;
2949642cd39Spraks 	mutex_enter(&pkevp->portkev_lock);
2957c478bd9Sstevel@tonic-gate 	pkevp->portkev_events = 0;	/* no fired events */
2967c478bd9Sstevel@tonic-gate 	pdp->pd_events = events;	/* events associated */
2979642cd39Spraks 	/*
2989642cd39Spraks 	 * allow new events.
2999642cd39Spraks 	 */
3009642cd39Spraks 	pkevp->portkev_flags |= PORT_KEV_VALID;
3019642cd39Spraks 	mutex_exit(&pkevp->portkev_lock);
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	/*
3047c478bd9Sstevel@tonic-gate 	 * do VOP_POLL and cache this poll fd.
3057c478bd9Sstevel@tonic-gate 	 *
3067c478bd9Sstevel@tonic-gate 	 * XXX - pollrelock() logic needs to know
3077c478bd9Sstevel@tonic-gate 	 * which pollcache lock to grab. It'd be a
3087c478bd9Sstevel@tonic-gate 	 * cleaner solution if we could pass pcp as
3097c478bd9Sstevel@tonic-gate 	 * an arguement in VOP_POLL interface instead
3107c478bd9Sstevel@tonic-gate 	 * of implicitly passing it using thread_t
3117c478bd9Sstevel@tonic-gate 	 * struct. On the other hand, changing VOP_POLL
3127c478bd9Sstevel@tonic-gate 	 * interface will require all driver/file system
3137c478bd9Sstevel@tonic-gate 	 * poll routine to change.
3147c478bd9Sstevel@tonic-gate 	 */
3157c478bd9Sstevel@tonic-gate 	curthread->t_pollcache = (pollcache_t *)pcp;
316da6c28aaSamw 	error = VOP_POLL(fp->f_vnode, events, 0, &revents, &php, NULL);
3177c478bd9Sstevel@tonic-gate 	curthread->t_pollcache = NULL;
3187c478bd9Sstevel@tonic-gate 
3197c88c50eSPramod Batni 	/*
3207c88c50eSPramod Batni 	 * The pc_lock can get dropped and reaquired in VOP_POLL.
3217c88c50eSPramod Batni 	 * In the window pc_lock is dropped another thread in
3227c88c50eSPramod Batni 	 * port_dissociate can remove the pfd from the port cache
3237c88c50eSPramod Batni 	 * and free the pfd.
3247c88c50eSPramod Batni 	 * It is also possible for another thread to sneak in and do a
3257c88c50eSPramod Batni 	 * port_associate on the same fd during the same window.
3267c88c50eSPramod Batni 	 * For both these cases return the current value of error.
3277c88c50eSPramod Batni 	 * The application should take care to ensure that the threads
3287c88c50eSPramod Batni 	 * do not race with each other for association and disassociation
3297c88c50eSPramod Batni 	 * of the same fd.
3307c88c50eSPramod Batni 	 */
3317c88c50eSPramod Batni 	if (((pfd = port_cache_lookup_fp(pcp, fd, fp)) == NULL) ||
3327c88c50eSPramod Batni 	    (pfd->pfd_thread != curthread)) {
3337c88c50eSPramod Batni 		releasef(fd);
3347c88c50eSPramod Batni 		mutex_exit(&pcp->pc_lock);
3357c88c50eSPramod Batni 		return (error);
3367c88c50eSPramod Batni 	}
3377c88c50eSPramod Batni 
3387c478bd9Sstevel@tonic-gate 	/*
3397c478bd9Sstevel@tonic-gate 	 * To keep synchronization between VOP_POLL above and
340*2c76d751SPatrick Mooney 	 * polldat_associate() below, it is necessary to
3417c478bd9Sstevel@tonic-gate 	 * call VOP_POLL() again (see port_bind_pollhead()).
3427c478bd9Sstevel@tonic-gate 	 */
3437c478bd9Sstevel@tonic-gate 	if (error) {
3449f23d599Spraks 		goto errout;
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 
3477c88c50eSPramod Batni 	if (php != NULL && (pdp->pd_php != php)) {
3487c478bd9Sstevel@tonic-gate 		/*
3497c478bd9Sstevel@tonic-gate 		 * No events delivered yet.
3507c478bd9Sstevel@tonic-gate 		 * Bind pollhead pointer with current polldat_t structure.
3517c478bd9Sstevel@tonic-gate 		 * Sub-system will call pollwakeup() later with php as
3527c478bd9Sstevel@tonic-gate 		 * argument.
3537c478bd9Sstevel@tonic-gate 		 */
3547c478bd9Sstevel@tonic-gate 		error = port_bind_pollhead(&php, pdp, &revents);
3557c88c50eSPramod Batni 		/*
3567c88c50eSPramod Batni 		 * The pc_lock can get dropped and reaquired in VOP_POLL.
3577c88c50eSPramod Batni 		 * In the window pc_lock is dropped another thread in
3587c88c50eSPramod Batni 		 * port_dissociate can remove the pfd from the port cache
3597c88c50eSPramod Batni 		 * and free the pfd.
3607c88c50eSPramod Batni 		 * It is also possible for another thread to sneak in and do a
3617c88c50eSPramod Batni 		 * port_associate on the same fd during the same window.
3627c88c50eSPramod Batni 		 * For both these cases return the current value of error.
3637c88c50eSPramod Batni 		 * The application should take care to ensure that the threads
3647c88c50eSPramod Batni 		 * do not race with each other for association
3657c88c50eSPramod Batni 		 * and disassociation of the same fd.
3667c88c50eSPramod Batni 		 */
3677c88c50eSPramod Batni 		if (((pfd = port_cache_lookup_fp(pcp, fd, fp)) == NULL) ||
3687c88c50eSPramod Batni 		    (pfd->pfd_thread != curthread)) {
3697c88c50eSPramod Batni 			releasef(fd);
3707c88c50eSPramod Batni 			mutex_exit(&pcp->pc_lock);
3717c88c50eSPramod Batni 			return (error);
3727c88c50eSPramod Batni 		}
3737c88c50eSPramod Batni 
3747c478bd9Sstevel@tonic-gate 		if (error) {
3759f23d599Spraks 			goto errout;
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 	}
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	/*
3809642cd39Spraks 	 * Check if new events where detected and no events have been
3819642cd39Spraks 	 * delivered. The revents was already set after the VOP_POLL
3829642cd39Spraks 	 * above or it was updated in port_bind_pollhead().
3837c478bd9Sstevel@tonic-gate 	 */
38461b4b1efSpraks 	mutex_enter(&pkevp->portkev_lock);
3859642cd39Spraks 	if (revents && (pkevp->portkev_flags & PORT_KEV_VALID)) {
38661b4b1efSpraks 		ASSERT((pkevp->portkev_flags & PORT_KEV_DONEQ) == 0);
3879642cd39Spraks 		pkevp->portkev_flags &= ~PORT_KEV_VALID;
3887c478bd9Sstevel@tonic-gate 		revents = revents & (pdp->pd_events | POLLHUP | POLLERR);
38961b4b1efSpraks 		/* send events to the event port */
39061b4b1efSpraks 		pkevp->portkev_events = revents;
39161b4b1efSpraks 		/*
39261b4b1efSpraks 		 * port_send_event will release the portkev_lock mutex.
39361b4b1efSpraks 		 */
39434709573Sraf 		port_send_event(pkevp);
39561b4b1efSpraks 	} else {
39661b4b1efSpraks 		mutex_exit(&pkevp->portkev_lock);
3977c478bd9Sstevel@tonic-gate 	}
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	releasef(fd);
4007c478bd9Sstevel@tonic-gate 	mutex_exit(&pcp->pc_lock);
4017c478bd9Sstevel@tonic-gate 	return (error);
4029f23d599Spraks 
4039f23d599Spraks errout:
4049f23d599Spraks 	delfd_port(fd, pfd);
4059f23d599Spraks 	/*
4069f23d599Spraks 	 * If the portkev is not valid, then an event was
4079f23d599Spraks 	 * delivered.
4089f23d599Spraks 	 *
4099f23d599Spraks 	 * If an event was delivered and got picked up, then
4109f23d599Spraks 	 * we return error = 0 treating this as a successful
4119f23d599Spraks 	 * port associate call. The thread which received
4129f23d599Spraks 	 * the event gets control of the object.
4139f23d599Spraks 	 */
4149f23d599Spraks 	active = 0;
4159f23d599Spraks 	mutex_enter(&pkevp->portkev_lock);
4169f23d599Spraks 	if (pkevp->portkev_flags & PORT_KEV_VALID) {
4179f23d599Spraks 		pkevp->portkev_flags &= ~PORT_KEV_VALID;
4189f23d599Spraks 		active = 1;
4199f23d599Spraks 	}
4208b4000f9Spraks 	mutex_exit(&pkevp->portkev_lock);
4219f23d599Spraks 
4229f23d599Spraks 	if (!port_remove_fd_object(pfd, pp, pcp) && !active) {
4239f23d599Spraks 		error = 0;
4249f23d599Spraks 	}
4259f23d599Spraks 	releasef(fd);
4269f23d599Spraks 	mutex_exit(&pcp->pc_lock);
4279f23d599Spraks 	return (error);
4287c478bd9Sstevel@tonic-gate }
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate /*
4317c478bd9Sstevel@tonic-gate  * The port_dissociate_fd() function dissociates the delivered file
4327c478bd9Sstevel@tonic-gate  * descriptor from the event port and removes already fired events.
4337c478bd9Sstevel@tonic-gate  * If a fd is shared between processes, all involved processes will get
4347c478bd9Sstevel@tonic-gate  * the same rights related to re-association of the fd with the port and
4357c478bd9Sstevel@tonic-gate  * retrieve of events from that fd.
4367c478bd9Sstevel@tonic-gate  * The process which associated the fd with a port for the first time
4377c478bd9Sstevel@tonic-gate  * becomes also the owner of the association. Only the owner of the
4387c478bd9Sstevel@tonic-gate  * association is allowed to dissociate the fd from the port.
4397c478bd9Sstevel@tonic-gate  */
4407c478bd9Sstevel@tonic-gate int
port_dissociate_fd(port_t * pp,uintptr_t object)4417c478bd9Sstevel@tonic-gate port_dissociate_fd(port_t *pp, uintptr_t object)
4427c478bd9Sstevel@tonic-gate {
4437c478bd9Sstevel@tonic-gate 	int		fd;
4447c478bd9Sstevel@tonic-gate 	port_fdcache_t	*pcp;
4457c478bd9Sstevel@tonic-gate 	portfd_t	*pfd;
4467c478bd9Sstevel@tonic-gate 	file_t		*fp;
4479f23d599Spraks 	int		active;
4489f23d599Spraks 	port_kevent_t	*pkevp;
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	if (object > (uintptr_t)INT_MAX)
4517c478bd9Sstevel@tonic-gate 		return (EBADFD);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	fd = object;
4547c478bd9Sstevel@tonic-gate 	pcp = pp->port_queue.portq_pcp;
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	mutex_enter(&pcp->pc_lock);
4577c478bd9Sstevel@tonic-gate 	if (pcp->pc_hash == NULL) {
4587c478bd9Sstevel@tonic-gate 		/* no file descriptor cache available */
4597c478bd9Sstevel@tonic-gate 		mutex_exit(&pcp->pc_lock);
4609f23d599Spraks 		return (ENOENT);
4617c478bd9Sstevel@tonic-gate 	}
4627c478bd9Sstevel@tonic-gate 	if ((fp = getf(fd)) == NULL) {
4637c478bd9Sstevel@tonic-gate 		mutex_exit(&pcp->pc_lock);
4647c478bd9Sstevel@tonic-gate 		return (EBADFD);
4657c478bd9Sstevel@tonic-gate 	}
4667c478bd9Sstevel@tonic-gate 	pfd = port_cache_lookup_fp(pcp, fd, fp);
4677c478bd9Sstevel@tonic-gate 	if (pfd == NULL) {
4687c478bd9Sstevel@tonic-gate 		releasef(fd);
4697c478bd9Sstevel@tonic-gate 		mutex_exit(&pcp->pc_lock);
4709f23d599Spraks 		return (ENOENT);
4717c478bd9Sstevel@tonic-gate 	}
4727c478bd9Sstevel@tonic-gate 	/* only association owner is allowed to remove the association */
4737c478bd9Sstevel@tonic-gate 	if (curproc->p_pid != PFTOD(pfd)->pd_portev->portkev_pid) {
4747c478bd9Sstevel@tonic-gate 		releasef(fd);
4757c478bd9Sstevel@tonic-gate 		mutex_exit(&pcp->pc_lock);
4767c478bd9Sstevel@tonic-gate 		return (EACCES);
4777c478bd9Sstevel@tonic-gate 	}
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	/* remove port from the file descriptor interested list */
4807c478bd9Sstevel@tonic-gate 	delfd_port(fd, pfd);
4817c478bd9Sstevel@tonic-gate 
4829f23d599Spraks 	/*
4839f23d599Spraks 	 * Deactivate the association. No events get posted after
4849f23d599Spraks 	 * this.
4859f23d599Spraks 	 */
4869f23d599Spraks 	pkevp = PFTOD(pfd)->pd_portev;
4879f23d599Spraks 	mutex_enter(&pkevp->portkev_lock);
4889f23d599Spraks 	if (pkevp->portkev_flags & PORT_KEV_VALID) {
4899f23d599Spraks 		pkevp->portkev_flags &= ~PORT_KEV_VALID;
4909f23d599Spraks 		active = 1;
4919f23d599Spraks 	} else {
4929f23d599Spraks 		active = 0;
4939f23d599Spraks 	}
4949f23d599Spraks 	mutex_exit(&pkevp->portkev_lock);
4959f23d599Spraks 
4967c478bd9Sstevel@tonic-gate 	/* remove polldat & port event structure */
4979f23d599Spraks 	if (port_remove_fd_object(pfd, pp, pcp)) {
4989f23d599Spraks 		/*
4999f23d599Spraks 		 * An event was found and removed from the
5009f23d599Spraks 		 * port done queue. This means the event has not yet
5019f23d599Spraks 		 * been retrived. In this case we treat this as an active
5029f23d599Spraks 		 * association.
5039f23d599Spraks 		 */
5049f23d599Spraks 		ASSERT(active == 0);
5059f23d599Spraks 		active = 1;
5069f23d599Spraks 	}
5077c88c50eSPramod Batni 	releasef(fd);
5087c478bd9Sstevel@tonic-gate 	mutex_exit(&pcp->pc_lock);
5097c478bd9Sstevel@tonic-gate 
5109f23d599Spraks 	/*
5119f23d599Spraks 	 * Return ENOENT if there was no active association.
5129f23d599Spraks 	 */
5139f23d599Spraks 	return ((active ? 0 : ENOENT));
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate /*
5177c478bd9Sstevel@tonic-gate  * Associate event port polldat_t structure with sub-system pointer to
5187c478bd9Sstevel@tonic-gate  * a polhead_t structure.
5197c478bd9Sstevel@tonic-gate  */
5207c478bd9Sstevel@tonic-gate static int
port_bind_pollhead(pollhead_t ** php,polldat_t * pdp,short * revents)5217c478bd9Sstevel@tonic-gate port_bind_pollhead(pollhead_t **php, polldat_t *pdp, short *revents)
5227c478bd9Sstevel@tonic-gate {
5237c478bd9Sstevel@tonic-gate 	int		error;
5247c478bd9Sstevel@tonic-gate 	file_t		*fp;
5257c478bd9Sstevel@tonic-gate 
526*2c76d751SPatrick Mooney 	/* break any existing association with pollhead */
527*2c76d751SPatrick Mooney 	polldat_disassociate(pdp);
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	/*
530*2c76d751SPatrick Mooney 	 * Before polldat_associate(), pollwakeup() will not detect a polldat
5317c478bd9Sstevel@tonic-gate 	 * entry in the ph_list and the event notification will disappear.
5327c478bd9Sstevel@tonic-gate 	 * This happens because polldat_t is still not associated with
5337c478bd9Sstevel@tonic-gate 	 * the pointer to the pollhead_t structure.
5347c478bd9Sstevel@tonic-gate 	 */
535*2c76d751SPatrick Mooney 	polldat_associate(pdp, *php);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	/*
5387c478bd9Sstevel@tonic-gate 	 * From now on event notification can be detected in pollwakeup(),
5397c478bd9Sstevel@tonic-gate 	 * Use VOP_POLL() again to check the current status of the event.
5407c478bd9Sstevel@tonic-gate 	 */
5417c478bd9Sstevel@tonic-gate 	fp = pdp->pd_fp;
5427c478bd9Sstevel@tonic-gate 	curthread->t_pollcache = (pollcache_t *)pdp->pd_pcache;
543da6c28aaSamw 	error = VOP_POLL(fp->f_vnode, pdp->pd_events, 0, revents, php, NULL);
5447c478bd9Sstevel@tonic-gate 	curthread->t_pollcache = NULL;
5457c478bd9Sstevel@tonic-gate 	return (error);
5467c478bd9Sstevel@tonic-gate }
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate /*
5497c478bd9Sstevel@tonic-gate  * Grow the hash table. Rehash all the elements on the hash table.
5507c478bd9Sstevel@tonic-gate  */
5517c478bd9Sstevel@tonic-gate static void
port_cache_grow_hashtbl(port_fdcache_t * pcp)5527c478bd9Sstevel@tonic-gate port_cache_grow_hashtbl(port_fdcache_t *pcp)
5537c478bd9Sstevel@tonic-gate {
5547c478bd9Sstevel@tonic-gate 	portfd_t	**oldtbl;
5557c478bd9Sstevel@tonic-gate 	polldat_t	*pdp;
5567c478bd9Sstevel@tonic-gate 	portfd_t	*pfd;
5577c478bd9Sstevel@tonic-gate 	polldat_t	*pdp1;
5587c478bd9Sstevel@tonic-gate 	int		oldsize;
5597c478bd9Sstevel@tonic-gate 	int		i;
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
5627c478bd9Sstevel@tonic-gate 	oldsize = pcp->pc_hashsize;
5637c478bd9Sstevel@tonic-gate 	oldtbl = pcp->pc_hash;
5647c478bd9Sstevel@tonic-gate 	pcp->pc_hashsize *= PORTHASH_MULT;
5657c478bd9Sstevel@tonic-gate 	pcp->pc_hash = kmem_zalloc(pcp->pc_hashsize * sizeof (portfd_t *),
5667c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
5677c478bd9Sstevel@tonic-gate 	/*
5687c478bd9Sstevel@tonic-gate 	 * rehash existing elements
5697c478bd9Sstevel@tonic-gate 	 */
5707c478bd9Sstevel@tonic-gate 	pcp->pc_fdcount = 0;
5717c478bd9Sstevel@tonic-gate 	for (i = 0; i < oldsize; i++) {
5727c478bd9Sstevel@tonic-gate 		pfd = oldtbl[i];
5737c478bd9Sstevel@tonic-gate 		pdp = PFTOD(pfd);
5747c478bd9Sstevel@tonic-gate 		while (pdp != NULL) {
5757c478bd9Sstevel@tonic-gate 			pdp1 = pdp->pd_hashnext;
5767c478bd9Sstevel@tonic-gate 			port_cache_insert_fd(pcp, pdp);
5777c478bd9Sstevel@tonic-gate 			pdp = pdp1;
5787c478bd9Sstevel@tonic-gate 		}
5797c478bd9Sstevel@tonic-gate 	}
5807c478bd9Sstevel@tonic-gate 	kmem_free(oldtbl, oldsize * sizeof (portfd_t *));
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate /*
5837c478bd9Sstevel@tonic-gate  * This routine inserts a polldat into the portcache's hash table. It
5847c478bd9Sstevel@tonic-gate  * may be necessary to grow the size of the hash table.
5857c478bd9Sstevel@tonic-gate  */
5867c478bd9Sstevel@tonic-gate static void
port_cache_insert_fd(port_fdcache_t * pcp,polldat_t * pdp)5877c478bd9Sstevel@tonic-gate port_cache_insert_fd(port_fdcache_t *pcp, polldat_t *pdp)
5887c478bd9Sstevel@tonic-gate {
5897c478bd9Sstevel@tonic-gate 	portfd_t	**bucket;
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
5927c478bd9Sstevel@tonic-gate 	if (pcp->pc_fdcount > (pcp->pc_hashsize * PORTHASH_MULT))
5937c478bd9Sstevel@tonic-gate 		port_cache_grow_hashtbl(pcp);
5947c478bd9Sstevel@tonic-gate 	bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
5957c478bd9Sstevel@tonic-gate 	pdp->pd_hashnext = PFTOD(*bucket);
5967c478bd9Sstevel@tonic-gate 	*bucket = PDTOF(pdp);
5977c478bd9Sstevel@tonic-gate 	pcp->pc_fdcount++;
5987c478bd9Sstevel@tonic-gate }
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate /*
6027c478bd9Sstevel@tonic-gate  * The port_remove_portfd() function dissociates the port from the fd
6037c478bd9Sstevel@tonic-gate  * and vive versa.
6047c478bd9Sstevel@tonic-gate  */
6057c478bd9Sstevel@tonic-gate static void
port_remove_portfd(polldat_t * pdp,port_fdcache_t * pcp)6067c478bd9Sstevel@tonic-gate port_remove_portfd(polldat_t *pdp, port_fdcache_t *pcp)
6077c478bd9Sstevel@tonic-gate {
6087c478bd9Sstevel@tonic-gate 	port_t	*pp;
6097c478bd9Sstevel@tonic-gate 	file_t	*fp;
6107c88c50eSPramod Batni 	int	fd;
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
6137c478bd9Sstevel@tonic-gate 	pp = pdp->pd_portev->portkev_port;
6147c88c50eSPramod Batni 	fp = getf(fd = pdp->pd_fd);
61561b4b1efSpraks 	/*
61661b4b1efSpraks 	 * If we did not get the fp for pd_fd but its portfd_t
61761b4b1efSpraks 	 * still exist in the cache, it means the pd_fd is being
61861b4b1efSpraks 	 * closed by some other thread which will also free the portfd_t.
61961b4b1efSpraks 	 */
62061b4b1efSpraks 	if (fp != NULL) {
62161b4b1efSpraks 		delfd_port(pdp->pd_fd, PDTOF(pdp));
6229f23d599Spraks 		(void) port_remove_fd_object(PDTOF(pdp), pp, pcp);
6237c88c50eSPramod Batni 		releasef(fd);
62461b4b1efSpraks 	}
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate /*
6287c478bd9Sstevel@tonic-gate  * This function is used by port_close_sourcefd() to destroy the cache
6297c478bd9Sstevel@tonic-gate  * on last close.
6307c478bd9Sstevel@tonic-gate  */
6317c478bd9Sstevel@tonic-gate static void
port_pcache_destroy(port_fdcache_t * pcp)6327c478bd9Sstevel@tonic-gate port_pcache_destroy(port_fdcache_t *pcp)
6337c478bd9Sstevel@tonic-gate {
6347c478bd9Sstevel@tonic-gate 	ASSERT(pcp->pc_fdcount == 0);
6357c478bd9Sstevel@tonic-gate 	kmem_free(pcp->pc_hash, sizeof (polldat_t *) * pcp->pc_hashsize);
6367c478bd9Sstevel@tonic-gate 	mutex_destroy(&pcp->pc_lock);
6377c478bd9Sstevel@tonic-gate 	kmem_free(pcp, sizeof (port_fdcache_t));
6387c478bd9Sstevel@tonic-gate }
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate /*
6417c478bd9Sstevel@tonic-gate  * port_close() calls this function to request the PORT_SOURCE_FD source
6427c478bd9Sstevel@tonic-gate  * to remove/free all resources allocated and associated with the port.
6437c478bd9Sstevel@tonic-gate  */
6447c478bd9Sstevel@tonic-gate /* ARGSUSED */
6457c478bd9Sstevel@tonic-gate static void
port_close_sourcefd(void * arg,int port,pid_t pid,int lastclose)6467c478bd9Sstevel@tonic-gate port_close_sourcefd(void *arg, int port, pid_t pid, int lastclose)
6477c478bd9Sstevel@tonic-gate {
6487c478bd9Sstevel@tonic-gate 	port_t		*pp = arg;
6497c478bd9Sstevel@tonic-gate 	port_fdcache_t	*pcp;
6507c478bd9Sstevel@tonic-gate 	portfd_t	**hashtbl;
6517c478bd9Sstevel@tonic-gate 	polldat_t	*pdp;
6527c478bd9Sstevel@tonic-gate 	polldat_t	*pdpnext;
6537c478bd9Sstevel@tonic-gate 	int		index;
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	pcp = pp->port_queue.portq_pcp;
6567c478bd9Sstevel@tonic-gate 	if (pcp == NULL)
6577c478bd9Sstevel@tonic-gate 		/* no cache available -> nothing to do */
6587c478bd9Sstevel@tonic-gate 		return;
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	mutex_enter(&pcp->pc_lock);
6617c478bd9Sstevel@tonic-gate 	/*
6627c478bd9Sstevel@tonic-gate 	 * Scan the cache and free all allocated portfd_t and port_kevent_t
6637c478bd9Sstevel@tonic-gate 	 * structures.
6647c478bd9Sstevel@tonic-gate 	 */
6657c478bd9Sstevel@tonic-gate 	hashtbl = pcp->pc_hash;
6667c478bd9Sstevel@tonic-gate 	for (index = 0; index < pcp->pc_hashsize; index++) {
6677c478bd9Sstevel@tonic-gate 		for (pdp = PFTOD(hashtbl[index]); pdp != NULL; pdp = pdpnext) {
6687c478bd9Sstevel@tonic-gate 			pdpnext = pdp->pd_hashnext;
6697c478bd9Sstevel@tonic-gate 			if (pid == pdp->pd_portev->portkev_pid) {
6707c478bd9Sstevel@tonic-gate 				/*
6717c478bd9Sstevel@tonic-gate 				 * remove polldat + port_event_t from cache
6727c478bd9Sstevel@tonic-gate 				 * only when current process did the
6737c478bd9Sstevel@tonic-gate 				 * association.
6747c478bd9Sstevel@tonic-gate 				 */
6757c478bd9Sstevel@tonic-gate 				port_remove_portfd(pdp, pcp);
6767c478bd9Sstevel@tonic-gate 			}
6777c478bd9Sstevel@tonic-gate 		}
6787c478bd9Sstevel@tonic-gate 	}
67961b4b1efSpraks 	if (lastclose) {
68061b4b1efSpraks 		/*
68161b4b1efSpraks 		 * Wait for all the portfd's to be freed.
68261b4b1efSpraks 		 * The remaining portfd_t's are the once we did not
68361b4b1efSpraks 		 * free in port_remove_portfd since some other thread
68461b4b1efSpraks 		 * is closing the fd. These threads will free the portfd_t's
68561b4b1efSpraks 		 * once we drop the pc_lock mutex.
68661b4b1efSpraks 		 */
68761b4b1efSpraks 		while (pcp->pc_fdcount) {
68861b4b1efSpraks 			(void) cv_wait_sig(&pcp->pc_lclosecv, &pcp->pc_lock);
68961b4b1efSpraks 		}
69061b4b1efSpraks 		/* event port vnode will be destroyed -> remove everything */
69161b4b1efSpraks 		pp->port_queue.portq_pcp = NULL;
69261b4b1efSpraks 	}
6937c478bd9Sstevel@tonic-gate 	mutex_exit(&pcp->pc_lock);
6947c478bd9Sstevel@tonic-gate 	/*
6957c478bd9Sstevel@tonic-gate 	 * last close:
6967c478bd9Sstevel@tonic-gate 	 * pollwakeup() can not further interact with this cache
6977c478bd9Sstevel@tonic-gate 	 * (all polldat structs are removed from pollhead entries).
6987c478bd9Sstevel@tonic-gate 	 */
6997c478bd9Sstevel@tonic-gate 	if (lastclose)
7007c478bd9Sstevel@tonic-gate 		port_pcache_destroy(pcp);
7017c478bd9Sstevel@tonic-gate }
702