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