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 2006 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 * The port_send_event() function is used by all event sources to submit 87 * trigerred events to a port. All the data required for the event management 88 * is already stored in the port_kevent_t structure. 89 * The event port internal data is stored in the port_kevent_t structure 90 * during the allocation time (see port_alloc_event()). The data related to 91 * the event itself and to the event source management is stored in the 92 * port_kevent_t structure between the allocation time and submit time 93 * (see port_init_event()). 94 * 95 * This function is often called from interrupt level. 96 */ 97 void 98 port_send_event(port_kevent_t *pkevp) 99 { 100 port_queue_t *portq; 101 102 portq = &pkevp->portkev_port->port_queue; 103 mutex_enter(&portq->portq_mutex); 104 105 if (pkevp->portkev_flags & PORT_KEV_DONEQ) { 106 /* Event already in the port queue */ 107 if (pkevp->portkev_flags & PORT_ALLOC_CACHED) { 108 mutex_exit(&pkevp->portkev_lock); 109 } 110 mutex_exit(&portq->portq_mutex); 111 return; 112 } 113 114 /* put event in the port queue */ 115 list_insert_tail(&portq->portq_list, pkevp); 116 portq->portq_nent++; 117 118 /* 119 * Remove the PORTQ_WAIT_EVENTS flag to indicate 120 * that new events are available. 121 */ 122 portq->portq_flags &= ~PORTQ_WAIT_EVENTS; 123 pkevp->portkev_flags |= PORT_KEV_DONEQ; /* event enqueued */ 124 125 if (pkevp->portkev_flags & PORT_ALLOC_CACHED) { 126 mutex_exit(&pkevp->portkev_lock); 127 } 128 129 /* Check if thread is in port_close() waiting for outstanding events */ 130 if (portq->portq_flags & PORTQ_CLOSE) { 131 /* Check if all outstanding events are already in port queue */ 132 if (pkevp->portkev_port->port_curr <= portq->portq_nent) 133 cv_signal(&portq->portq_closecv); 134 } 135 136 if (portq->portq_getn == 0) { 137 /* 138 * No thread retrieving events -> check if enough events are 139 * available to satify waiting threads. 140 */ 141 if (portq->portq_thread && 142 (portq->portq_nent >= portq->portq_nget)) 143 cv_signal(&portq->portq_thread->portget_cv); 144 } 145 146 if (portq->portq_flags & PORTQ_POLLIN) { 147 portq->portq_flags &= ~PORTQ_POLLIN; 148 mutex_exit(&portq->portq_mutex); 149 pollwakeup(&pkevp->portkev_port->port_pollhd, POLLIN); 150 } else { 151 mutex_exit(&portq->portq_mutex); 152 } 153 } 154 155 /* 156 * The port_alloc_event() function has to be used by all event sources 157 * to request an slot for event notification. 158 * The slot reservation could be denied because of lack of resources. 159 * For that reason the event source should allocate an event slot as early 160 * as possible and be prepared to get an error code instead of the 161 * port event pointer. 162 * Al current event sources allocate an event slot during a system call 163 * entry. They return an error code to the application if an event slot 164 * could not be reserved. 165 * It is also recommended to associate the event source with the port 166 * before some other port function is used. 167 * The port argument is a file descriptor obtained by the application as 168 * a return value of port_create(). 169 * Possible values of flags are: 170 * PORT_ALLOC_DEFAULT 171 * This is the standard type of port events. port_get(n) will free this 172 * type of event structures as soon as the events are delivered to the 173 * application. 174 * PORT_ALLOC_PRIVATE 175 * This type of event will be use for private use of the event source. 176 * The port_get(n) function will deliver events of such an structure to 177 * the application but it will not free the event structure itself. 178 * The event source must free this structure using port_free_event(). 179 * PORT_ALLOC_CACHED 180 * This type of events is used when the event source helds an own 181 * cache. 182 * The port_get(n) function will deliver events of such an structure to 183 * the application but it will not free the event structure itself. 184 * The event source must free this structure using port_free_event(). 185 */ 186 int 187 port_alloc_event(int port, int flags, int source, port_kevent_t **pkevpp) 188 { 189 port_t *pp; 190 file_t *fp; 191 port_kevent_t *pkevp; 192 193 if ((fp = getf(port)) == NULL) 194 return (EBADF); 195 196 if (fp->f_vnode->v_type != VPORT) { 197 releasef(port); 198 return (EBADFD); 199 } 200 201 pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP); 202 if (pkevp == NULL) { 203 releasef(port); 204 return (ENOMEM); 205 } 206 207 /* 208 * port_max_events is controlled by the resource control 209 * process.port-max-events 210 */ 211 pp = VTOEP(fp->f_vnode); 212 mutex_enter(&pp->port_queue.portq_mutex); 213 if (pp->port_curr >= pp->port_max_events) { 214 mutex_exit(&pp->port_queue.portq_mutex); 215 kmem_cache_free(port_control.pc_cache, pkevp); 216 releasef(port); 217 return (EAGAIN); 218 } 219 pp->port_curr++; 220 mutex_exit(&pp->port_queue.portq_mutex); 221 222 bzero(pkevp, sizeof (port_kevent_t)); 223 mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL); 224 pkevp->portkev_source = source; 225 pkevp->portkev_flags = flags; 226 pkevp->portkev_pid = curproc->p_pid; 227 pkevp->portkev_port = pp; 228 *pkevpp = pkevp; 229 releasef(port); 230 return (0); 231 } 232 233 /* 234 * This function is faster than the standard port_alloc_event() and 235 * can be used when the event source already allocated an event from 236 * a port. 237 */ 238 int 239 port_dup_event(port_kevent_t *pkevp, port_kevent_t **pkevdupp, int flags) 240 { 241 int error; 242 243 error = port_alloc_event_local(pkevp->portkev_port, 244 pkevp->portkev_source, flags, pkevdupp); 245 if (error == 0) 246 (*pkevdupp)->portkev_pid = pkevp->portkev_pid; 247 return (error); 248 } 249 250 /* 251 * port_alloc_event_local() is reserved for internal use only. 252 * It is doing the same job as port_alloc_event() but with the event port 253 * pointer as the first argument. 254 * The check of the validity of the port file descriptor is skipped here. 255 */ 256 int 257 port_alloc_event_local(port_t *pp, int source, int flags, 258 port_kevent_t **pkevpp) 259 { 260 port_kevent_t *pkevp; 261 262 pkevp = kmem_cache_alloc(port_control.pc_cache, KM_NOSLEEP); 263 if (pkevp == NULL) 264 return (ENOMEM); 265 266 mutex_enter(&pp->port_queue.portq_mutex); 267 if (pp->port_curr >= pp->port_max_events) { 268 mutex_exit(&pp->port_queue.portq_mutex); 269 kmem_cache_free(port_control.pc_cache, pkevp); 270 return (EAGAIN); 271 } 272 pp->port_curr++; 273 mutex_exit(&pp->port_queue.portq_mutex); 274 275 bzero(pkevp, sizeof (port_kevent_t)); 276 mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL); 277 pkevp->portkev_flags = flags; 278 pkevp->portkev_port = pp; 279 pkevp->portkev_source = source; 280 pkevp->portkev_pid = curproc->p_pid; 281 *pkevpp = pkevp; 282 return (0); 283 } 284 285 /* 286 * port_alloc_event_block() has the same functionality of port_alloc_event() + 287 * - it blocks if not enough event slots are available and 288 * - it blocks if not enough memory is available. 289 * Currently port_dispatch() is using this function to increase the 290 * reliability of event delivery for library event sources. 291 */ 292 int 293 port_alloc_event_block(port_t *pp, int source, int flags, 294 port_kevent_t **pkevpp) 295 { 296 port_kevent_t *pkevp = 297 kmem_cache_alloc(port_control.pc_cache, KM_SLEEP); 298 299 mutex_enter(&pp->port_queue.portq_mutex); 300 while (pp->port_curr >= pp->port_max_events) { 301 if (!cv_wait_sig(&pp->port_cv, &pp->port_queue.portq_mutex)) { 302 /* signal detected */ 303 mutex_exit(&pp->port_queue.portq_mutex); 304 kmem_cache_free(port_control.pc_cache, pkevp); 305 return (EINTR); 306 } 307 } 308 pp->port_curr++; 309 mutex_exit(&pp->port_queue.portq_mutex); 310 311 bzero(pkevp, sizeof (port_kevent_t)); 312 mutex_init(&pkevp->portkev_lock, NULL, MUTEX_DEFAULT, NULL); 313 pkevp->portkev_flags = flags; 314 pkevp->portkev_port = pp; 315 pkevp->portkev_source = source; 316 pkevp->portkev_pid = curproc->p_pid; 317 *pkevpp = pkevp; 318 return (0); 319 } 320 321 /* 322 * Take an event out of the port queue 323 */ 324 static void 325 port_remove_event_doneq(port_kevent_t *pkevp, port_queue_t *portq) 326 { 327 ASSERT(MUTEX_HELD(&portq->portq_mutex)); 328 list_remove(&portq->portq_list, pkevp); 329 portq->portq_nent--; 330 pkevp->portkev_flags &= ~PORT_KEV_DONEQ; 331 } 332 333 /* 334 * The port_remove_done_event() function takes a fired event out of the 335 * port queue. 336 * Currently this function is required to cancel a fired event because 337 * the application is delivering new association data (see port_associate_fd()). 338 */ 339 void 340 port_remove_done_event(port_kevent_t *pkevp) 341 { 342 port_queue_t *portq; 343 344 portq = &pkevp->portkev_port->port_queue; 345 mutex_enter(&portq->portq_mutex); 346 /* wait for port_get() or port_getn() */ 347 port_block(portq); 348 if (pkevp->portkev_flags & PORT_KEV_DONEQ) { 349 /* event still in port queue */ 350 if (portq->portq_getn) { 351 /* 352 * There could be still fired events in the temp queue; 353 * push those events back to the port queue and 354 * remove requested event afterwards. 355 */ 356 port_push_eventq(portq); 357 } 358 /* now remove event from the port queue */ 359 port_remove_event_doneq(pkevp, portq); 360 } 361 port_unblock(portq); 362 mutex_exit(&portq->portq_mutex); 363 } 364 365 /* 366 * Return port event back to the kmem_cache. 367 * If the event is currently in the port queue the event itself will only 368 * be set as invalid. The port_get(n) function will not deliver such events 369 * to the application and it will return them back to the kmem_cache. 370 */ 371 void 372 port_free_event(port_kevent_t *pkevp) 373 { 374 port_queue_t *portq; 375 port_t *pp; 376 377 pp = pkevp->portkev_port; 378 if (pp == NULL) 379 return; 380 if (pkevp->portkev_flags & PORT_ALLOC_PRIVATE) { 381 port_free_event_local(pkevp, 0); 382 return; 383 } 384 385 portq = &pp->port_queue; 386 mutex_enter(&portq->portq_mutex); 387 port_block(portq); 388 if (pkevp->portkev_flags & PORT_KEV_DONEQ) { 389 pkevp->portkev_flags |= PORT_KEV_FREE; 390 pkevp->portkev_callback = NULL; 391 port_unblock(portq); 392 mutex_exit(&portq->portq_mutex); 393 return; 394 } 395 port_unblock(portq); 396 397 if (pkevp->portkev_flags & PORT_KEV_CACHED) { 398 mutex_exit(&portq->portq_mutex); 399 return; 400 } 401 402 if (--pp->port_curr < pp->port_max_events) 403 cv_signal(&pp->port_cv); 404 if (portq->portq_flags & PORTQ_CLOSE) { 405 /* 406 * Another thread is closing the event port. 407 * That thread will sleep until all allocated event 408 * structures returned to the event port framework. 409 * The portq_mutex is used to synchronize the status 410 * of the allocated event structures (port_curr). 411 */ 412 if (pp->port_curr <= portq->portq_nent) 413 cv_signal(&portq->portq_closecv); 414 } 415 mutex_exit(&portq->portq_mutex); 416 port_free_event_local(pkevp, 1); 417 } 418 419 /* 420 * This event port internal function is used by port_free_event() and 421 * other port internal functions to return event structures back to the 422 * kmem_cache. 423 */ 424 void 425 port_free_event_local(port_kevent_t *pkevp, int counter) 426 { 427 port_t *pp = pkevp->portkev_port; 428 port_queue_t *portq = &pp->port_queue; 429 int wakeup; 430 431 pkevp->portkev_callback = NULL; 432 pkevp->portkev_flags = 0; 433 pkevp->portkev_port = NULL; 434 mutex_destroy(&pkevp->portkev_lock); 435 kmem_cache_free(port_control.pc_cache, pkevp); 436 437 mutex_enter(&portq->portq_mutex); 438 if (counter == 0) { 439 if (--pp->port_curr < pp->port_max_events) 440 cv_signal(&pp->port_cv); 441 } 442 wakeup = (portq->portq_flags & PORTQ_POLLOUT); 443 portq->portq_flags &= ~PORTQ_POLLOUT; 444 mutex_exit(&portq->portq_mutex); 445 446 /* Submit a POLLOUT event if requested */ 447 if (wakeup) 448 pollwakeup(&pp->port_pollhd, POLLOUT); 449 } 450 451 /* 452 * port_init_event(port_event_t *pev, uintptr_t object, void *user, 453 * int (*port_callback)(void *, int *, pid_t, int, void *), void *sysarg); 454 * This function initializes most of the "wired" elements of the port 455 * event structure. This is normally being used just after the allocation 456 * of the port event structure. 457 * pkevp : pointer to the port event structure 458 * object : object associated with this event structure 459 * user : user defined pointer delivered with the association function 460 * port_callback: 461 * Address of the callback function which will be called 462 * - just before the event is delivered to the application. 463 * The callback function is called in user context and can be 464 * used for copyouts, e.g. 465 * - on close() or dissociation of the event. The sub-system 466 * must remove immediately every existing association of 467 * some object with this event. 468 * sysarg : event source propietary data 469 */ 470 void 471 port_init_event(port_kevent_t *pkevp, uintptr_t object, void *user, 472 int (*port_callback)(void *, int *, pid_t, int, void *), 473 void *sysarg) 474 { 475 pkevp->portkev_object = object; 476 pkevp->portkev_user = user; 477 pkevp->portkev_callback = port_callback; 478 pkevp->portkev_arg = sysarg; 479 } 480 481 /* 482 * This routine removes a portfd_t from the fd cache's hash table. 483 */ 484 void 485 port_pcache_remove_fd(port_fdcache_t *pcp, portfd_t *pfd) 486 { 487 polldat_t *lpdp; 488 polldat_t *cpdp; 489 portfd_t **bucket; 490 polldat_t *pdp = PFTOD(pfd); 491 492 ASSERT(MUTEX_HELD(&pcp->pc_lock)); 493 bucket = PORT_FD_BUCKET(pcp, pdp->pd_fd); 494 cpdp = PFTOD(*bucket); 495 if (pdp == cpdp) { 496 *bucket = PDTOF(pdp->pd_hashnext); 497 if (--pcp->pc_fdcount == 0) { 498 /* 499 * signal the thread which may have blocked in 500 * port_close_sourcefd() on lastclose waiting 501 * for pc_fdcount to drop to 0. 502 */ 503 cv_signal(&pcp->pc_lclosecv); 504 } 505 kmem_free(pfd, sizeof (portfd_t)); 506 return; 507 } 508 509 while (cpdp != NULL) { 510 lpdp = cpdp; 511 cpdp = cpdp->pd_hashnext; 512 if (cpdp == pdp) { 513 /* polldat struct found */ 514 lpdp->pd_hashnext = pdp->pd_hashnext; 515 if (--pcp->pc_fdcount == 0) { 516 /* 517 * signal the thread which may have blocked in 518 * port_close_sourcefd() on lastclose waiting 519 * for pc_fdcount to drop to 0. 520 */ 521 cv_signal(&pcp->pc_lclosecv); 522 } 523 break; 524 } 525 } 526 ASSERT(cpdp != NULL); 527 kmem_free(pfd, sizeof (portfd_t)); 528 } 529 530 /* 531 * The port_push_eventq() function is used to move all remaining events 532 * from the temporary queue used in port_get(n)() to the standard port 533 * queue. 534 */ 535 void 536 port_push_eventq(port_queue_t *portq) 537 { 538 /* 539 * Append temporary portq_get_list to the port queue. On return 540 * the temporary portq_get_list is empty. 541 */ 542 list_move_tail(&portq->portq_list, &portq->portq_get_list); 543 portq->portq_nent += portq->portq_tnent; 544 portq->portq_tnent = 0; 545 } 546 547 /* 548 * The port_remove_fd_object() function frees all resources associated with 549 * delivered portfd_t structure. 550 */ 551 void 552 port_remove_fd_object(portfd_t *pfd, port_t *pp, port_fdcache_t *pcp) 553 { 554 port_queue_t *portq; 555 polldat_t *pdp = PFTOD(pfd); 556 port_kevent_t *pkevp; 557 int error; 558 559 ASSERT(MUTEX_HELD(&pcp->pc_lock)); 560 if (pdp->pd_php != NULL) { 561 pollhead_delete(pdp->pd_php, pdp); 562 pdp->pd_php = NULL; 563 } 564 pkevp = pdp->pd_portev; 565 portq = &pp->port_queue; 566 mutex_enter(&portq->portq_mutex); 567 port_block(portq); 568 if (pkevp->portkev_flags & PORT_KEV_DONEQ) { 569 if (portq->portq_getn && portq->portq_tnent) { 570 /* 571 * move events from the temporary "get" queue 572 * back to the port queue 573 */ 574 port_push_eventq(portq); 575 } 576 /* cleanup merged port queue */ 577 port_remove_event_doneq(pkevp, portq); 578 } 579 port_unblock(portq); 580 mutex_exit(&portq->portq_mutex); 581 if (pkevp->portkev_callback) { 582 (void) (*pkevp->portkev_callback)(pkevp->portkev_arg, 583 &error, pkevp->portkev_pid, PORT_CALLBACK_DISSOCIATE, 584 pkevp); 585 } 586 port_free_event_local(pkevp, 0); 587 588 /* remove polldat struct */ 589 port_pcache_remove_fd(pcp, pfd); 590 } 591 592 /* 593 * The port_close_fd() function dissociates a file descriptor from a port 594 * and removes all allocated resources. 595 * close(2) detects in the uf_entry_t structure that the fd is associated 596 * with a port (at least one port). 597 * The fd can be associated with several ports. 598 */ 599 void 600 port_close_pfd(portfd_t *pfd) 601 { 602 port_t *pp; 603 port_fdcache_t *pcp; 604 605 /* 606 * the portfd_t passed in should be for this proc. 607 */ 608 ASSERT(curproc->p_pid == PFTOD(pfd)->pd_portev->portkev_pid); 609 pp = PFTOD(pfd)->pd_portev->portkev_port; 610 pcp = pp->port_queue.portq_pcp; 611 mutex_enter(&pcp->pc_lock); 612 port_remove_fd_object(pfd, pp, pcp); 613 mutex_exit(&pcp->pc_lock); 614 } 615 616 /* 617 * The port_associate_ksource() function associates an event source with a port. 618 * On port_close() all associated sources are requested to free all local 619 * resources associated with the event port. 620 * The association of a source with a port can only be done one time. Further 621 * calls of this function will only increment the reference counter. 622 * The allocated port_source_t structure is removed from the port as soon as 623 * the reference counter becomes 0. 624 */ 625 /* ARGSUSED */ 626 int 627 port_associate_ksource(int port, int source, port_source_t **portsrc, 628 void (*port_src_close)(void *, int, pid_t, int), void *arg, 629 int (*port_src_associate)(port_kevent_t *, int, int, uintptr_t, void *)) 630 { 631 port_t *pp; 632 file_t *fp; 633 port_source_t **ps; 634 port_source_t *pse; 635 636 if ((fp = getf(port)) == NULL) 637 return (EBADF); 638 639 if (fp->f_vnode->v_type != VPORT) { 640 releasef(port); 641 return (EBADFD); 642 } 643 pp = VTOEP(fp->f_vnode); 644 645 mutex_enter(&pp->port_queue.portq_source_mutex); 646 ps = &pp->port_queue.portq_scache[PORT_SHASH(source)]; 647 for (pse = *ps; pse != NULL; pse = pse->portsrc_next) { 648 if (pse->portsrc_source == source) 649 break; 650 } 651 652 if (pse == NULL) { 653 /* Create association of the event source with the port */ 654 pse = kmem_zalloc(sizeof (port_source_t), KM_NOSLEEP); 655 if (pse == NULL) { 656 mutex_exit(&pp->port_queue.portq_source_mutex); 657 releasef(port); 658 return (ENOMEM); 659 } 660 pse->portsrc_source = source; 661 pse->portsrc_close = port_src_close; 662 pse->portsrc_closearg = arg; 663 pse->portsrc_cnt = 1; 664 if (*ps) 665 pse->portsrc_next = (*ps)->portsrc_next; 666 *ps = pse; 667 } else { 668 /* entry already available, source is only requesting count */ 669 pse->portsrc_cnt++; 670 } 671 mutex_exit(&pp->port_queue.portq_source_mutex); 672 releasef(port); 673 if (portsrc) 674 *portsrc = pse; 675 return (0); 676 } 677 678 /* 679 * The port_dissociate_ksource() function dissociates an event source from 680 * a port. 681 */ 682 int 683 port_dissociate_ksource(int port, int source, port_source_t *ps) 684 { 685 port_t *pp; 686 file_t *fp; 687 port_source_t **psh; 688 689 if (ps == NULL) 690 return (EINVAL); 691 692 if ((fp = getf(port)) == NULL) 693 return (EBADF); 694 695 if (fp->f_vnode->v_type != VPORT) { 696 releasef(port); 697 return (EBADFD); 698 } 699 pp = VTOEP(fp->f_vnode); 700 701 mutex_enter(&pp->port_queue.portq_source_mutex); 702 if (--ps->portsrc_cnt == 0) { 703 /* last association removed -> free source structure */ 704 if (ps->portsrc_prev == NULL) { 705 /* first entry */ 706 psh = &pp->port_queue.portq_scache[PORT_SHASH(source)]; 707 *psh = ps->portsrc_next; 708 if (ps->portsrc_next) 709 ps->portsrc_next->portsrc_prev = NULL; 710 } else { 711 ps->portsrc_prev->portsrc_next = ps->portsrc_next; 712 if (ps->portsrc_next) 713 ps->portsrc_next->portsrc_prev = 714 ps->portsrc_prev; 715 } 716 kmem_free(ps, sizeof (port_source_t)); 717 } 718 mutex_exit(&pp->port_queue.portq_source_mutex); 719 releasef(port); 720 return (0); 721 } 722