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