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