xref: /titanic_50/usr/src/uts/common/os/port_subr.c (revision 15d9d0b528387242011cdcc6190c9e598cfe3a07)
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 2007 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 		portq->portq_flags &= ~PORTQ_POLLIN;
202 		mutex_exit(&portq->portq_mutex);
203 		pollwakeup(&pkevp->portkev_port->port_pollhd, POLLIN);
204 	} else {
205 		mutex_exit(&portq->portq_mutex);
206 	}
207 }
208 
209 /*
210  * The port_alloc_event() function has to be used by all event sources
211  * to request an slot for event notification.
212  * The slot reservation could be denied because of lack of resources.
213  * For that reason the event source should allocate an event slot as early
214  * as possible and be prepared to get an error code instead of the
215  * port event pointer.
216  * Al current event sources allocate an event slot during a system call
217  * entry. They return an error code to the application if an event slot
218  * could not be reserved.
219  * It is also recommended to associate the event source with the port
220  * before some other port function is used.
221  * The port argument is a file descriptor obtained by the application as
222  * a return value of port_create().
223  * Possible values of flags are:
224  * PORT_ALLOC_DEFAULT
225  *	This is the standard type of port events. port_get(n) will free this
226  *	type of event structures as soon as the events are delivered to the
227  *	application.
228  * PORT_ALLOC_PRIVATE
229  *	This type of event will be use for private use of the event source.
230  *	The port_get(n) function will deliver events of such an structure to
231  *	the application but it will not free the event structure itself.
232  *	The event source must free this structure using port_free_event().
233  * PORT_ALLOC_CACHED
234  *	This type of events is used when the event source helds an own
235  *	cache.
236  *	The port_get(n) function will deliver events of such an structure to
237  *	the application but it will not free the event structure itself.
238  *	The event source must free this structure using port_free_event().
239  */
240 int
241 port_alloc_event(int port, int flags, int source, port_kevent_t **pkevpp)
242 {
243 	port_t		*pp;
244 	file_t		*fp;
245 	port_kevent_t	*pkevp;
246 
247 	if ((fp = getf(port)) == NULL)
248 		return (EBADF);
249 
250 	if (fp->f_vnode->v_type != VPORT) {
251 		releasef(port);
252 		return (EBADFD);
253 	}
254 
255 	pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
256 	if (pkevp == NULL) {
257 		releasef(port);
258 		return (ENOMEM);
259 	}
260 
261 	/*
262 	 * port_max_events is controlled by the resource control
263 	 * process.port-max-events
264 	 */
265 	pp = VTOEP(fp->f_vnode);
266 	mutex_enter(&pp->port_queue.portq_mutex);
267 	if (pp->port_curr >= pp->port_max_events) {
268 		mutex_exit(&pp->port_queue.portq_mutex);
269 		kmem_cache_free(port_control.pc_cache, pkevp);
270 		releasef(port);
271 		return (EAGAIN);
272 	}
273 	pp->port_curr++;
274 	mutex_exit(&pp->port_queue.portq_mutex);
275 
276 	bzero(pkevp, sizeof (port_kevent_t));
277 	mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
278 	pkevp->portkev_source = source;
279 	pkevp->portkev_flags = flags;
280 	pkevp->portkev_pid = curproc->p_pid;
281 	pkevp->portkev_port = pp;
282 	*pkevpp = pkevp;
283 	releasef(port);
284 	return (0);
285 }
286 
287 /*
288  * This function is faster than the standard port_alloc_event() and
289  * can be used when the event source already allocated an event from
290  * a port.
291  */
292 int
293 port_dup_event(port_kevent_t *pkevp, port_kevent_t **pkevdupp, int flags)
294 {
295 	int	error;
296 
297 	error = port_alloc_event_local(pkevp->portkev_port,
298 	    pkevp->portkev_source, flags, pkevdupp);
299 	if (error == 0)
300 		(*pkevdupp)->portkev_pid = pkevp->portkev_pid;
301 	return (error);
302 }
303 
304 /*
305  * port_alloc_event_local() is reserved for internal use only.
306  * It is doing the same job as port_alloc_event() but with the event port
307  * pointer as the first argument.
308  * The check of the validity of the port file descriptor is skipped here.
309  */
310 int
311 port_alloc_event_local(port_t *pp, int source, int flags,
312     port_kevent_t **pkevpp)
313 {
314 	port_kevent_t	*pkevp;
315 
316 	pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
317 	if (pkevp == NULL)
318 		return (ENOMEM);
319 
320 	mutex_enter(&pp->port_queue.portq_mutex);
321 	if (pp->port_curr >= pp->port_max_events) {
322 		mutex_exit(&pp->port_queue.portq_mutex);
323 		kmem_cache_free(port_control.pc_cache, pkevp);
324 		return (EAGAIN);
325 	}
326 	pp->port_curr++;
327 	mutex_exit(&pp->port_queue.portq_mutex);
328 
329 	bzero(pkevp, sizeof (port_kevent_t));
330 	mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
331 	pkevp->portkev_flags = flags;
332 	pkevp->portkev_port = pp;
333 	pkevp->portkev_source = source;
334 	pkevp->portkev_pid = curproc->p_pid;
335 	*pkevpp = pkevp;
336 	return (0);
337 }
338 
339 /*
340  * port_alloc_event_block() has the same functionality of port_alloc_event() +
341  * - it blocks if not enough event slots are available and
342  * - it blocks if not enough memory is available.
343  * Currently port_dispatch() is using this function to increase the
344  * reliability of event delivery for library event sources.
345  */
346 int
347 port_alloc_event_block(port_t *pp, int source, int flags,
348     port_kevent_t **pkevpp)
349 {
350 	port_kevent_t *pkevp =
351 	    kmem_cache_alloc(port_control.pc_cache, KM_SLEEP);
352 
353 	mutex_enter(&pp->port_queue.portq_mutex);
354 	while (pp->port_curr >= pp->port_max_events) {
355 		if (!cv_wait_sig(&pp->port_cv, &pp->port_queue.portq_mutex)) {
356 			/* signal detected */
357 			mutex_exit(&pp->port_queue.portq_mutex);
358 			kmem_cache_free(port_control.pc_cache, pkevp);
359 			return (EINTR);
360 		}
361 	}
362 	pp->port_curr++;
363 	mutex_exit(&pp->port_queue.portq_mutex);
364 
365 	bzero(pkevp, sizeof (port_kevent_t));
366 	mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
367 	pkevp->portkev_flags = flags;
368 	pkevp->portkev_port = pp;
369 	pkevp->portkev_source = source;
370 	pkevp->portkev_pid = curproc->p_pid;
371 	*pkevpp = pkevp;
372 	return (0);
373 }
374 
375 /*
376  * Take an event out of the port queue
377  */
378 static void
379 port_remove_event_doneq(port_kevent_t *pkevp, port_queue_t *portq)
380 {
381 	ASSERT(MUTEX_HELD(&portq->portq_mutex));
382 	list_remove(&portq->portq_list, pkevp);
383 	portq->portq_nent--;
384 	pkevp->portkev_flags &= ~PORT_KEV_DONEQ;
385 }
386 
387 /*
388  * The port_remove_done_event() function takes a fired event out of the
389  * port queue.
390  * Currently this function is required to cancel a fired event because
391  * the application is delivering new association data (see port_associate_fd()).
392  */
393 int
394 port_remove_done_event(port_kevent_t *pkevp)
395 {
396 	port_queue_t	*portq;
397 	int	removed = 0;
398 
399 	portq = &pkevp->portkev_port->port_queue;
400 	mutex_enter(&portq->portq_mutex);
401 	/* wait for port_get() or port_getn() */
402 	port_block(portq);
403 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
404 		/* event still in port queue */
405 		if (portq->portq_getn) {
406 			/*
407 			 * There could be still fired events in the temp queue;
408 			 * push those events back to the port queue and
409 			 * remove requested event afterwards.
410 			 */
411 			port_push_eventq(portq);
412 		}
413 		/* now remove event from the port queue */
414 		port_remove_event_doneq(pkevp, portq);
415 		removed = 1;
416 	}
417 	port_unblock(portq);
418 	mutex_exit(&portq->portq_mutex);
419 	return (removed);
420 }
421 
422 /*
423  * Return port event back to the kmem_cache.
424  * If the event is currently in the port queue the event itself will only
425  * be set as invalid. The port_get(n) function will not deliver such events
426  * to the application and it will return them back to the kmem_cache.
427  */
428 void
429 port_free_event(port_kevent_t *pkevp)
430 {
431 	port_queue_t	*portq;
432 	port_t		*pp;
433 
434 	pp = pkevp->portkev_port;
435 	if (pp == NULL)
436 		return;
437 	if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) {
438 		port_free_event_local(pkevp, 0);
439 		return;
440 	}
441 
442 	portq = &pp->port_queue;
443 	mutex_enter(&portq->portq_mutex);
444 	port_block(portq);
445 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
446 		pkevp->portkev_flags |= PORT_KEV_FREE;
447 		pkevp->portkev_callback = NULL;
448 		port_unblock(portq);
449 		mutex_exit(&portq->portq_mutex);
450 		return;
451 	}
452 	port_unblock(portq);
453 
454 	if (pkevp->portkev_flags & PORT_KEV_CACHED) {
455 		mutex_exit(&portq->portq_mutex);
456 		return;
457 	}
458 
459 	if (--pp->port_curr < pp->port_max_events)
460 		cv_signal(&pp->port_cv);
461 	if (portq->portq_flags & PORTQ_CLOSE) {
462 		/*
463 		 * Another thread is closing the event port.
464 		 * That thread will sleep until all allocated event
465 		 * structures returned to the event port framework.
466 		 * The portq_mutex is used to synchronize the status
467 		 * of the allocated event structures (port_curr).
468 		 */
469 		if (pp->port_curr <= portq->portq_nent)
470 			cv_signal(&portq->portq_closecv);
471 	}
472 	mutex_exit(&portq->portq_mutex);
473 	port_free_event_local(pkevp, 1);
474 }
475 
476 /*
477  * This event port internal function is used by port_free_event() and
478  * other port internal functions to return event structures back to the
479  * kmem_cache.
480  */
481 void
482 port_free_event_local(port_kevent_t *pkevp, int counter)
483 {
484 	port_t *pp = pkevp->portkev_port;
485 	port_queue_t *portq = &pp->port_queue;
486 	int wakeup;
487 
488 	pkevp->portkev_callback = NULL;
489 	pkevp->portkev_flags = 0;
490 	pkevp->portkev_port = NULL;
491 	mutex_destroy(&pkevp->portkev_lock);
492 	kmem_cache_free(port_control.pc_cache, pkevp);
493 
494 	mutex_enter(&portq->portq_mutex);
495 	if (counter == 0) {
496 		if (--pp->port_curr < pp->port_max_events)
497 			cv_signal(&pp->port_cv);
498 	}
499 	wakeup = (portq->portq_flags & PORTQ_POLLOUT);
500 	portq->portq_flags &= ~PORTQ_POLLOUT;
501 	mutex_exit(&portq->portq_mutex);
502 
503 	/* Submit a POLLOUT event if requested */
504 	if (wakeup)
505 		pollwakeup(&pp->port_pollhd, POLLOUT);
506 }
507 
508 /*
509  * port_init_event(port_event_t *pev, uintptr_t object, void *user,
510  *	int (*port_callback)(void *, int *, pid_t, int, void *), void *sysarg);
511  *	This function initializes most of the "wired" elements of the port
512  *	event structure. This is normally being used just after the allocation
513  *	of the port event structure.
514  *	pkevp	: pointer to the port event structure
515  *	object	: object associated with this event structure
516  *	user	: user defined pointer delivered with the association function
517  *	port_callback:
518  *		  Address of the callback function which will be called
519  *		  - just before the event is delivered to the application.
520  *		    The callback function is called in user context and can be
521  *		    used for copyouts, e.g.
522  *		  - on close() or dissociation of the event. The sub-system
523  *		    must remove immediately every existing association of
524  *		    some object with this event.
525  *	sysarg	: event source propietary data
526  */
527 void
528 port_init_event(port_kevent_t *pkevp, uintptr_t object, void *user,
529     int (*port_callback)(void *, int *, pid_t, int, void *),
530     void *sysarg)
531 {
532 	pkevp->portkev_object = object;
533 	pkevp->portkev_user = user;
534 	pkevp->portkev_callback = port_callback;
535 	pkevp->portkev_arg = sysarg;
536 }
537 
538 /*
539  * This routine removes a portfd_t from the fd cache's hash table.
540  */
541 void
542 port_pcache_remove_fd(port_fdcache_t *pcp, portfd_t *pfd)
543 {
544 	polldat_t	*lpdp;
545 	polldat_t	*cpdp;
546 	portfd_t	**bucket;
547 	polldat_t	*pdp = PFTOD(pfd);
548 
549 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
550 	bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
551 	cpdp = PFTOD(*bucket);
552 	if (pdp == cpdp) {
553 		*bucket = PDTOF(pdp->pd_hashnext);
554 		if (--pcp->pc_fdcount == 0) {
555 			/*
556 			 * signal the thread which may have blocked in
557 			 * port_close_sourcefd() on lastclose waiting
558 			 * for pc_fdcount to drop to 0.
559 			 */
560 			cv_signal(&pcp->pc_lclosecv);
561 		}
562 		kmem_free(pfd, sizeof (portfd_t));
563 		return;
564 	}
565 
566 	while (cpdp != NULL) {
567 		lpdp = cpdp;
568 		cpdp = cpdp->pd_hashnext;
569 		if (cpdp == pdp) {
570 			/* polldat struct found */
571 			lpdp->pd_hashnext = pdp->pd_hashnext;
572 			if (--pcp->pc_fdcount == 0) {
573 				/*
574 				 * signal the thread which may have blocked in
575 				 * port_close_sourcefd() on lastclose waiting
576 				 * for pc_fdcount to drop to 0.
577 				 */
578 				cv_signal(&pcp->pc_lclosecv);
579 			}
580 			break;
581 		}
582 	}
583 	ASSERT(cpdp != NULL);
584 	kmem_free(pfd, sizeof (portfd_t));
585 }
586 
587 /*
588  * The port_push_eventq() function is used to move all remaining events
589  * from the temporary queue used in port_get(n)() to the standard port
590  * queue.
591  */
592 void
593 port_push_eventq(port_queue_t *portq)
594 {
595 	/*
596 	 * Append temporary portq_get_list to the port queue. On return
597 	 * the temporary portq_get_list is empty.
598 	 */
599 	list_move_tail(&portq->portq_list, &portq->portq_get_list);
600 	portq->portq_nent += portq->portq_tnent;
601 	portq->portq_tnent = 0;
602 }
603 
604 /*
605  * The port_remove_fd_object() function frees all resources associated with
606  * delivered portfd_t structure. Returns 1 if the port_kevent was found
607  * and removed from the port queue.
608  */
609 int
610 port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
611 {
612 	port_queue_t	*portq;
613 	polldat_t	*pdp = PFTOD(pfd);
614 	port_kevent_t	*pkevp;
615 	int		error;
616 	int		removed = 0;
617 
618 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
619 	if (pdp->pd_php != NULL) {
620 		pollhead_delete(pdp->pd_php, pdp);
621 		pdp->pd_php = NULL;
622 	}
623 	pkevp =  pdp->pd_portev;
624 	portq = &pp->port_queue;
625 	mutex_enter(&portq->portq_mutex);
626 	port_block(portq);
627 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
628 		if (portq->portq_getn && portq->portq_tnent) {
629 			/*
630 			 * move events from the temporary "get" queue
631 			 * back to the port queue
632 			 */
633 			port_push_eventq(portq);
634 		}
635 		/* cleanup merged port queue */
636 		port_remove_event_doneq(pkevp, portq);
637 		removed = 1;
638 	}
639 	port_unblock(portq);
640 	mutex_exit(&portq->portq_mutex);
641 	if (pkevp->portkev_callback) {
642 		(void) (*pkevp->portkev_callback)(pkevp->portkev_arg,
643 		    &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE,
644 		    pkevp);
645 	}
646 	port_free_event_local(pkevp, 0);
647 
648 	/* remove polldat struct */
649 	port_pcache_remove_fd(pcp, pfd);
650 	return (removed);
651 }
652 
653 /*
654  * The port_close_fd() function dissociates a file descriptor from a port
655  * and removes all allocated resources.
656  * close(2) detects in the uf_entry_t structure that the fd is associated
657  * with a port (at least one port).
658  * The fd can be associated with several ports.
659  */
660 void
661 port_close_pfd(portfd_t *pfd)
662 {
663 	port_t		*pp;
664 	port_fdcache_t	*pcp;
665 
666 	/*
667 	 * the portfd_t passed in should be for this proc.
668 	 */
669 	ASSERT(curproc->p_pid == PFTOD(pfd)->pd_portev->portkev_pid);
670 	pp = PFTOD(pfd)->pd_portev->portkev_port;
671 	pcp = pp->port_queue.portq_pcp;
672 	mutex_enter(&pcp->pc_lock);
673 	(void) port_remove_fd_object(pfd, pp, pcp);
674 	mutex_exit(&pcp->pc_lock);
675 }
676 
677 /*
678  * The port_associate_ksource() function associates an event source with a port.
679  * On port_close() all associated sources are requested to free all local
680  * resources associated with the event port.
681  * The association of a source with a port can only be done one time. Further
682  * calls of this function will only increment the reference counter.
683  * The allocated port_source_t structure is removed from the port as soon as
684  * the reference counter becomes 0.
685  */
686 /* ARGSUSED */
687 int
688 port_associate_ksource(int port, int source, port_source_t **portsrc,
689     void (*port_src_close)(void *, int, pid_t, int), void *arg,
690     int (*port_src_associate)(port_kevent_t *, int, int, uintptr_t, void *))
691 {
692 	port_t		*pp;
693 	file_t		*fp;
694 	port_source_t	**ps;
695 	port_source_t	*pse;
696 
697 	if ((fp = getf(port)) == NULL)
698 		return (EBADF);
699 
700 	if (fp->f_vnode->v_type != VPORT) {
701 		releasef(port);
702 		return (EBADFD);
703 	}
704 	pp = VTOEP(fp->f_vnode);
705 
706 	mutex_enter(&pp->port_queue.portq_source_mutex);
707 	ps = &pp->port_queue.portq_scache[PORT_SHASH(source)];
708 	for (pse = *ps; pse != NULL; pse = pse->portsrc_next) {
709 		if (pse->portsrc_source == source)
710 			break;
711 	}
712 
713 	if (pse == NULL) {
714 		/* Create association of the event source with the port */
715 		pse = kmem_zalloc(sizeof (port_source_t), KM_NOSLEEP);
716 		if (pse == NULL) {
717 			mutex_exit(&pp->port_queue.portq_source_mutex);
718 			releasef(port);
719 			return (ENOMEM);
720 		}
721 		pse->portsrc_source = source;
722 		pse->portsrc_close = port_src_close;
723 		pse->portsrc_closearg = arg;
724 		pse->portsrc_cnt = 1;
725 		if (*ps)
726 			pse->portsrc_next = (*ps)->portsrc_next;
727 		*ps = pse;
728 	} else {
729 		/* entry already available, source is only requesting count */
730 		pse->portsrc_cnt++;
731 	}
732 	mutex_exit(&pp->port_queue.portq_source_mutex);
733 	releasef(port);
734 	if (portsrc)
735 		*portsrc = pse;
736 	return (0);
737 }
738 
739 /*
740  * The port_dissociate_ksource() function dissociates an event source from
741  * a port.
742  */
743 int
744 port_dissociate_ksource(int port, int source, port_source_t *ps)
745 {
746 	port_t		*pp;
747 	file_t		*fp;
748 	port_source_t	**psh;
749 
750 	if (ps == NULL)
751 		return (EINVAL);
752 
753 	if ((fp = getf(port)) == NULL)
754 		return (EBADF);
755 
756 	if (fp->f_vnode->v_type != VPORT) {
757 		releasef(port);
758 		return (EBADFD);
759 	}
760 	pp = VTOEP(fp->f_vnode);
761 
762 	mutex_enter(&pp->port_queue.portq_source_mutex);
763 	if (--ps->portsrc_cnt == 0) {
764 		/* last association removed -> free source structure */
765 		if (ps->portsrc_prev == NULL) {
766 			/* first entry */
767 			psh = &pp->port_queue.portq_scache[PORT_SHASH(source)];
768 			*psh = ps->portsrc_next;
769 			if (ps->portsrc_next)
770 				ps->portsrc_next->portsrc_prev = NULL;
771 		} else {
772 			ps->portsrc_prev->portsrc_next = ps->portsrc_next;
773 			if (ps->portsrc_next)
774 				ps->portsrc_next->portsrc_prev =
775 				    ps->portsrc_prev;
776 		}
777 		kmem_free(ps, sizeof (port_source_t));
778 	}
779 	mutex_exit(&pp->port_queue.portq_source_mutex);
780 	releasef(port);
781 	return (0);
782 }
783 
784 void
785 free_fopdata(vnode_t *vp)
786 {
787 	portfop_vp_t *pvp;
788 	pvp = vp->v_fopdata;
789 	ASSERT(pvp->pvp_femp == NULL);
790 	mutex_destroy(&pvp->pvp_mutex);
791 	list_destroy(&pvp->pvp_pfoplist);
792 	kmem_free(pvp, sizeof (*pvp));
793 	vp->v_fopdata = NULL;
794 }
795