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