xref: /illumos-gate/usr/src/uts/common/os/port_subr.c (revision eda50310abb3984bab11856a2aca8936d26881cb)
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  * 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 void
394 port_remove_done_event(port_kevent_t *pkevp)
395 {
396 	port_queue_t	*portq;
397 
398 	portq = &pkevp->portkev_port->port_queue;
399 	mutex_enter(&portq->portq_mutex);
400 	/* wait for port_get() or port_getn() */
401 	port_block(portq);
402 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
403 		/* event still in port queue */
404 		if (portq->portq_getn) {
405 			/*
406 			 * There could be still fired events in the temp queue;
407 			 * push those events back to the port queue and
408 			 * remove requested event afterwards.
409 			 */
410 			port_push_eventq(portq);
411 		}
412 		/* now remove event from the port queue */
413 		port_remove_event_doneq(pkevp, portq);
414 	}
415 	port_unblock(portq);
416 	mutex_exit(&portq->portq_mutex);
417 }
418 
419 /*
420  * Return port event back to the kmem_cache.
421  * If the event is currently in the port queue the event itself will only
422  * be set as invalid. The port_get(n) function will not deliver such events
423  * to the application and it will return them back to the kmem_cache.
424  */
425 void
426 port_free_event(port_kevent_t *pkevp)
427 {
428 	port_queue_t	*portq;
429 	port_t		*pp;
430 
431 	pp = pkevp->portkev_port;
432 	if (pp == NULL)
433 		return;
434 	if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) {
435 		port_free_event_local(pkevp, 0);
436 		return;
437 	}
438 
439 	portq = &pp->port_queue;
440 	mutex_enter(&portq->portq_mutex);
441 	port_block(portq);
442 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
443 		pkevp->portkev_flags |= PORT_KEV_FREE;
444 		pkevp->portkev_callback = NULL;
445 		port_unblock(portq);
446 		mutex_exit(&portq->portq_mutex);
447 		return;
448 	}
449 	port_unblock(portq);
450 
451 	if (pkevp->portkev_flags & PORT_KEV_CACHED) {
452 		mutex_exit(&portq->portq_mutex);
453 		return;
454 	}
455 
456 	if (--pp->port_curr < pp->port_max_events)
457 		cv_signal(&pp->port_cv);
458 	if (portq->portq_flags & PORTQ_CLOSE) {
459 		/*
460 		 * Another thread is closing the event port.
461 		 * That thread will sleep until all allocated event
462 		 * structures returned to the event port framework.
463 		 * The portq_mutex is used to synchronize the status
464 		 * of the allocated event structures (port_curr).
465 		 */
466 		if (pp->port_curr <= portq->portq_nent)
467 			cv_signal(&portq->portq_closecv);
468 	}
469 	mutex_exit(&portq->portq_mutex);
470 	port_free_event_local(pkevp, 1);
471 }
472 
473 /*
474  * This event port internal function is used by port_free_event() and
475  * other port internal functions to return event structures back to the
476  * kmem_cache.
477  */
478 void
479 port_free_event_local(port_kevent_t *pkevp, int counter)
480 {
481 	port_t *pp = pkevp->portkev_port;
482 	port_queue_t *portq = &pp->port_queue;
483 	int wakeup;
484 
485 	pkevp->portkev_callback = NULL;
486 	pkevp->portkev_flags = 0;
487 	pkevp->portkev_port = NULL;
488 	mutex_destroy(&pkevp->portkev_lock);
489 	kmem_cache_free(port_control.pc_cache, pkevp);
490 
491 	mutex_enter(&portq->portq_mutex);
492 	if (counter == 0) {
493 		if (--pp->port_curr < pp->port_max_events)
494 			cv_signal(&pp->port_cv);
495 	}
496 	wakeup = (portq->portq_flags & PORTQ_POLLOUT);
497 	portq->portq_flags &= ~PORTQ_POLLOUT;
498 	mutex_exit(&portq->portq_mutex);
499 
500 	/* Submit a POLLOUT event if requested */
501 	if (wakeup)
502 		pollwakeup(&pp->port_pollhd, POLLOUT);
503 }
504 
505 /*
506  * port_init_event(port_event_t *pev, uintptr_t object, void *user,
507  *	int (*port_callback)(void *, int *, pid_t, int, void *), void *sysarg);
508  *	This function initializes most of the "wired" elements of the port
509  *	event structure. This is normally being used just after the allocation
510  *	of the port event structure.
511  *	pkevp	: pointer to the port event structure
512  *	object	: object associated with this event structure
513  *	user	: user defined pointer delivered with the association function
514  *	port_callback:
515  *		  Address of the callback function which will be called
516  *		  - just before the event is delivered to the application.
517  *		    The callback function is called in user context and can be
518  *		    used for copyouts, e.g.
519  *		  - on close() or dissociation of the event. The sub-system
520  *		    must remove immediately every existing association of
521  *		    some object with this event.
522  *	sysarg	: event source propietary data
523  */
524 void
525 port_init_event(port_kevent_t *pkevp, uintptr_t object, void *user,
526     int (*port_callback)(void *, int *, pid_t, int, void *),
527     void *sysarg)
528 {
529 	pkevp->portkev_object = object;
530 	pkevp->portkev_user = user;
531 	pkevp->portkev_callback = port_callback;
532 	pkevp->portkev_arg = sysarg;
533 }
534 
535 /*
536  * This routine removes a portfd_t from the fd cache's hash table.
537  */
538 void
539 port_pcache_remove_fd(port_fdcache_t *pcp, portfd_t *pfd)
540 {
541 	polldat_t	*lpdp;
542 	polldat_t	*cpdp;
543 	portfd_t	**bucket;
544 	polldat_t	*pdp = PFTOD(pfd);
545 
546 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
547 	bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
548 	cpdp = PFTOD(*bucket);
549 	if (pdp == cpdp) {
550 		*bucket = PDTOF(pdp->pd_hashnext);
551 		if (--pcp->pc_fdcount == 0) {
552 			/*
553 			 * signal the thread which may have blocked in
554 			 * port_close_sourcefd() on lastclose waiting
555 			 * for pc_fdcount to drop to 0.
556 			 */
557 			cv_signal(&pcp->pc_lclosecv);
558 		}
559 		kmem_free(pfd, sizeof (portfd_t));
560 		return;
561 	}
562 
563 	while (cpdp != NULL) {
564 		lpdp = cpdp;
565 		cpdp = cpdp->pd_hashnext;
566 		if (cpdp == pdp) {
567 			/* polldat struct found */
568 			lpdp->pd_hashnext = pdp->pd_hashnext;
569 			if (--pcp->pc_fdcount == 0) {
570 				/*
571 				 * signal the thread which may have blocked in
572 				 * port_close_sourcefd() on lastclose waiting
573 				 * for pc_fdcount to drop to 0.
574 				 */
575 				cv_signal(&pcp->pc_lclosecv);
576 			}
577 			break;
578 		}
579 	}
580 	ASSERT(cpdp != NULL);
581 	kmem_free(pfd, sizeof (portfd_t));
582 }
583 
584 /*
585  * The port_push_eventq() function is used to move all remaining events
586  * from the temporary queue used in port_get(n)() to the standard port
587  * queue.
588  */
589 void
590 port_push_eventq(port_queue_t *portq)
591 {
592 	/*
593 	 * Append temporary portq_get_list to the port queue. On return
594 	 * the temporary portq_get_list is empty.
595 	 */
596 	list_move_tail(&portq->portq_list, &portq->portq_get_list);
597 	portq->portq_nent += portq->portq_tnent;
598 	portq->portq_tnent = 0;
599 }
600 
601 /*
602  * The port_remove_fd_object() function frees all resources associated with
603  * delivered portfd_t structure.
604  */
605 void
606 port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp)
607 {
608 	port_queue_t	*portq;
609 	polldat_t	*pdp = PFTOD(pfd);
610 	port_kevent_t	*pkevp;
611 	int		error;
612 
613 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
614 	if (pdp->pd_php != NULL) {
615 		pollhead_delete(pdp->pd_php, pdp);
616 		pdp->pd_php = NULL;
617 	}
618 	pkevp =  pdp->pd_portev;
619 	portq = &pp->port_queue;
620 	mutex_enter(&portq->portq_mutex);
621 	port_block(portq);
622 	if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
623 		if (portq->portq_getn && portq->portq_tnent) {
624 			/*
625 			 * move events from the temporary "get" queue
626 			 * back to the port queue
627 			 */
628 			port_push_eventq(portq);
629 		}
630 		/* cleanup merged port queue */
631 		port_remove_event_doneq(pkevp, portq);
632 	}
633 	port_unblock(portq);
634 	mutex_exit(&portq->portq_mutex);
635 	if (pkevp->portkev_callback) {
636 		(void) (*pkevp->portkev_callback)(pkevp->portkev_arg,
637 		    &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE,
638 		    pkevp);
639 	}
640 	port_free_event_local(pkevp, 0);
641 
642 	/* remove polldat struct */
643 	port_pcache_remove_fd(pcp, pfd);
644 }
645 
646 /*
647  * The port_close_fd() function dissociates a file descriptor from a port
648  * and removes all allocated resources.
649  * close(2) detects in the uf_entry_t structure that the fd is associated
650  * with a port (at least one port).
651  * The fd can be associated with several ports.
652  */
653 void
654 port_close_pfd(portfd_t *pfd)
655 {
656 	port_t		*pp;
657 	port_fdcache_t	*pcp;
658 
659 	/*
660 	 * the portfd_t passed in should be for this proc.
661 	 */
662 	ASSERT(curproc->p_pid == PFTOD(pfd)->pd_portev->portkev_pid);
663 	pp = PFTOD(pfd)->pd_portev->portkev_port;
664 	pcp = pp->port_queue.portq_pcp;
665 	mutex_enter(&pcp->pc_lock);
666 	port_remove_fd_object(pfd, pp, pcp);
667 	mutex_exit(&pcp->pc_lock);
668 }
669 
670 /*
671  * The port_associate_ksource() function associates an event source with a port.
672  * On port_close() all associated sources are requested to free all local
673  * resources associated with the event port.
674  * The association of a source with a port can only be done one time. Further
675  * calls of this function will only increment the reference counter.
676  * The allocated port_source_t structure is removed from the port as soon as
677  * the reference counter becomes 0.
678  */
679 /* ARGSUSED */
680 int
681 port_associate_ksource(int port, int source, port_source_t **portsrc,
682     void (*port_src_close)(void *, int, pid_t, int), void *arg,
683     int (*port_src_associate)(port_kevent_t *, int, int, uintptr_t, void *))
684 {
685 	port_t		*pp;
686 	file_t		*fp;
687 	port_source_t	**ps;
688 	port_source_t	*pse;
689 
690 	if ((fp = getf(port)) == NULL)
691 		return (EBADF);
692 
693 	if (fp->f_vnode->v_type != VPORT) {
694 		releasef(port);
695 		return (EBADFD);
696 	}
697 	pp = VTOEP(fp->f_vnode);
698 
699 	mutex_enter(&pp->port_queue.portq_source_mutex);
700 	ps = &pp->port_queue.portq_scache[PORT_SHASH(source)];
701 	for (pse = *ps; pse != NULL; pse = pse->portsrc_next) {
702 		if (pse->portsrc_source == source)
703 			break;
704 	}
705 
706 	if (pse == NULL) {
707 		/* Create association of the event source with the port */
708 		pse = kmem_zalloc(sizeof (port_source_t), KM_NOSLEEP);
709 		if (pse == NULL) {
710 			mutex_exit(&pp->port_queue.portq_source_mutex);
711 			releasef(port);
712 			return (ENOMEM);
713 		}
714 		pse->portsrc_source = source;
715 		pse->portsrc_close = port_src_close;
716 		pse->portsrc_closearg = arg;
717 		pse->portsrc_cnt = 1;
718 		if (*ps)
719 			pse->portsrc_next = (*ps)->portsrc_next;
720 		*ps = pse;
721 	} else {
722 		/* entry already available, source is only requesting count */
723 		pse->portsrc_cnt++;
724 	}
725 	mutex_exit(&pp->port_queue.portq_source_mutex);
726 	releasef(port);
727 	if (portsrc)
728 		*portsrc = pse;
729 	return (0);
730 }
731 
732 /*
733  * The port_dissociate_ksource() function dissociates an event source from
734  * a port.
735  */
736 int
737 port_dissociate_ksource(int port, int source, port_source_t *ps)
738 {
739 	port_t		*pp;
740 	file_t		*fp;
741 	port_source_t	**psh;
742 
743 	if (ps == NULL)
744 		return (EINVAL);
745 
746 	if ((fp = getf(port)) == NULL)
747 		return (EBADF);
748 
749 	if (fp->f_vnode->v_type != VPORT) {
750 		releasef(port);
751 		return (EBADFD);
752 	}
753 	pp = VTOEP(fp->f_vnode);
754 
755 	mutex_enter(&pp->port_queue.portq_source_mutex);
756 	if (--ps->portsrc_cnt == 0) {
757 		/* last association removed -> free source structure */
758 		if (ps->portsrc_prev == NULL) {
759 			/* first entry */
760 			psh = &pp->port_queue.portq_scache[PORT_SHASH(source)];
761 			*psh = ps->portsrc_next;
762 			if (ps->portsrc_next)
763 				ps->portsrc_next->portsrc_prev = NULL;
764 		} else {
765 			ps->portsrc_prev->portsrc_next = ps->portsrc_next;
766 			if (ps->portsrc_next)
767 				ps->portsrc_next->portsrc_prev =
768 				    ps->portsrc_prev;
769 		}
770 		kmem_free(ps, sizeof (port_source_t));
771 	}
772 	mutex_exit(&pp->port_queue.portq_source_mutex);
773 	releasef(port);
774 	return (0);
775 }
776