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