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