Пример #1
0
/*
 * 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);
}
Пример #2
0
/*
 * 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);
}