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