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