/* * This function is faster than the standard port_alloc_event() and * can be used when the event source already allocated an event from * a port. */ int port_dup_event(port_kevent_t *pkevp, port_kevent_t **pkevdupp, int flags) { int error; error = port_alloc_event_local(pkevp->portkev_port, pkevp->portkev_source, flags, pkevdupp); if (error == 0) (*pkevdupp)->portkev_pid = pkevp->portkev_pid; return (error); }
/* * port_associate_fd() * This function associates new file descriptors with a port or * reactivate already associated file descriptors. * The reactivation also updates the events types to be checked and the * attached user pointer. * Per port a cache is used to store associated file descriptors. * Internally the VOP_POLL interface is used to poll for existing events. * The VOP_POLL interface can also deliver a pointer to a pollhead_t structure * which is used to enqueue polldat_t structures with pending events. * If VOP_POLL immediately returns valid events (revents) then those events * will be submitted to the event port with port_send_event(). * Otherwise VOP_POLL does not return events but it delivers a pointer to a * pollhead_t structure. In such a case the corresponding file system behind * VOP_POLL will use the pollwakeup() function to notify about exisiting * events. */ int port_associate_fd(port_t *pp, int source, uintptr_t object, int events, void *user) { port_fdcache_t *pcp; int fd; struct pollhead *php = NULL; portfd_t *pfd; polldat_t *pdp; file_t *fp; port_kevent_t *pkevp; short revents; int error = 0; pcp = pp->port_queue.portq_pcp; if (object > (uintptr_t)INT_MAX) return (EBADFD); fd = object; if ((fp = getf(fd)) == NULL) return (EBADFD); mutex_enter(&pcp->pc_lock); if (pcp->pc_hash == NULL) { /* * This is the first time that a fd is being associated with * the current port: * - create PORT_SOURCE_FD cache * - associate PORT_SOURCE_FD source with the port */ error = port_associate_ksource(pp->port_fd, PORT_SOURCE_FD, NULL, port_close_sourcefd, pp, NULL); if (error) { mutex_exit(&pcp->pc_lock); releasef(fd); return (error); } /* create polldat cache */ pcp->pc_hashsize = PORTHASH_START; pcp->pc_hash = kmem_zalloc(pcp->pc_hashsize * sizeof (portfd_t *), KM_SLEEP); pfd = NULL; } else { /* Check if the fd/fp is already associated with the port */ pfd = port_cache_lookup_fp(pcp, fd, fp); } if (pfd == NULL) { /* * new entry * Allocate a polldat_t structure per fd * The use of the polldat_t structure to cache file descriptors * is required to be able to share the pollwakeup() function * with poll(2) and devpoll(7d). */ pfd = kmem_zalloc(sizeof (portfd_t), KM_SLEEP); pdp = PFTOD(pfd); pdp->pd_fd = fd; pdp->pd_fp = fp; pdp->pd_pcache = (void *)pcp; /* Allocate a port event structure per fd */ error = port_alloc_event_local(pp, source, PORT_ALLOC_CACHED, &pdp->pd_portev); if (error) { kmem_free(pfd, sizeof (portfd_t)); releasef(fd); mutex_exit(&pcp->pc_lock); return (error); } pkevp = pdp->pd_portev; pkevp->portkev_callback = port_fd_callback; pkevp->portkev_arg = pfd; /* add portfd_t entry to the cache */ port_cache_insert_fd(pcp, pdp); pkevp->portkev_object = fd; pkevp->portkev_user = user; /* * Add current port to the file descriptor interested list * The members of the list are notified when the file descriptor * is closed. */ addfd_port(fd, pfd); } else { /* * The file descriptor is already associated with the port */ pdp = PFTOD(pfd); pkevp = pdp->pd_portev; /* * Check if the re-association happens before the last * submitted event of the file descriptor was retrieved. * Clear the PORT_KEV_VALID flag if set. No new events * should get submitted after this flag is cleared. */ mutex_enter(&pkevp->portkev_lock); if (pkevp->portkev_flags & PORT_KEV_VALID) { pkevp->portkev_flags &= ~PORT_KEV_VALID; } if (pkevp->portkev_flags & PORT_KEV_DONEQ) { mutex_exit(&pkevp->portkev_lock); /* * Remove any events that where already fired * for this fd and are still in the port queue. */ port_remove_done_event(pkevp); } else { mutex_exit(&pkevp->portkev_lock); } pkevp->portkev_user = user; } mutex_enter(&pkevp->portkev_lock); pkevp->portkev_events = 0; /* no fired events */ pdp->pd_events = events; /* events associated */ /* * allow new events. */ pkevp->portkev_flags |= PORT_KEV_VALID; mutex_exit(&pkevp->portkev_lock); /* * do VOP_POLL and cache this poll fd. * * XXX - pollrelock() logic needs to know * which pollcache lock to grab. It'd be a * cleaner solution if we could pass pcp as * an arguement in VOP_POLL interface instead * of implicitly passing it using thread_t * struct. On the other hand, changing VOP_POLL * interface will require all driver/file system * poll routine to change. */ curthread->t_pollcache = (pollcache_t *)pcp; error = VOP_POLL(fp->f_vnode, events, 0, &revents, &php); curthread->t_pollcache = NULL; /* * To keep synchronization between VOP_POLL above and * pollhead_insert below, it is necessary to * call VOP_POLL() again (see port_bind_pollhead()). */ if (error) { /* dissociate the fd from the port */ delfd_port(fd, pfd); port_remove_fd_local(pfd, pcp); releasef(fd); mutex_exit(&pcp->pc_lock); return (error); } if (php != NULL) { /* * No events delivered yet. * Bind pollhead pointer with current polldat_t structure. * Sub-system will call pollwakeup() later with php as * argument. */ error = port_bind_pollhead(&php, pdp, &revents); if (error) { delfd_port(fd, pfd); port_remove_fd_local(pfd, pcp); releasef(fd); mutex_exit(&pcp->pc_lock); return (error); } } /* * Check if new events where detected and no events have been * delivered. The revents was already set after the VOP_POLL * above or it was updated in port_bind_pollhead(). */ mutex_enter(&pkevp->portkev_lock); if (revents && (pkevp->portkev_flags & PORT_KEV_VALID)) { ASSERT((pkevp->portkev_flags & PORT_KEV_DONEQ) == 0); pkevp->portkev_flags &= ~PORT_KEV_VALID; revents = revents & (pdp->pd_events | POLLHUP | POLLERR); /* send events to the event port */ pkevp->portkev_events = revents; /* * port_send_event will release the portkev_lock mutex. */ port_send_event(pkevp); } else { mutex_exit(&pkevp->portkev_lock); } releasef(fd); mutex_exit(&pcp->pc_lock); return (error); }