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