Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * This file containts all the functions required for interactions of
     31  * event sources with the event port file system.
     32  */
     33 
     34 #include <sys/types.h>
     35 #include <sys/conf.h>
     36 #include <sys/stat.h>
     37 #include <sys/errno.h>
     38 #include <sys/kmem.h>
     39 #include <sys/debug.h>
     40 #include <sys/file.h>
     41 #include <sys/sysmacros.h>
     42 #include <sys/systm.h>
     43 #include <sys/bitmap.h>
     44 #include <sys/rctl.h>
     45 #include <sys/atomic.h>
     46 #include <sys/poll_impl.h>
     47 #include <sys/port_impl.h>
     48 
     49 /*
     50  * Maximum number of elements allowed to be passed in a single call of a
     51  * port function (port_sendn(), port_getn().  We need to allocate kernel memory
     52  * for all of them at once, so we can't let it scale without limit.
     53  */
     54 uint_t		port_max_list = PORT_MAX_LIST;
     55 port_control_t	port_control;	/* Event port framework main structure */
     56 
     57 /*
     58  * Block other threads from using a port.
     59  * We enter holding portq->portq_mutex but
     60  * we may drop and reacquire this lock.
     61  * Callers must deal with this fact.
     62  */
     63 void
     64 port_block(port_queue_t *portq)
     65 {
     66 	ASSERT(MUTEX_HELD(&portq->portq_mutex));
     67 
     68 	while (portq->portq_flags & PORTQ_BLOCKED)
     69 		cv_wait(&portq->portq_block_cv, &portq->portq_mutex);
     70 	portq->portq_flags |= PORTQ_BLOCKED;
     71 }
     72 
     73 /*
     74  * Undo port_block(portq).
     75  */
     76 void
     77 port_unblock(port_queue_t *portq)
     78 {
     79 	ASSERT(MUTEX_HELD(&portq->portq_mutex));
     80 
     81 	portq->portq_flags &= ~PORTQ_BLOCKED;
     82 	cv_signal(&portq->portq_block_cv);
     83 }
     84 
     85 /*
     86  * Called from pollwakeup(PORT_SOURCE_FD source) to determine
     87  * if the port's fd needs to be notified of poll events. If yes,
     88  * we mark the port indicating that pollwakeup() is referring
     89  * it so that the port_t does not disappear.  pollwakeup()
     90  * calls port_pollwkdone() after notifying. In port_pollwkdone(),
     91  * we clear the hold on the port_t (clear PORTQ_POLLWK_PEND).
     92  */
     93 int
     94 port_pollwkup(port_t *pp)
     95 {
     96 	int events = 0;
     97 	port_queue_t *portq;
     98 	portq = &pp->port_queue;
     99 	mutex_enter(&portq->portq_mutex);
    100 
    101 	/*
    102 	 * Normally, we should not have a situation where PORTQ_POLLIN
    103 	 * and PORTQ_POLLWK_PEND are set at the same time, but it is
    104 	 * possible. So, in pollwakeup() we ensure that no new fd's get
    105 	 * added to the pollhead between the time it notifies poll events
    106 	 * and calls poll_wkupdone() where we clear the PORTQ_POLLWK_PEND flag.
    107 	 */
    108 	if (portq->portq_flags & PORTQ_POLLIN &&
    109 	    !(portq->portq_flags & PORTQ_POLLWK_PEND)) {
    110 		portq->portq_flags &= ~PORTQ_POLLIN;
    111 		portq->portq_flags |= PORTQ_POLLWK_PEND;
    112 		events = POLLIN;
    113 	}
    114 	mutex_exit(&portq->portq_mutex);
    115 	return (events);
    116 }
    117 
    118 void
    119 port_pollwkdone(port_t *pp)
    120 {
    121 	port_queue_t *portq;
    122 	portq = &pp->port_queue;
    123 	ASSERT(portq->portq_flags & PORTQ_POLLWK_PEND);
    124 	mutex_enter(&portq->portq_mutex);
    125 	portq->portq_flags &= ~PORTQ_POLLWK_PEND;
    126 	cv_signal(&pp->port_cv);
    127 	mutex_exit(&portq->portq_mutex);
    128 }
    129 
    130 
    131 /*
    132  * The port_send_event() function is used by all event sources to submit
    133  * trigerred events to a port. All the data  required for the event management
    134  * is already stored in the port_kevent_t structure.
    135  * The event port internal data is stored in the port_kevent_t structure
    136  * during the allocation time (see port_alloc_event()). The data related to
    137  * the event itself and to the event source management is stored in the
    138  * port_kevent_t structure between the allocation time and submit time
    139  * (see port_init_event()).
    140  *
    141  * This function is often called from interrupt level.
    142  */
    143 void
    144 port_send_event(port_kevent_t *pkevp)
    145 {
    146 	port_queue_t	*portq;
    147 
    148 	portq = &pkevp->portkev_port->port_queue;
    149 	mutex_enter(&portq->portq_mutex);
    150 
    151 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
    152 		/* Event already in the port queue */
    153 		if (pkevp->portkev_source == PORT_SOURCE_FD) {
    154 			mutex_exit(&pkevp->portkev_lock);
    155 		}
    156 		mutex_exit(&portq->portq_mutex);
    157 		return;
    158 	}
    159 
    160 	/* put event in the port queue */
    161 	list_insert_tail(&portq->portq_list, pkevp);
    162 	portq->portq_nent++;
    163 
    164 	/*
    165 	 * Remove the PORTQ_WAIT_EVENTS flag to indicate
    166 	 * that new events are available.
    167 	 */
    168 	portq->portq_flags &= ~PORTQ_WAIT_EVENTS;
    169 	pkevp->portkev_flags |= PORT_KEV_DONEQ;		/* event enqueued */
    170 
    171 	if (pkevp->portkev_source == PORT_SOURCE_FD) {
    172 		mutex_exit(&pkevp->portkev_lock);
    173 	}
    174 
    175 	/* Check if thread is in port_close() waiting for outstanding events */
    176 	if (portq->portq_flags & PORTQ_CLOSE) {
    177 		/* Check if all outstanding events are already in port queue */
    178 		if (pkevp->portkev_port->port_curr <= portq->portq_nent)
    179 			cv_signal(&portq->portq_closecv);
    180 	}
    181 
    182 	if (portq->portq_getn == 0) {
    183 		/*
    184 		 * No thread retrieving events -> check if enough events are
    185 		 * available to satify waiting threads.
    186 		 */
    187 		if (portq->portq_thread &&
    188 		    (portq->portq_nent >= portq->portq_nget))
    189 			cv_signal(&portq->portq_thread->portget_cv);
    190 	}
    191 
    192 	/*
    193 	 * If some thread is polling the port's fd, then notify it.
    194 	 * For PORT_SOURCE_FD source, we don't need to call pollwakeup()
    195 	 * here as it will result in a recursive call(PORT_SOURCE_FD source
    196 	 * is pollwakeup()). Therefore pollwakeup() itself will  notify the
    197 	 * ports if being polled.
    198 	 */
    199 	if (pkevp->portkev_source != PORT_SOURCE_FD &&
    200 	    portq->portq_flags & PORTQ_POLLIN) {
    201 		port_t	*pp;
    202 
    203 		portq->portq_flags &= ~PORTQ_POLLIN;
    204 		/*
    205 		 * Need to save port_t for calling pollwakeup since port_getn()
    206 		 * may end up freeing pkevp once portq_mutex is dropped.
    207 		 */
    208 		pp = pkevp->portkev_port;
    209 		mutex_exit(&portq->portq_mutex);
    210 		pollwakeup(&pp->port_pollhd, POLLIN);
    211 	} else {
    212 		mutex_exit(&portq->portq_mutex);
    213 	}
    214 }
    215 
    216 /*
    217  * The port_alloc_event() function has to be used by all event sources
    218  * to request an slot for event notification.
    219  * The slot reservation could be denied because of lack of resources.
    220  * For that reason the event source should allocate an event slot as early
    221  * as possible and be prepared to get an error code instead of the
    222  * port event pointer.
    223  * Al current event sources allocate an event slot during a system call
    224  * entry. They return an error code to the application if an event slot
    225  * could not be reserved.
    226  * It is also recommended to associate the event source with the port
    227  * before some other port function is used.
    228  * The port argument is a file descriptor obtained by the application as
    229  * a return value of port_create().
    230  * Possible values of flags are:
    231  * PORT_ALLOC_DEFAULT
    232  *	This is the standard type of port events. port_get(n) will free this
    233  *	type of event structures as soon as the events are delivered to the
    234  *	application.
    235  * PORT_ALLOC_PRIVATE
    236  *	This type of event will be use for private use of the event source.
    237  *	The port_get(n) function will deliver events of such an structure to
    238  *	the application but it will not free the event structure itself.
    239  *	The event source must free this structure using port_free_event().
    240  * PORT_ALLOC_CACHED
    241  *	This type of events is used when the event source helds an own
    242  *	cache.
    243  *	The port_get(n) function will deliver events of such an structure to
    244  *	the application but it will not free the event structure itself.
    245  *	The event source must free this structure using port_free_event().
    246  */
    247 int
    248 port_alloc_event(int port, int flags, int source, port_kevent_t **pkevpp)
    249 {
    250 	port_t		*pp;
    251 	file_t		*fp;
    252 	port_kevent_t	*pkevp;
    253 
    254 	if ((fp = getf(port)) == NULL)
    255 		return (EBADF);
    256 
    257 	if (fp->f_vnode->v_type != VPORT) {
    258 		releasef(port);
    259 		return (EBADFD);
    260 	}
    261 
    262 	pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
    263 	if (pkevp == NULL) {
    264 		releasef(port);
    265 		return (ENOMEM);
    266 	}
    267 
    268 	/*
    269 	 * port_max_events is controlled by the resource control
    270 	 * process.port-max-events
    271 	 */
    272 	pp = VTOEP(fp->f_vnode);
    273 	mutex_enter(&pp->port_queue.portq_mutex);
    274 	if (pp->port_curr >= pp->port_max_events) {
    275 		mutex_exit(&pp->port_queue.portq_mutex);
    276 		kmem_cache_free(port_control.pc_cache, pkevp);
    277 		releasef(port);
    278 		return (EAGAIN);
    279 	}
    280 	pp->port_curr++;
    281 	mutex_exit(&pp->port_queue.portq_mutex);
    282 
    283 	bzero(pkevp, sizeof (port_kevent_t));
    284 	mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
    285 	pkevp->portkev_source = source;
    286 	pkevp->portkev_flags = flags;
    287 	pkevp->portkev_pid = curproc->p_pid;
    288 	pkevp->portkev_port = pp;
    289 	*pkevpp = pkevp;
    290 	releasef(port);
    291 	return (0);
    292 }
    293 
    294 /*
    295  * This function is faster than the standard port_alloc_event() and
    296  * can be used when the event source already allocated an event from
    297  * a port.
    298  */
    299 int
    300 port_dup_event(port_kevent_t *pkevp, port_kevent_t **pkevdupp, int flags)
    301 {
    302 	int	error;
    303 
    304 	error = port_alloc_event_local(pkevp->portkev_port,
    305 	    pkevp->portkev_source, flags, pkevdupp);
    306 	if (error == 0)
    307 		(*pkevdupp)->portkev_pid = pkevp->portkev_pid;
    308 	return (error);
    309 }
    310 
    311 /*
    312  * port_alloc_event_local() is reserved for internal use only.
    313  * It is doing the same job as port_alloc_event() but with the event port
    314  * pointer as the first argument.
    315  * The check of the validity of the port file descriptor is skipped here.
    316  */
    317 int
    318 port_alloc_event_local(port_t *pp, int source, int flags,
    319     port_kevent_t **pkevpp)
    320 {
    321 	port_kevent_t	*pkevp;
    322 
    323 	pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
    324 	if (pkevp == NULL)
    325 		return (ENOMEM);
    326 
    327 	mutex_enter(&pp->port_queue.portq_mutex);
    328 	if (pp->port_curr >= pp->port_max_events) {
    329 		mutex_exit(&pp->port_queue.portq_mutex);
    330 		kmem_cache_free(port_control.pc_cache, pkevp);
    331 		return (EAGAIN);
    332 	}
    333 	pp->port_curr++;
    334 	mutex_exit(&pp->port_queue.portq_mutex);
    335 
    336 	bzero(pkevp, sizeof (port_kevent_t));
    337 	mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
    338 	pkevp->portkev_flags = flags;
    339 	pkevp->portkev_port = pp;
    340 	pkevp->portkev_source = source;
    341 	pkevp->portkev_pid = curproc->p_pid;
    342 	*pkevpp = pkevp;
    343 	return (0);
    344 }
    345 
    346 /*
    347  * port_alloc_event_block() has the same functionality of port_alloc_event() +
    348  * - it blocks if not enough event slots are available and
    349  * - it blocks if not enough memory is available.
    350  * Currently port_dispatch() is using this function to increase the
    351  * reliability of event delivery for library event sources.
    352  */
    353 int
    354 port_alloc_event_block(port_t *pp, int source, int flags,
    355     port_kevent_t **pkevpp)
    356 {
    357 	port_kevent_t *pkevp =
    358 	    kmem_cache_alloc(port_control.pc_cache, KM_SLEEP);
    359 
    360 	mutex_enter(&pp->port_queue.portq_mutex);
    361 	while (pp->port_curr >= pp->port_max_events) {
    362 		if (!cv_wait_sig(&pp->port_cv, &pp->port_queue.portq_mutex)) {
    363 			/* signal detected */
    364 			mutex_exit(&pp->port_queue.portq_mutex);
    365 			kmem_cache_free(port_control.pc_cache, pkevp);
    366 			return (EINTR);
    367 		}
    368 	}
    369 	pp->port_curr++;
    370 	mutex_exit(&pp->port_queue.portq_mutex);
    371 
    372 	bzero(pkevp, sizeof (port_kevent_t));
    373 	mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
    374 	pkevp->portkev_flags = flags;
    375 	pkevp->portkev_port = pp;
    376 	pkevp->portkev_source = source;
    377 	pkevp->portkev_pid = curproc->p_pid;
    378 	*pkevpp = pkevp;
    379 	return (0);
    380 }
    381 
    382 /*
    383  * Take an event out of the port queue
    384  */
    385 static void
    386 port_remove_event_doneq(port_kevent_t *pkevp, port_queue_t *portq)
    387 {
    388 	ASSERT(MUTEX_HELD(&portq->portq_mutex));
    389 	list_remove(&portq->portq_list, pkevp);
    390 	portq->portq_nent--;
    391 	pkevp->portkev_flags &= ~PORT_KEV_DONEQ;
    392 }
    393 
    394 /*
    395  * The port_remove_done_event() function takes a fired event out of the
    396  * port queue.
    397  * Currently this function is required to cancel a fired event because
    398  * the application is delivering new association data (see port_associate_fd()).
    399  */
    400 int
    401 port_remove_done_event(port_kevent_t *pkevp)
    402 {
    403 	port_queue_t	*portq;
    404 	int	removed = 0;
    405 
    406 	portq = &pkevp->portkev_port->port_queue;
    407 	mutex_enter(&portq->portq_mutex);
    408 	/* wait for port_get() or port_getn() */
    409 	port_block(portq);
    410 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
    411 		/* event still in port queue */
    412 		if (portq->portq_getn) {
    413 			/*
    414 			 * There could be still fired events in the temp queue;
    415 			 * push those events back to the port queue and
    416 			 * remove requested event afterwards.
    417 			 */
    418 			port_push_eventq(portq);
    419 		}
    420 		/* now remove event from the port queue */
    421 		port_remove_event_doneq(pkevp, portq);
    422 		removed = 1;
    423 	}
    424 	port_unblock(portq);
    425 	mutex_exit(&portq->portq_mutex);
    426 	return (removed);
    427 }
    428 
    429 /*
    430  * Return port event back to the kmem_cache.
    431  * If the event is currently in the port queue the event itself will only
    432  * be set as invalid. The port_get(n) function will not deliver such events
    433  * to the application and it will return them back to the kmem_cache.
    434  */
    435 void
    436 port_free_event(port_kevent_t *pkevp)
    437 {
    438 	port_queue_t	*portq;
    439 	port_t		*pp;
    440 
    441 	pp = pkevp->portkev_port;
    442 	if (pp == NULL)
    443 		return;
    444 	if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) {
    445 		port_free_event_local(pkevp, 0);
    446 		return;
    447 	}
    448 
    449 	portq = &pp->port_queue;
    450 	mutex_enter(&portq->portq_mutex);
    451 	port_block(portq);
    452 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
    453 		pkevp->portkev_flags |= PORT_KEV_FREE;
    454 		pkevp->portkev_callback = NULL;
    455 		port_unblock(portq);
    456 		mutex_exit(&portq->portq_mutex);
    457 		return;
    458 	}
    459 	port_unblock(portq);
    460 
    461 	if (pkevp->portkev_flags & PORT_KEV_CACHED) {
    462 		mutex_exit(&portq->portq_mutex);
    463 		return;
    464 	}
    465 
    466 	if (--pp->port_curr < pp->port_max_events)
    467 		cv_signal(&pp->port_cv);
    468 	if (portq->portq_flags & PORTQ_CLOSE) {
    469 		/*
    470 		 * Another thread is closing the event port.
    471 		 * That thread will sleep until all allocated event
    472 		 * structures returned to the event port framework.
    473 		 * The portq_mutex is used to synchronize the status
    474 		 * of the allocated event structures (port_curr).
    475 		 */
    476 		if (pp->port_curr <= portq->portq_nent)
    477 			cv_signal(&portq->portq_closecv);
    478 	}
    479 	mutex_exit(&portq->portq_mutex);
    480 	port_free_event_local(pkevp, 1);
    481 }
    482 
    483 /*
    484  * This event port internal function is used by port_free_event() and
    485  * other port internal functions to return event structures back to the
    486  * kmem_cache.
    487  */
    488 void
    489 port_free_event_local(port_kevent_t *pkevp, int counter)
    490 {
    491 	port_t *pp = pkevp->portkev_port;
    492 	port_queue_t *portq = &pp->port_queue;
    493 	int wakeup;
    494 
    495 	pkevp->portkev_callback = NULL;
    496 	pkevp->portkev_flags = 0;
    497 	pkevp->portkev_port = NULL;
    498 	mutex_destroy(&pkevp->portkev_lock);
    499 	kmem_cache_free(port_control.pc_cache, pkevp);
    500 
    501 	mutex_enter(&portq->portq_mutex);
    502 	if (counter == 0) {
    503 		if (--pp->port_curr < pp->port_max_events)
    504 			cv_signal(&pp->port_cv);
    505 	}
    506 	wakeup = (portq->portq_flags & PORTQ_POLLOUT);
    507 	portq->portq_flags &= ~PORTQ_POLLOUT;
    508 	mutex_exit(&portq->portq_mutex);
    509 
    510 	/* Submit a POLLOUT event if requested */
    511 	if (wakeup)
    512 		pollwakeup(&pp->port_pollhd, POLLOUT);
    513 }
    514 
    515 /*
    516  * port_init_event(port_event_t *pev, uintptr_t object, void *user,
    517  *	int (*port_callback)(void *, int *, pid_t, int, void *), void *sysarg);
    518  *	This function initializes most of the "wired" elements of the port
    519  *	event structure. This is normally being used just after the allocation
    520  *	of the port event structure.
    521  *	pkevp	: pointer to the port event structure
    522  *	object	: object associated with this event structure
    523  *	user	: user defined pointer delivered with the association function
    524  *	port_callback:
    525  *		  Address of the callback function which will be called
    526  *		  - just before the event is delivered to the application.
    527  *		    The callback function is called in user context and can be
    528  *		    used for copyouts, e.g.
    529  *		  - on close() or dissociation of the event. The sub-system
    530  *		    must remove immediately every existing association of
    531  *		    some object with this event.
    532  *	sysarg	: event source propietary data
    533  */
    534 void
    535 port_init_event(port_kevent_t *pkevp, uintptr_t object, void *user,
    536     int (*port_callback)(void *, int *, pid_t, int, void *),
    537     void *sysarg)
    538 {
    539 	pkevp->portkev_object = object;
    540 	pkevp->portkev_user = user;
    541 	pkevp->portkev_callback = port_callback;
    542 	pkevp->portkev_arg = sysarg;
    543 }
    544 
    545 /*
    546  * This routine removes a portfd_t from the fd cache's hash table.
    547  */
    548 void
    549 port_pcache_remove_fd(port_fdcache_t *pcp, portfd_t *pfd)
    550 {
    551 	polldat_t	*lpdp;
    552 	polldat_t	*cpdp;
    553 	portfd_t	**bucket;
    554 	polldat_t	*pdp = PFTOD(pfd);
    555 
    556 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
    557 	bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
    558 	cpdp = PFTOD(*bucket);
    559 	if (pdp == cpdp) {
    560 		*bucket = PDTOF(pdp->pd_hashnext);
    561 		if (--pcp->pc_fdcount == 0) {
    562 			/*
    563 			 * signal the thread which may have blocked in
    564 			 * port_close_sourcefd() on lastclose waiting
    565 			 * for pc_fdcount to drop to 0.
    566 			 */
    567 			cv_signal(&pcp->pc_lclosecv);
    568 		}
    569 		kmem_free(pfd, sizeof (portfd_t));
    570 		return;
    571 	}
    572 
    573 	while (cpdp != NULL) {
    574 		lpdp = cpdp;
    575 		cpdp = cpdp->pd_hashnext;
    576 		if (cpdp == pdp) {
    577 			/* polldat struct found */
    578 			lpdp->pd_hashnext = pdp->pd_hashnext;
    579 			if (--pcp->pc_fdcount == 0) {
    580 				/*
    581 				 * signal the thread which may have blocked in
    582 				 * port_close_sourcefd() on lastclose waiting
    583 				 * for pc_fdcount to drop to 0.
    584 				 */
    585 				cv_signal(&pcp->pc_lclosecv);
    586 			}
    587 			break;
    588 		}
    589 	}
    590 	ASSERT(cpdp != NULL);
    591 	kmem_free(pfd, sizeof (portfd_t));
    592 }
    593 
    594 /*
    595  * The port_push_eventq() function is used to move all remaining events
    596  * from the temporary queue used in port_get(n)() to the standard port
    597  * queue.
    598  */
    599 void
    600 port_push_eventq(port_queue_t *portq)
    601 {
    602 	/*
    603 	 * Append temporary portq_get_list to the port queue. On return
    604 	 * the temporary portq_get_list is empty.
    605 	 */
    606 	list_move_tail(&portq->portq_list, &portq->portq_get_list);
    607 	portq->portq_nent += portq->portq_tnent;
    608 	portq->portq_tnent = 0;
    609 }
    610 
    611 /*
    612  * The port_remove_fd_object() function frees all resources associated with
    613  * delivered portfd_t structure. Returns 1 if the port_kevent was found
    614  * and removed from the port queue.
    615  */
    616 int
    617 port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
    618 {
    619 	port_queue_t	*portq;
    620 	polldat_t	*pdp = PFTOD(pfd);
    621 	port_kevent_t	*pkevp;
    622 	int		error;
    623 	int		removed = 0;
    624 
    625 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
    626 	if (pdp->pd_php != NULL) {
    627 		pollhead_delete(pdp->pd_php, pdp);
    628 		pdp->pd_php = NULL;
    629 	}
    630 	pkevp =  pdp->pd_portev;
    631 	portq = &pp->port_queue;
    632 	mutex_enter(&portq->portq_mutex);
    633 	port_block(portq);
    634 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
    635 		if (portq->portq_getn && portq->portq_tnent) {
    636 			/*
    637 			 * move events from the temporary "get" queue
    638 			 * back to the port queue
    639 			 */
    640 			port_push_eventq(portq);
    641 		}
    642 		/* cleanup merged port queue */
    643 		port_remove_event_doneq(pkevp, portq);
    644 		removed = 1;
    645 	}
    646 	port_unblock(portq);
    647 	mutex_exit(&portq->portq_mutex);
    648 	if (pkevp->portkev_callback) {
    649 		(void) (*pkevp->portkev_callback)(pkevp->portkev_arg,
    650 		    &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE,
    651 		    pkevp);
    652 	}
    653 	port_free_event_local(pkevp, 0);
    654 
    655 	/* remove polldat struct */
    656 	port_pcache_remove_fd(pcp, pfd);
    657 	return (removed);
    658 }
    659 
    660 /*
    661  * The port_close_fd() function dissociates a file descriptor from a port
    662  * and removes all allocated resources.
    663  * close(2) detects in the uf_entry_t structure that the fd is associated
    664  * with a port (at least one port).
    665  * The fd can be associated with several ports.
    666  */
    667 void
    668 port_close_pfd(portfd_t *pfd)
    669 {
    670 	port_t		*pp;
    671 	port_fdcache_t	*pcp;
    672 
    673 	/*
    674 	 * the portfd_t passed in should be for this proc.
    675 	 */
    676 	ASSERT(curproc->p_pid == PFTOD(pfd)->pd_portev->portkev_pid);
    677 	pp = PFTOD(pfd)->pd_portev->portkev_port;
    678 	pcp = pp->port_queue.portq_pcp;
    679 	mutex_enter(&pcp->pc_lock);
    680 	(void) port_remove_fd_object(pfd, pp, pcp);
    681 	mutex_exit(&pcp->pc_lock);
    682 }
    683 
    684 /*
    685  * The port_associate_ksource() function associates an event source with a port.
    686  * On port_close() all associated sources are requested to free all local
    687  * resources associated with the event port.
    688  * The association of a source with a port can only be done one time. Further
    689  * calls of this function will only increment the reference counter.
    690  * The allocated port_source_t structure is removed from the port as soon as
    691  * the reference counter becomes 0.
    692  */
    693 /* ARGSUSED */
    694 int
    695 port_associate_ksource(int port, int source, port_source_t **portsrc,
    696     void (*port_src_close)(void *, int, pid_t, int), void *arg,
    697     int (*port_src_associate)(port_kevent_t *, int, int, uintptr_t, void *))
    698 {
    699 	port_t		*pp;
    700 	file_t		*fp;
    701 	port_source_t	**ps;
    702 	port_source_t	*pse;
    703 
    704 	if ((fp = getf(port)) == NULL)
    705 		return (EBADF);
    706 
    707 	if (fp->f_vnode->v_type != VPORT) {
    708 		releasef(port);
    709 		return (EBADFD);
    710 	}
    711 	pp = VTOEP(fp->f_vnode);
    712 
    713 	mutex_enter(&pp->port_queue.portq_source_mutex);
    714 	ps = &pp->port_queue.portq_scache[PORT_SHASH(source)];
    715 	for (pse = *ps; pse != NULL; pse = pse->portsrc_next) {
    716 		if (pse->portsrc_source == source)
    717 			break;
    718 	}
    719 
    720 	if (pse == NULL) {
    721 		/* Create association of the event source with the port */
    722 		pse = kmem_zalloc(sizeof (port_source_t), KM_NOSLEEP);
    723 		if (pse == NULL) {
    724 			mutex_exit(&pp->port_queue.portq_source_mutex);
    725 			releasef(port);
    726 			return (ENOMEM);
    727 		}
    728 		pse->portsrc_source = source;
    729 		pse->portsrc_close = port_src_close;
    730 		pse->portsrc_closearg = arg;
    731 		pse->portsrc_cnt = 1;
    732 		if (*ps)
    733 			pse->portsrc_next = (*ps)->portsrc_next;
    734 		*ps = pse;
    735 	} else {
    736 		/* entry already available, source is only requesting count */
    737 		pse->portsrc_cnt++;
    738 	}
    739 	mutex_exit(&pp->port_queue.portq_source_mutex);
    740 	releasef(port);
    741 	if (portsrc)
    742 		*portsrc = pse;
    743 	return (0);
    744 }
    745 
    746 /*
    747  * The port_dissociate_ksource() function dissociates an event source from
    748  * a port.
    749  */
    750 int
    751 port_dissociate_ksource(int port, int source, port_source_t *ps)
    752 {
    753 	port_t		*pp;
    754 	file_t		*fp;
    755 	port_source_t	**psh;
    756 
    757 	if (ps == NULL)
    758 		return (EINVAL);
    759 
    760 	if ((fp = getf(port)) == NULL)
    761 		return (EBADF);
    762 
    763 	if (fp->f_vnode->v_type != VPORT) {
    764 		releasef(port);
    765 		return (EBADFD);
    766 	}
    767 	pp = VTOEP(fp->f_vnode);
    768 
    769 	mutex_enter(&pp->port_queue.portq_source_mutex);
    770 	if (--ps->portsrc_cnt == 0) {
    771 		/* last association removed -> free source structure */
    772 		if (ps->portsrc_prev == NULL) {
    773 			/* first entry */
    774 			psh = &pp->port_queue.portq_scache[PORT_SHASH(source)];
    775 			*psh = ps->portsrc_next;
    776 			if (ps->portsrc_next)
    777 				ps->portsrc_next->portsrc_prev = NULL;
    778 		} else {
    779 			ps->portsrc_prev->portsrc_next = ps->portsrc_next;
    780 			if (ps->portsrc_next)
    781 				ps->portsrc_next->portsrc_prev =
    782 				    ps->portsrc_prev;
    783 		}
    784 		kmem_free(ps, sizeof (port_source_t));
    785 	}
    786 	mutex_exit(&pp->port_queue.portq_source_mutex);
    787 	releasef(port);
    788 	return (0);
    789 }
    790 
    791 void
    792 free_fopdata(vnode_t *vp)
    793 {
    794 	portfop_vp_t *pvp;
    795 	pvp = vp->v_fopdata;
    796 	ASSERT(pvp->pvp_femp == NULL);
    797 	mutex_destroy(&pvp->pvp_mutex);
    798 	list_destroy(&pvp->pvp_pfoplist);
    799 	kmem_free(pvp, sizeof (*pvp));
    800 	vp->v_fopdata = NULL;
    801 }
    802