xref: /illumos-gate/usr/src/uts/common/fs/portfs/port_fd.c (revision 2c76d751)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2022 Oxide Computer Company
26  */
27 
28 
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/stat.h>
32 #include <sys/errno.h>
33 #include <sys/kmem.h>
34 #include <sys/sysmacros.h>
35 #include <sys/debug.h>
36 #include <sys/poll_impl.h>
37 #include <sys/port_impl.h>
38 
39 #define	PORTHASH_START	256	/* start cache space for events */
40 #define	PORTHASH_MULT	2	/* growth threshold and factor */
41 
42 /* local functions */
43 static int	port_fd_callback(void *, int *, pid_t, int, void *);
44 static int	port_bind_pollhead(pollhead_t **, polldat_t *, short *);
45 static void	port_close_sourcefd(void *, int, pid_t, int);
46 static void	port_cache_insert_fd(port_fdcache_t *, polldat_t *);
47 
48 /*
49  * port_fd_callback()
50  * The event port framework uses callback functions to notify associated
51  * event sources about actions on source specific objects.
52  * The source itself defines the "arg" required to identify the object with
53  * events. In the port_fd_callback() case the "arg" is a pointer to portfd_t
54  * structure. The portfd_t structure is specific for PORT_SOURCE_FD source.
55  * The port_fd_callback() function is notified in three cases:
56  * - PORT_CALLBACK_DEFAULT
57  *	The object (fd) will be delivered to the application.
58  * - PORT_CALLBACK_DISSOCIATE
59  *	The object (fd) will be dissociated from  the port.
60  * - PORT_CALLBACK_CLOSE
61  *	The object (fd) will be dissociated from the port because the port
62  *	is being closed.
63  * A fd is shareable between processes only when
64  * - processes have the same fd id and
65  * - processes have the same fp.
66  * A fd becomes shareable:
67  * - on fork() across parent and child process and
68  * - when I_SENDFD is used to pass file descriptors between parent and child
69  *   immediately after fork() (the sender and receiver must get the same
70  *   file descriptor id).
71  * If a fd is shared between processes, all involved processes will get
72  * the same rights related to re-association of the fd with the port and
73  * retrieve of events from that fd.
74  * The process which associated the fd with a port for the first time
75  * becomes also the owner of the association. Only the owner of the
76  * association is allowed to dissociate the fd from the port.
77  */
78 /* ARGSUSED */
79 static int
port_fd_callback(void * arg,int * events,pid_t pid,int flag,void * evp)80 port_fd_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
81 {
82 	portfd_t	*pfd = (portfd_t *)arg;
83 	polldat_t	*pdp = PFTOD(pfd);
84 	port_fdcache_t	*pcp;
85 	file_t		*fp;
86 	int		error;
87 
88 	ASSERT((pdp != NULL) && (events != NULL));
89 	switch (flag) {
90 	case PORT_CALLBACK_DEFAULT:
91 		if (curproc->p_pid != pid) {
92 			/*
93 			 * Check if current process is allowed to retrieve
94 			 * events from this fd.
95 			 */
96 			fp = getf(pdp->pd_fd);
97 			if (fp == NULL) {
98 				error = EACCES; /* deny delivery of events */
99 				break;
100 			}
101 			releasef(pdp->pd_fd);
102 			if (fp != pdp->pd_fp) {
103 				error = EACCES; /* deny delivery of events */
104 				break;
105 			}
106 		}
107 		*events = pdp->pd_portev->portkev_events; /* update events */
108 		error = 0;
109 		break;
110 	case PORT_CALLBACK_DISSOCIATE:
111 		error = 0;
112 		break;
113 	case PORT_CALLBACK_CLOSE:
114 		/* remove polldat/portfd struct */
115 		pdp->pd_portev = NULL;
116 		pcp = (port_fdcache_t *)pdp->pd_pcache;
117 		mutex_enter(&pcp->pc_lock);
118 		pdp->pd_fp = NULL;
119 		pdp->pd_events = 0;
120 		polldat_disassociate(pdp);
121 		port_pcache_remove_fd(pcp, pfd);
122 		mutex_exit(&pcp->pc_lock);
123 		error = 0;
124 		break;
125 	default:
126 		error = EINVAL;
127 		break;
128 	}
129 	return (error);
130 }
131 
132 /*
133  * This routine returns a pointer to a cached poll fd entry, or NULL if it
134  * does not find it in the hash table.
135  * The fd is used as index.
136  * The fd and the fp are used to detect a valid entry.
137  * This function returns a pointer to a valid portfd_t structure only when
138  * the fd and the fp in the args match the entries in polldat_t.
139  */
140 portfd_t *
port_cache_lookup_fp(port_fdcache_t * pcp,int fd,file_t * fp)141 port_cache_lookup_fp(port_fdcache_t *pcp, int fd, file_t *fp)
142 {
143 	polldat_t	*pdp;
144 	portfd_t	**bucket;
145 
146 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
147 	bucket = PORT_FD_BUCKET(pcp, fd);
148 	pdp = PFTOD(*bucket);
149 	while (pdp != NULL) {
150 		if (pdp->pd_fd == fd && pdp->pd_fp == fp)
151 			break;
152 		pdp = pdp->pd_hashnext;
153 	}
154 	return (PDTOF(pdp));
155 }
156 
157 /*
158  * port_associate_fd()
159  * This function associates new file descriptors with a port or
160  * reactivate already associated file descriptors.
161  * The reactivation also updates the events types to be checked and the
162  * attached user pointer.
163  * Per port a cache is used to store associated file descriptors.
164  * Internally the VOP_POLL interface is used to poll for existing events.
165  * The VOP_POLL interface can also deliver a pointer to a pollhead_t structure
166  * which is used to enqueue polldat_t structures with pending events.
167  * If VOP_POLL immediately returns valid events (revents) then those events
168  * will be submitted to the event port with port_send_event().
169  * Otherwise VOP_POLL does not return events but it delivers a pointer to a
170  * pollhead_t structure. In such a case the corresponding file system behind
171  * VOP_POLL will use the pollwakeup() function to notify about existing
172  * events.
173  */
174 int
port_associate_fd(port_t * pp,int source,uintptr_t object,int events,void * user)175 port_associate_fd(port_t *pp, int source, uintptr_t object, int events,
176     void *user)
177 {
178 	port_fdcache_t	*pcp;
179 	int		fd;
180 	struct pollhead	*php = NULL;
181 	portfd_t	*pfd;
182 	polldat_t	*pdp;
183 	file_t		*fp;
184 	port_kevent_t	*pkevp;
185 	short		revents;
186 	int		error = 0;
187 	int		active;
188 
189 	pcp = pp->port_queue.portq_pcp;
190 	if (object > (uintptr_t)INT_MAX)
191 		return (EBADFD);
192 
193 	fd = object;
194 
195 	if ((fp = getf(fd)) == NULL)
196 		return (EBADFD);
197 
198 	mutex_enter(&pcp->pc_lock);
199 
200 	if (pcp->pc_hash == NULL) {
201 		/*
202 		 * This is the first time that a fd is being associated with
203 		 * the current port:
204 		 * - create PORT_SOURCE_FD cache
205 		 * - associate PORT_SOURCE_FD source with the port
206 		 */
207 		error = port_associate_ksource(pp->port_fd, PORT_SOURCE_FD,
208 		    NULL, port_close_sourcefd, pp, NULL);
209 		if (error) {
210 			mutex_exit(&pcp->pc_lock);
211 			releasef(fd);
212 			return (error);
213 		}
214 
215 		/* create polldat cache */
216 		pcp->pc_hashsize = PORTHASH_START;
217 		pcp->pc_hash = kmem_zalloc(pcp->pc_hashsize *
218 		    sizeof (portfd_t *), KM_SLEEP);
219 		pfd = NULL;
220 	} else {
221 		/* Check if the fd/fp is already associated with the port */
222 		pfd = port_cache_lookup_fp(pcp, fd, fp);
223 	}
224 
225 	if (pfd == NULL) {
226 		/*
227 		 * new entry
228 		 * Allocate a polldat_t structure per fd
229 		 * The use of the polldat_t structure to cache file descriptors
230 		 * is required to be able to share the pollwakeup() function
231 		 * with poll(2) and devpoll(4D).
232 		 */
233 		pfd = kmem_zalloc(sizeof (portfd_t), KM_SLEEP);
234 		pdp = PFTOD(pfd);
235 		pdp->pd_fd = fd;
236 		pdp->pd_fp = fp;
237 		pdp->pd_pcache = (void *)pcp;
238 
239 		/* Allocate a port event structure per fd */
240 		error = port_alloc_event_local(pp, source, PORT_ALLOC_CACHED,
241 		    &pdp->pd_portev);
242 		if (error) {
243 			kmem_free(pfd, sizeof (portfd_t));
244 			releasef(fd);
245 			mutex_exit(&pcp->pc_lock);
246 			return (error);
247 		}
248 		pkevp = pdp->pd_portev;
249 		pkevp->portkev_callback = port_fd_callback;
250 		pkevp->portkev_arg = pfd;
251 
252 		/* add portfd_t entry  to the cache */
253 		port_cache_insert_fd(pcp, pdp);
254 		pkevp->portkev_object = fd;
255 		pkevp->portkev_user = user;
256 
257 		/*
258 		 * Add current port to the file descriptor interested list
259 		 * The members of the list are notified when the file descriptor
260 		 * is closed.
261 		 */
262 		addfd_port(fd, pfd);
263 	} else {
264 		/*
265 		 * The file descriptor is already associated with the port
266 		 */
267 		pdp = PFTOD(pfd);
268 		pkevp = pdp->pd_portev;
269 
270 		/*
271 		 * Check if the re-association happens before the last
272 		 * submitted event of the file descriptor was retrieved.
273 		 * Clear the PORT_KEV_VALID flag if set. No new events
274 		 * should get submitted after this flag is cleared.
275 		 */
276 		mutex_enter(&pkevp->portkev_lock);
277 		if (pkevp->portkev_flags & PORT_KEV_VALID) {
278 			pkevp->portkev_flags &= ~PORT_KEV_VALID;
279 		}
280 		if (pkevp->portkev_flags & PORT_KEV_DONEQ) {
281 			mutex_exit(&pkevp->portkev_lock);
282 			/*
283 			 * Remove any events that where already fired
284 			 * for this fd and are still in the port queue.
285 			 */
286 			(void) port_remove_done_event(pkevp);
287 		} else {
288 			mutex_exit(&pkevp->portkev_lock);
289 		}
290 		pkevp->portkev_user = user;
291 	}
292 
293 	pfd->pfd_thread = curthread;
294 	mutex_enter(&pkevp->portkev_lock);
295 	pkevp->portkev_events = 0;	/* no fired events */
296 	pdp->pd_events = events;	/* events associated */
297 	/*
298 	 * allow new events.
299 	 */
300 	pkevp->portkev_flags |= PORT_KEV_VALID;
301 	mutex_exit(&pkevp->portkev_lock);
302 
303 	/*
304 	 * do VOP_POLL and cache this poll fd.
305 	 *
306 	 * XXX - pollrelock() logic needs to know
307 	 * which pollcache lock to grab. It'd be a
308 	 * cleaner solution if we could pass pcp as
309 	 * an arguement in VOP_POLL interface instead
310 	 * of implicitly passing it using thread_t
311 	 * struct. On the other hand, changing VOP_POLL
312 	 * interface will require all driver/file system
313 	 * poll routine to change.
314 	 */
315 	curthread->t_pollcache = (pollcache_t *)pcp;
316 	error = VOP_POLL(fp->f_vnode, events, 0, &revents, &php, NULL);
317 	curthread->t_pollcache = NULL;
318 
319 	/*
320 	 * The pc_lock can get dropped and reaquired in VOP_POLL.
321 	 * In the window pc_lock is dropped another thread in
322 	 * port_dissociate can remove the pfd from the port cache
323 	 * and free the pfd.
324 	 * It is also possible for another thread to sneak in and do a
325 	 * port_associate on the same fd during the same window.
326 	 * For both these cases return the current value of error.
327 	 * The application should take care to ensure that the threads
328 	 * do not race with each other for association and disassociation
329 	 * of the same fd.
330 	 */
331 	if (((pfd = port_cache_lookup_fp(pcp, fd, fp)) == NULL) ||
332 	    (pfd->pfd_thread != curthread)) {
333 		releasef(fd);
334 		mutex_exit(&pcp->pc_lock);
335 		return (error);
336 	}
337 
338 	/*
339 	 * To keep synchronization between VOP_POLL above and
340 	 * polldat_associate() below, it is necessary to
341 	 * call VOP_POLL() again (see port_bind_pollhead()).
342 	 */
343 	if (error) {
344 		goto errout;
345 	}
346 
347 	if (php != NULL && (pdp->pd_php != php)) {
348 		/*
349 		 * No events delivered yet.
350 		 * Bind pollhead pointer with current polldat_t structure.
351 		 * Sub-system will call pollwakeup() later with php as
352 		 * argument.
353 		 */
354 		error = port_bind_pollhead(&php, pdp, &revents);
355 		/*
356 		 * The pc_lock can get dropped and reaquired in VOP_POLL.
357 		 * In the window pc_lock is dropped another thread in
358 		 * port_dissociate can remove the pfd from the port cache
359 		 * and free the pfd.
360 		 * It is also possible for another thread to sneak in and do a
361 		 * port_associate on the same fd during the same window.
362 		 * For both these cases return the current value of error.
363 		 * The application should take care to ensure that the threads
364 		 * do not race with each other for association
365 		 * and disassociation of the same fd.
366 		 */
367 		if (((pfd = port_cache_lookup_fp(pcp, fd, fp)) == NULL) ||
368 		    (pfd->pfd_thread != curthread)) {
369 			releasef(fd);
370 			mutex_exit(&pcp->pc_lock);
371 			return (error);
372 		}
373 
374 		if (error) {
375 			goto errout;
376 		}
377 	}
378 
379 	/*
380 	 * Check if new events where detected and no events have been
381 	 * delivered. The revents was already set after the VOP_POLL
382 	 * above or it was updated in port_bind_pollhead().
383 	 */
384 	mutex_enter(&pkevp->portkev_lock);
385 	if (revents && (pkevp->portkev_flags & PORT_KEV_VALID)) {
386 		ASSERT((pkevp->portkev_flags & PORT_KEV_DONEQ) == 0);
387 		pkevp->portkev_flags &= ~PORT_KEV_VALID;
388 		revents = revents & (pdp->pd_events | POLLHUP | POLLERR);
389 		/* send events to the event port */
390 		pkevp->portkev_events = revents;
391 		/*
392 		 * port_send_event will release the portkev_lock mutex.
393 		 */
394 		port_send_event(pkevp);
395 	} else {
396 		mutex_exit(&pkevp->portkev_lock);
397 	}
398 
399 	releasef(fd);
400 	mutex_exit(&pcp->pc_lock);
401 	return (error);
402 
403 errout:
404 	delfd_port(fd, pfd);
405 	/*
406 	 * If the portkev is not valid, then an event was
407 	 * delivered.
408 	 *
409 	 * If an event was delivered and got picked up, then
410 	 * we return error = 0 treating this as a successful
411 	 * port associate call. The thread which received
412 	 * the event gets control of the object.
413 	 */
414 	active = 0;
415 	mutex_enter(&pkevp->portkev_lock);
416 	if (pkevp->portkev_flags & PORT_KEV_VALID) {
417 		pkevp->portkev_flags &= ~PORT_KEV_VALID;
418 		active = 1;
419 	}
420 	mutex_exit(&pkevp->portkev_lock);
421 
422 	if (!port_remove_fd_object(pfd, pp, pcp) && !active) {
423 		error = 0;
424 	}
425 	releasef(fd);
426 	mutex_exit(&pcp->pc_lock);
427 	return (error);
428 }
429 
430 /*
431  * The port_dissociate_fd() function dissociates the delivered file
432  * descriptor from the event port and removes already fired events.
433  * If a fd is shared between processes, all involved processes will get
434  * the same rights related to re-association of the fd with the port and
435  * retrieve of events from that fd.
436  * The process which associated the fd with a port for the first time
437  * becomes also the owner of the association. Only the owner of the
438  * association is allowed to dissociate the fd from the port.
439  */
440 int
port_dissociate_fd(port_t * pp,uintptr_t object)441 port_dissociate_fd(port_t *pp, uintptr_t object)
442 {
443 	int		fd;
444 	port_fdcache_t	*pcp;
445 	portfd_t	*pfd;
446 	file_t		*fp;
447 	int		active;
448 	port_kevent_t	*pkevp;
449 
450 	if (object > (uintptr_t)INT_MAX)
451 		return (EBADFD);
452 
453 	fd = object;
454 	pcp = pp->port_queue.portq_pcp;
455 
456 	mutex_enter(&pcp->pc_lock);
457 	if (pcp->pc_hash == NULL) {
458 		/* no file descriptor cache available */
459 		mutex_exit(&pcp->pc_lock);
460 		return (ENOENT);
461 	}
462 	if ((fp = getf(fd)) == NULL) {
463 		mutex_exit(&pcp->pc_lock);
464 		return (EBADFD);
465 	}
466 	pfd = port_cache_lookup_fp(pcp, fd, fp);
467 	if (pfd == NULL) {
468 		releasef(fd);
469 		mutex_exit(&pcp->pc_lock);
470 		return (ENOENT);
471 	}
472 	/* only association owner is allowed to remove the association */
473 	if (curproc->p_pid != PFTOD(pfd)->pd_portev->portkev_pid) {
474 		releasef(fd);
475 		mutex_exit(&pcp->pc_lock);
476 		return (EACCES);
477 	}
478 
479 	/* remove port from the file descriptor interested list */
480 	delfd_port(fd, pfd);
481 
482 	/*
483 	 * Deactivate the association. No events get posted after
484 	 * this.
485 	 */
486 	pkevp = PFTOD(pfd)->pd_portev;
487 	mutex_enter(&pkevp->portkev_lock);
488 	if (pkevp->portkev_flags & PORT_KEV_VALID) {
489 		pkevp->portkev_flags &= ~PORT_KEV_VALID;
490 		active = 1;
491 	} else {
492 		active = 0;
493 	}
494 	mutex_exit(&pkevp->portkev_lock);
495 
496 	/* remove polldat & port event structure */
497 	if (port_remove_fd_object(pfd, pp, pcp)) {
498 		/*
499 		 * An event was found and removed from the
500 		 * port done queue. This means the event has not yet
501 		 * been retrived. In this case we treat this as an active
502 		 * association.
503 		 */
504 		ASSERT(active == 0);
505 		active = 1;
506 	}
507 	releasef(fd);
508 	mutex_exit(&pcp->pc_lock);
509 
510 	/*
511 	 * Return ENOENT if there was no active association.
512 	 */
513 	return ((active ? 0 : ENOENT));
514 }
515 
516 /*
517  * Associate event port polldat_t structure with sub-system pointer to
518  * a polhead_t structure.
519  */
520 static int
port_bind_pollhead(pollhead_t ** php,polldat_t * pdp,short * revents)521 port_bind_pollhead(pollhead_t **php, polldat_t *pdp, short *revents)
522 {
523 	int		error;
524 	file_t		*fp;
525 
526 	/* break any existing association with pollhead */
527 	polldat_disassociate(pdp);
528 
529 	/*
530 	 * Before polldat_associate(), pollwakeup() will not detect a polldat
531 	 * entry in the ph_list and the event notification will disappear.
532 	 * This happens because polldat_t is still not associated with
533 	 * the pointer to the pollhead_t structure.
534 	 */
535 	polldat_associate(pdp, *php);
536 
537 	/*
538 	 * From now on event notification can be detected in pollwakeup(),
539 	 * Use VOP_POLL() again to check the current status of the event.
540 	 */
541 	fp = pdp->pd_fp;
542 	curthread->t_pollcache = (pollcache_t *)pdp->pd_pcache;
543 	error = VOP_POLL(fp->f_vnode, pdp->pd_events, 0, revents, php, NULL);
544 	curthread->t_pollcache = NULL;
545 	return (error);
546 }
547 
548 /*
549  * Grow the hash table. Rehash all the elements on the hash table.
550  */
551 static void
port_cache_grow_hashtbl(port_fdcache_t * pcp)552 port_cache_grow_hashtbl(port_fdcache_t *pcp)
553 {
554 	portfd_t	**oldtbl;
555 	polldat_t	*pdp;
556 	portfd_t	*pfd;
557 	polldat_t	*pdp1;
558 	int		oldsize;
559 	int		i;
560 
561 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
562 	oldsize = pcp->pc_hashsize;
563 	oldtbl = pcp->pc_hash;
564 	pcp->pc_hashsize *= PORTHASH_MULT;
565 	pcp->pc_hash = kmem_zalloc(pcp->pc_hashsize * sizeof (portfd_t *),
566 	    KM_SLEEP);
567 	/*
568 	 * rehash existing elements
569 	 */
570 	pcp->pc_fdcount = 0;
571 	for (i = 0; i < oldsize; i++) {
572 		pfd = oldtbl[i];
573 		pdp = PFTOD(pfd);
574 		while (pdp != NULL) {
575 			pdp1 = pdp->pd_hashnext;
576 			port_cache_insert_fd(pcp, pdp);
577 			pdp = pdp1;
578 		}
579 	}
580 	kmem_free(oldtbl, oldsize * sizeof (portfd_t *));
581 }
582 /*
583  * This routine inserts a polldat into the portcache's hash table. It
584  * may be necessary to grow the size of the hash table.
585  */
586 static void
port_cache_insert_fd(port_fdcache_t * pcp,polldat_t * pdp)587 port_cache_insert_fd(port_fdcache_t *pcp, polldat_t *pdp)
588 {
589 	portfd_t	**bucket;
590 
591 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
592 	if (pcp->pc_fdcount > (pcp->pc_hashsize * PORTHASH_MULT))
593 		port_cache_grow_hashtbl(pcp);
594 	bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd);
595 	pdp->pd_hashnext = PFTOD(*bucket);
596 	*bucket = PDTOF(pdp);
597 	pcp->pc_fdcount++;
598 }
599 
600 
601 /*
602  * The port_remove_portfd() function dissociates the port from the fd
603  * and vive versa.
604  */
605 static void
port_remove_portfd(polldat_t * pdp,port_fdcache_t * pcp)606 port_remove_portfd(polldat_t *pdp, port_fdcache_t *pcp)
607 {
608 	port_t	*pp;
609 	file_t	*fp;
610 	int	fd;
611 
612 	ASSERT(MUTEX_HELD(&pcp->pc_lock));
613 	pp = pdp->pd_portev->portkev_port;
614 	fp = getf(fd = pdp->pd_fd);
615 	/*
616 	 * If we did not get the fp for pd_fd but its portfd_t
617 	 * still exist in the cache, it means the pd_fd is being
618 	 * closed by some other thread which will also free the portfd_t.
619 	 */
620 	if (fp != NULL) {
621 		delfd_port(pdp->pd_fd, PDTOF(pdp));
622 		(void) port_remove_fd_object(PDTOF(pdp), pp, pcp);
623 		releasef(fd);
624 	}
625 }
626 
627 /*
628  * This function is used by port_close_sourcefd() to destroy the cache
629  * on last close.
630  */
631 static void
port_pcache_destroy(port_fdcache_t * pcp)632 port_pcache_destroy(port_fdcache_t *pcp)
633 {
634 	ASSERT(pcp->pc_fdcount == 0);
635 	kmem_free(pcp->pc_hash, sizeof (polldat_t *) * pcp->pc_hashsize);
636 	mutex_destroy(&pcp->pc_lock);
637 	kmem_free(pcp, sizeof (port_fdcache_t));
638 }
639 
640 /*
641  * port_close() calls this function to request the PORT_SOURCE_FD source
642  * to remove/free all resources allocated and associated with the port.
643  */
644 /* ARGSUSED */
645 static void
port_close_sourcefd(void * arg,int port,pid_t pid,int lastclose)646 port_close_sourcefd(void *arg, int port, pid_t pid, int lastclose)
647 {
648 	port_t		*pp = arg;
649 	port_fdcache_t	*pcp;
650 	portfd_t	**hashtbl;
651 	polldat_t	*pdp;
652 	polldat_t	*pdpnext;
653 	int		index;
654 
655 	pcp = pp->port_queue.portq_pcp;
656 	if (pcp == NULL)
657 		/* no cache available -> nothing to do */
658 		return;
659 
660 	mutex_enter(&pcp->pc_lock);
661 	/*
662 	 * Scan the cache and free all allocated portfd_t and port_kevent_t
663 	 * structures.
664 	 */
665 	hashtbl = pcp->pc_hash;
666 	for (index = 0; index < pcp->pc_hashsize; index++) {
667 		for (pdp = PFTOD(hashtbl[index]); pdp != NULL; pdp = pdpnext) {
668 			pdpnext = pdp->pd_hashnext;
669 			if (pid == pdp->pd_portev->portkev_pid) {
670 				/*
671 				 * remove polldat + port_event_t from cache
672 				 * only when current process did the
673 				 * association.
674 				 */
675 				port_remove_portfd(pdp, pcp);
676 			}
677 		}
678 	}
679 	if (lastclose) {
680 		/*
681 		 * Wait for all the portfd's to be freed.
682 		 * The remaining portfd_t's are the once we did not
683 		 * free in port_remove_portfd since some other thread
684 		 * is closing the fd. These threads will free the portfd_t's
685 		 * once we drop the pc_lock mutex.
686 		 */
687 		while (pcp->pc_fdcount) {
688 			(void) cv_wait_sig(&pcp->pc_lclosecv, &pcp->pc_lock);
689 		}
690 		/* event port vnode will be destroyed -> remove everything */
691 		pp->port_queue.portq_pcp = NULL;
692 	}
693 	mutex_exit(&pcp->pc_lock);
694 	/*
695 	 * last close:
696 	 * pollwakeup() can not further interact with this cache
697 	 * (all polldat structs are removed from pollhead entries).
698 	 */
699 	if (lastclose)
700 		port_pcache_destroy(pcp);
701 }
702