xref: /illumos-gate/usr/src/uts/common/os/port_subr.c (revision 80ab886d233f514d54c2a6bdeb9fdfd951bd6881)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2006 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  * The port_send_event() function is used by all event sources to submit
59  * trigerred events to a port. All the data  required for the event management
60  * is already stored in the port_kevent_t structure.
61  * The event port internal data is stored in the port_kevent_t structure
62  * during the allocation time (see port_alloc_event()). The data related to
63  * the event itself and to the event source management is stored in the
64  * port_kevent_t structure between the allocation time and submit time
65  * (see port_init_event()).
66  *
67  * This function is often called from interrupt level.
68  */
69 int
70 port_send_event(port_kevent_t *pkevp)
71 {
72 	port_queue_t	*portq;
73 
74 	portq = &pkevp->portkev_port->port_queue;
75 	mutex_enter(&portq->portq_mutex);
76 
77 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
78 		/* Event already in the port queue */
79 		if (pkevp->portkev_flags & PORT_ALLOC_CACHED) {
80 			mutex_exit(&pkevp->portkev_lock);
81 		}
82 		mutex_exit(&portq->portq_mutex);
83 		return (0);
84 	}
85 
86 	/* put event in the port queue */
87 	list_insert_tail(&portq->portq_list, pkevp);
88 	portq->portq_nent++;
89 
90 	/*
91 	 * Remove PORTQ_WAIT_EVENTS flags to indicate that new events are
92 	 * available.
93 	 */
94 	portq->portq_flags &= ~PORTQ_WAIT_EVENTS;
95 	pkevp->portkev_flags |= PORT_KEV_DONEQ;		/* event enqueued */
96 
97 	if (pkevp->portkev_flags & PORT_ALLOC_CACHED) {
98 		mutex_exit(&pkevp->portkev_lock);
99 	}
100 
101 	/* Check if thread is in port_close() waiting for outstanding events */
102 	if (portq->portq_flags & PORTQ_CLOSE) {
103 		/* Check if all outstanding events are already in port queue */
104 		if (pkevp->portkev_port->port_curr <= portq->portq_nent)
105 			cv_signal(&portq->portq_closecv);
106 	}
107 
108 	if (portq->portq_getn == 0) {
109 		/*
110 		 * No thread retrieving events -> check if enough events are
111 		 * available to satify waiting threads.
112 		 */
113 		if (portq->portq_thread &&
114 		    (portq->portq_nent >= portq->portq_nget))
115 			cv_signal(&portq->portq_thread->portget_cv);
116 	}
117 
118 	if (portq->portq_flags & PORTQ_POLLIN) {
119 		portq->portq_flags &= ~PORTQ_POLLIN;
120 		mutex_exit(&portq->portq_mutex);
121 		pollwakeup(&pkevp->portkev_port->port_pollhd, POLLIN);
122 	} else {
123 		mutex_exit(&portq->portq_mutex);
124 	}
125 	return (0);
126 }
127 
128 /*
129  * The port_alloc_event() function has to be used by all event sources
130  * to request an slot for event notification.
131  * The slot reservation could be denied because of lack of resources.
132  * For that reason the event source should allocate an event slot as early
133  * as possible and be prepared to get an error code instead of the
134  * port event pointer.
135  * Al current event sources allocate an event slot during a system call
136  * entry. They return an error code to the application if an event slot
137  * could not be reserved.
138  * It is also recommended to associate the event source with the port
139  * before some other port function is used.
140  * The port argument is a file descriptor obtained by the application as
141  * a return value of port_create().
142  * Possible values of flags are:
143  * PORT_ALLOC_DEFAULT
144  *	This is the standard type of port events. port_get(n) will free this
145  *	type of event structures as soon as the events are delivered to the
146  *	application.
147  * PORT_ALLOC_PRIVATE
148  *	This type of event will be use for private use of the event source.
149  *	The port_get(n) function will deliver events of such an structure to
150  *	the application but it will not free the event structure itself.
151  *	The event source must free this structure using port_free_event().
152  * PORT_ALLOC_CACHED
153  *	This type of events is used when the event source helds an own
154  *	cache.
155  *	The port_get(n) function will deliver events of such an structure to
156  *	the application but it will not free the event structure itself.
157  *	The event source must free this structure using port_free_event().
158  */
159 int
160 port_alloc_event(int port, int flags, int source, port_kevent_t **pkevpp)
161 {
162 	port_t		*pp;
163 	file_t		*fp;
164 
165 	if ((fp = getf(port)) == NULL)
166 		return (EBADF);
167 
168 	if (fp->f_vnode->v_type != VPORT) {
169 		releasef(port);
170 		return (EBADFD);
171 	}
172 
173 	pp = VTOEP(fp->f_vnode);
174 
175 	/*
176 	 * port_max_events is controlled by the resource control
177 	 * process.port-max-events
178 	 */
179 	if (pp->port_curr >= pp->port_max_events) {
180 		releasef(port);
181 		return (EAGAIN);
182 	}
183 
184 	*pkevpp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
185 	if (*pkevpp == NULL) {
186 		releasef(port);
187 		return (ENOMEM);
188 	}
189 	atomic_add_32(&pp->port_curr, 1);
190 	mutex_init(&(*pkevpp)->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
191 	(*pkevpp)->portkev_source = source;
192 	(*pkevpp)->portkev_flags = flags;
193 	(*pkevpp)->portkev_pid = curproc->p_pid;
194 	(*pkevpp)->portkev_port = pp;
195 	(*pkevpp)->portkev_callback = NULL;
196 	releasef(port);
197 	return (0);
198 }
199 
200 /*
201  * This function is faster than the standard port_alloc_event() and
202  * can be used when the event source already allocated an event from
203  * a port.
204  */
205 int
206 port_dup_event(port_kevent_t *pkevp, port_kevent_t **pkevdupp, int flags)
207 {
208 	int	error;
209 
210 	error = port_alloc_event_local(pkevp->portkev_port,
211 	    pkevp->portkev_source, flags, pkevdupp);
212 	if (error == 0)
213 		(*pkevdupp)->portkev_pid = pkevp->portkev_pid;
214 	return (error);
215 }
216 
217 /*
218  * port_alloc_event_local() is reserved for internal use only.
219  * It is doing the same job as port_alloc_event() but with the event port
220  * pointer as the first argument.
221  * The check of the validity of the port file descriptor is skipped here.
222  */
223 int
224 port_alloc_event_local(port_t *pp, int source, int flags,
225     port_kevent_t **pkevpp)
226 {
227 	if (pp->port_curr >= pp->port_max_events)
228 		return (EAGAIN);
229 
230 	*pkevpp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP);
231 	if (*pkevpp == NULL)
232 		return (ENOMEM);
233 
234 	atomic_add_32(&pp->port_curr, 1);
235 	mutex_init(&(*pkevpp)->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
236 	(*pkevpp)->portkev_flags = flags;
237 	(*pkevpp)->portkev_port = pp;
238 	(*pkevpp)->portkev_source = source;
239 	(*pkevpp)->portkev_pid = curproc->p_pid;
240 	return (0);
241 }
242 
243 /*
244  * port_alloc_event_block() has the same functionality of port_alloc_event() +
245  * - it blocks if not enough event slots are available and
246  * - it blocks if not enough memory is available.
247  * Currently port_dispatch() is using this function to increase the
248  * reliability of event delivery for library event sources.
249  */
250 int
251 port_alloc_event_block(port_t *pp, int source, int flags,
252     port_kevent_t **pkevp)
253 {
254 	int		rval;
255 
256 	if (pp->port_curr >= pp->port_max_events) {
257 		mutex_enter(&pp->port_mutex);
258 		pp->port_flags |= PORT_EVENTS;
259 		while (pp->port_curr >= pp->port_max_events) {
260 			rval = cv_wait_sig(&pp->port_cv, &pp->port_mutex);
261 			if (rval == 0) {
262 				/* signal detected */
263 				mutex_exit(&pp->port_mutex);
264 				return (EINTR);
265 			}
266 		}
267 		mutex_exit(&pp->port_mutex);
268 	}
269 
270 	*pkevp = kmem_cache_alloc(port_control.pc_cache, KM_SLEEP);
271 	atomic_add_32(&pp->port_curr, 1);
272 	mutex_init(&(*pkevp)->portkev_lock, NULL, MUTEX_DEFAULT, NULL);
273 	(*pkevp)->portkev_flags = flags;
274 	(*pkevp)->portkev_port = pp;
275 	(*pkevp)->portkev_source = source;
276 	(*pkevp)->portkev_pid = curproc->p_pid;
277 	return (0);
278 }
279 
280 /*
281  * Take an event out of the port queue
282  */
283 static void
284 port_remove_event_doneq(port_kevent_t *pkevp, port_queue_t *portq)
285 {
286 	ASSERT(MUTEX_HELD(&portq->portq_mutex));
287 	list_remove(&portq->portq_list, pkevp);
288 	portq->portq_nent--;
289 	pkevp->portkev_flags &= ~PORT_KEV_DONEQ;
290 }
291 
292 /*
293  * The port_remove_done_event() function takes a fired event out of the
294  * port queue.
295  * Currently this function is required to cancel a fired event because
296  * the application is delivering new association data (see port_associate_fd()).
297  */
298 void
299 port_remove_done_event(port_kevent_t *pkevp)
300 {
301 	port_queue_t	*portq;
302 
303 	portq = &pkevp->portkev_port->port_queue;
304 	mutex_enter(&portq->portq_mutex);
305 	/* wait for port_get() or port_getn() */
306 	mutex_enter(&portq->portq_block_mutex);
307 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
308 		/* event still in port queue */
309 		if (portq->portq_getn) {
310 			/*
311 			 * There could be still fired events in the temp queue;
312 			 * push those events back to the port queue and
313 			 * remove requested event afterwards.
314 			 */
315 			port_push_eventq(portq);
316 		}
317 		/* now remove event from the port queue */
318 		port_remove_event_doneq(pkevp, portq);
319 	}
320 	mutex_exit(&portq->portq_block_mutex);
321 	mutex_exit(&portq->portq_mutex);
322 }
323 
324 /*
325  * Return port event back to the kmem_cache.
326  * If the event is currently in the port queue the event itself will only
327  * be set as invalid. The port_get(n) function will not deliver such events
328  * to the application and it will return them back to the kmem_cache.
329  */
330 void
331 port_free_event(port_kevent_t *pkevp)
332 {
333 	port_queue_t	*portq;
334 	port_t		*pp;
335 
336 	pp = pkevp->portkev_port;
337 	if (pp == NULL)
338 		return;
339 	if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) {
340 		port_free_event_local(pkevp, 0);
341 		return;
342 	}
343 
344 	portq = &pp->port_queue;
345 	mutex_enter(&portq->portq_mutex);
346 	mutex_enter(&portq->portq_block_mutex);
347 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
348 		pkevp->portkev_flags |= PORT_KEV_FREE;
349 		pkevp->portkev_callback = NULL;
350 		mutex_exit(&portq->portq_block_mutex);
351 		mutex_exit(&portq->portq_mutex);
352 		return;
353 	}
354 	mutex_exit(&portq->portq_block_mutex);
355 
356 	if (pkevp->portkev_flags & PORT_KEV_CACHED) {
357 		mutex_exit(&portq->portq_mutex);
358 		return;
359 	}
360 
361 	atomic_add_32(&pp->port_curr, -1);
362 	if (portq->portq_flags & PORTQ_CLOSE) {
363 		/*
364 		 * Another thread is closing the event port.
365 		 * That thread will sleep until all allocated event
366 		 * structures returned to the event port framework.
367 		 * The portq_mutex is used to synchronize the status
368 		 * of the allocated event structures (port_curr).
369 		 */
370 		if (pp->port_curr <= portq->portq_nent)
371 			cv_signal(&portq->portq_closecv);
372 	}
373 	mutex_exit(&portq->portq_mutex);
374 	port_free_event_local(pkevp, 1);
375 }
376 
377 /*
378  * This event port internal function is used by port_free_event() and
379  * other port internal functions to return event structures back to the
380  * kmem_cache.
381  */
382 void
383 port_free_event_local(port_kevent_t *pkevp, int counter)
384 {
385 	port_t	*pp = pkevp->portkev_port;
386 
387 	ASSERT(pp != NULL);
388 	if (counter == 0)
389 		atomic_add_32(&pp->port_curr, -1);
390 	pkevp->portkev_callback = NULL;
391 	pkevp->portkev_flags = 0;
392 	pkevp->portkev_port = NULL;
393 	mutex_destroy(&pkevp->portkev_lock);
394 	kmem_cache_free(port_control.pc_cache, pkevp);
395 
396 	/* Check if blocking calls are waiting for event slots */
397 	if (pp->port_flags & PORT_EVENTS) {
398 		mutex_enter(&pp->port_mutex);
399 		pp->port_flags &= ~PORT_EVENTS;
400 		cv_signal(&pp->port_cv);
401 		mutex_exit(&pp->port_mutex);
402 	}
403 
404 	/* Submit a POLLOUT event if requested */
405 	if (pp->port_queue.portq_flags & PORTQ_POLLOUT) {
406 		port_queue_t	*portq = &pp->port_queue;
407 		mutex_enter(&portq->portq_mutex);
408 		portq->portq_flags &= ~PORTQ_POLLOUT;
409 		mutex_exit(&portq->portq_mutex);
410 		pollwakeup(&pp->port_pollhd, POLLOUT);
411 	}
412 }
413 
414 /*
415  * port_init_event(port_event_t *pev, uintptr_t object, void *user,
416  *	int (*port_callback)(void *, int *, pid_t, int, void *), void *sysarg);
417  *	This function initializes most of the "wired" elements of the port
418  *	event structure. This is normally being used just after the allocation
419  *	of the port event structure.
420  *	pkevp	: pointer to the port event structure
421  *	object	: object associated with this event structure
422  *	user	: user defined pointer delivered with the association function
423  *	port_callback:
424  *		  Address of the callback function which will be called
425  *		  - just before the event is delivered to the application.
426  *		    The callback function is called in user context and can be
427  *		    used for copyouts, e.g.
428  *		  - on close() or dissociation of the event. The sub-system
429  *		    must remove immediately every existing association of
430  *		    some object with this event.
431  *	sysarg	: event source propietary data
432  */
433 void
434 port_init_event(port_kevent_t *pkevp, uintptr_t object, void *user,
435     int (*port_callback)(void *, int *, pid_t, int, void *),
436     void *sysarg)
437 {
438 	pkevp->portkev_object = object;
439 	pkevp->portkev_user = user;
440 	pkevp->portkev_callback = port_callback;
441 	pkevp->portkev_arg = sysarg;
442 }
443 
444 /*
445  * This routine removes a portfd_t from the fd cache's hash table.
446  */
447 void
448 port_pcache_remove_fd(port_fdcache_t *pcp, portfd_t *pfd)
449 {
450 	polldat_t	*lpdp;
451 	polldat_t	*cpdp;
452 	portfd_t	**bucket;
453 	polldat_t	*pdp = PFTOD(pfd);
454 
455 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
456 	bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
457 	cpdp = PFTOD(*bucket);
458 	if (pdp == cpdp) {
459 		*bucket = PDTOF(pdp->pd_hashnext);
460 		if (--pcp->pc_fdcount == 0) {
461 			/*
462 			 * signal the thread which may have blocked in
463 			 * port_close_sourcefd() on lastclose waiting
464 			 * for pc_fdcount to drop to 0.
465 			 */
466 			cv_signal(&pcp->pc_lclosecv);
467 		}
468 		kmem_free(pfd, sizeof (portfd_t));
469 		return;
470 	}
471 
472 	while (cpdp != NULL) {
473 		lpdp = cpdp;
474 		cpdp = cpdp->pd_hashnext;
475 		if (cpdp == pdp) {
476 			/* polldat struct found */
477 			lpdp->pd_hashnext = pdp->pd_hashnext;
478 			if (--pcp->pc_fdcount == 0) {
479 				/*
480 				 * signal the thread which may have blocked in
481 				 * port_close_sourcefd() on lastclose waiting
482 				 * for pc_fdcount to drop to 0.
483 				 */
484 				cv_signal(&pcp->pc_lclosecv);
485 			}
486 			break;
487 		}
488 	}
489 	ASSERT(cpdp != NULL);
490 	kmem_free(pfd, sizeof (portfd_t));
491 }
492 
493 /*
494  * The port_push_eventq() function is used to move all remaining events
495  * from the temporary queue used in port_get(n)() to the standard port
496  * queue.
497  */
498 void
499 port_push_eventq(port_queue_t *portq)
500 {
501 	/*
502 	 * Append temporary portq_get_list to the port queue. On return
503 	 * the temporary portq_get_list is empty.
504 	 */
505 	list_move_tail(&portq->portq_list, &portq->portq_get_list);
506 	portq->portq_nent += portq->portq_tnent;
507 	portq->portq_tnent = 0;
508 }
509 
510 /*
511  * The port_remove_fd_object() function frees all resources associated with
512  * delivered portfd_t structure.
513  */
514 void
515 port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
516 {
517 	port_queue_t	*portq;
518 	polldat_t	*pdp = PFTOD(pfd);
519 	port_kevent_t	*pkevp;
520 	int		error;
521 
522 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
523 	if (pdp->pd_php != NULL) {
524 		pollhead_delete(pdp->pd_php, pdp);
525 		pdp->pd_php = NULL;
526 	}
527 	pkevp =  pdp->pd_portev;
528 	portq = &pp->port_queue;
529 	mutex_enter(&portq->portq_mutex);
530 	mutex_enter(&portq->portq_block_mutex);
531 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
532 		if (portq->portq_getn && portq->portq_tnent) {
533 			/*
534 			 * move events from the temporary "get" queue
535 			 * back to the port queue
536 			 */
537 			port_push_eventq(portq);
538 		}
539 		/* cleanup merged port queue */
540 		port_remove_event_doneq(pkevp, portq);
541 	}
542 	mutex_exit(&portq->portq_block_mutex);
543 	mutex_exit(&portq->portq_mutex);
544 	if (pkevp->portkev_callback) {
545 		(void) (*pkevp->portkev_callback)(pkevp->portkev_arg,
546 		    &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE,
547 		    pkevp);
548 	}
549 	port_free_event_local(pkevp, 0);
550 
551 	/* remove polldat struct */
552 	port_pcache_remove_fd(pcp, pfd);
553 }
554 
555 /*
556  * The port_close_fd() function dissociates a file descriptor from a port
557  * and removes all allocated resources.
558  * close(2) detects in the uf_entry_t structure that the fd is associated
559  * with a port (at least one port).
560  * The fd can be associated with several ports.
561  */
562 void
563 port_close_pfd(portfd_t *pfd)
564 {
565 	port_t		*pp;
566 	port_fdcache_t	*pcp;
567 
568 	/*
569 	 * the portfd_t passed in should be for this proc.
570 	 */
571 	ASSERT(curproc->p_pid == PFTOD(pfd)->pd_portev->portkev_pid);
572 	pp = PFTOD(pfd)->pd_portev->portkev_port;
573 	pcp = pp->port_queue.portq_pcp;
574 	mutex_enter(&pcp->pc_lock);
575 	port_remove_fd_object(pfd, pp, pcp);
576 	mutex_exit(&pcp->pc_lock);
577 }
578 
579 /*
580  * The port_associate_ksource() function associates an event source with a port.
581  * On port_close() all associated sources are requested to free all local
582  * resources associated with the event port.
583  * The association of a source with a port can only be done one time. Further
584  * calls of this function will only increment the reference counter.
585  * The allocated port_source_t structure is removed from the port as soon as
586  * the reference counter becomes 0.
587  */
588 /* ARGSUSED */
589 int
590 port_associate_ksource(int port, int source, port_source_t **portsrc,
591     void (*port_src_close)(void *, int, pid_t, int), void *arg,
592     int (*port_src_associate)(port_kevent_t *, int, int, uintptr_t, void *))
593 {
594 	port_t		*pp;
595 	file_t		*fp;
596 	port_source_t	**ps;
597 	port_source_t	*pse;
598 
599 	if ((fp = getf(port)) == NULL)
600 		return (EBADF);
601 
602 	if (fp->f_vnode->v_type != VPORT) {
603 		releasef(port);
604 		return (EBADFD);
605 	}
606 	pp = VTOEP(fp->f_vnode);
607 
608 	mutex_enter(&pp->port_queue.portq_source_mutex);
609 	ps = &pp->port_queue.portq_scache[PORT_SHASH(source)];
610 	for (pse = *ps; pse != NULL; pse = pse->portsrc_next) {
611 		if (pse->portsrc_source == source)
612 			break;
613 	}
614 
615 	if (pse == NULL) {
616 		/* Create association of the event source with the port */
617 		pse = kmem_zalloc(sizeof (port_source_t), KM_NOSLEEP);
618 		if (pse == NULL) {
619 			mutex_exit(&pp->port_queue.portq_source_mutex);
620 			releasef(port);
621 			return (ENOMEM);
622 		}
623 		pse->portsrc_source = source;
624 		pse->portsrc_close = port_src_close;
625 		pse->portsrc_closearg = arg;
626 		pse->portsrc_cnt = 1;
627 		if (*ps)
628 			pse->portsrc_next = (*ps)->portsrc_next;
629 		*ps = pse;
630 	} else {
631 		/* entry already available, source is only requesting count */
632 		pse->portsrc_cnt++;
633 	}
634 	mutex_exit(&pp->port_queue.portq_source_mutex);
635 	releasef(port);
636 	if (portsrc)
637 		*portsrc = pse;
638 	return (0);
639 }
640 
641 /*
642  * The port_dissociate_ksource() function dissociates an event source from
643  * a port.
644  */
645 int
646 port_dissociate_ksource(int port, int source, port_source_t *ps)
647 {
648 	port_t		*pp;
649 	file_t		*fp;
650 	port_source_t	**psh;
651 
652 	if (ps == NULL)
653 		return (EINVAL);
654 
655 	if ((fp = getf(port)) == NULL)
656 		return (EBADF);
657 
658 	if (fp->f_vnode->v_type != VPORT) {
659 		releasef(port);
660 		return (EBADFD);
661 	}
662 	pp = VTOEP(fp->f_vnode);
663 
664 	mutex_enter(&pp->port_queue.portq_source_mutex);
665 	if (--ps->portsrc_cnt == 0) {
666 		/* last association removed -> free source structure */
667 		if (ps->portsrc_prev == NULL) {
668 			/* first entry */
669 			psh = &pp->port_queue.portq_scache[PORT_SHASH(source)];
670 			*psh = ps->portsrc_next;
671 			if (ps->portsrc_next)
672 				ps->portsrc_next->portsrc_prev = NULL;
673 		} else {
674 			ps->portsrc_prev->portsrc_next = ps->portsrc_next;
675 			if (ps->portsrc_next)
676 				ps->portsrc_next->portsrc_prev =
677 				    ps->portsrc_prev;
678 		}
679 		kmem_free(ps, sizeof (port_source_t));
680 	}
681 	mutex_exit(&pp->port_queue.portq_source_mutex);
682 	releasef(port);
683 	return (0);
684 }
685