+ ibits = sel->ibits;
+ obits = sel->obits;
+ wql = sel->wql;
+
+ nw = howmany(nfd, NFDBITS);
+
+ nc = 0;
+ proc_fdlock(p);
+
+ if (sel->count) {
+ for (msk = 0; msk < 3; msk++) {
+ iptr = (u_int32_t *)&ibits[msk * nw];
+ optr = (u_int32_t *)&obits[msk * nw];
+
+ for (i = 0; i < nfd; i += NFDBITS) {
+ bits = iptr[i/NFDBITS];
+
+ while ((j = ffs(bits)) && (fd = i + --j) < nfd) {
+ bits &= ~(1 << j);
+ fp = fdp->fd_ofiles[fd];
+
+ if (fp == NULL ||
+ (fdp->fd_ofileflags[fd] & UF_RESERVED)) {
+ proc_fdunlock(p);
+ return(EBADF);
+ }
+ if (sel_pass == SEL_SECONDPASS) {
+ wql_ptr = (char *)0;
+ fp->f_flags &= ~FP_INSELECT;
+ fp->f_waddr = (void *)0;
+ } else {
+ wql_ptr = (wql + nc * SIZEOF_WAITQUEUE_LINK);
+ fp->f_flags |= FP_INSELECT;
+ fp->f_waddr = (void *)wqsub;
+ }
+ if (fp->f_ops && fo_select(fp, flag[msk], wql_ptr, p)) {
+ optr[fd/NFDBITS] |= (1 << (fd % NFDBITS));
+ n++;
+ }
+ nc++;
+ }
+ }
+ }
+ }
+ proc_fdunlock(p);
+ *retval = n;
+ return (0);
+}
+
+static int poll_callback(struct kqueue *, struct kevent *, void *);
+
+struct poll_continue_args {
+ user_addr_t pca_fds;
+ u_int pca_nfds;
+ u_int pca_rfds;
+};
+
+int
+poll(struct proc *p, struct poll_args *uap, register_t *retval)
+{
+ struct poll_continue_args *cont;
+ struct pollfd *fds;
+ struct kqueue *kq;
+ struct timeval atv;
+ int ncoll, error = 0;
+ u_int nfds = uap->nfds;
+ u_int rfds = 0;
+ u_int i;
+ size_t ni;
+
+ /*
+ * This is kinda bogus. We have fd limits, but that is not
+ * really related to the size of the pollfd array. Make sure
+ * we let the process use at least FD_SETSIZE entries and at
+ * least enough for the current limits. We want to be reasonably
+ * safe, but not overly restrictive.
+ */
+ if (nfds > OPEN_MAX ||
+ (nfds > p->p_rlimit[RLIMIT_NOFILE].rlim_cur && nfds > FD_SETSIZE))
+ return (EINVAL);
+
+ kq = kqueue_alloc(p);
+ if (kq == NULL)
+ return (EAGAIN);
+
+ ni = nfds * sizeof(struct pollfd) + sizeof(struct poll_continue_args);
+ MALLOC(cont, struct poll_continue_args *, ni, M_TEMP, M_WAITOK);
+ if (NULL == cont) {
+ error = EAGAIN;
+ goto out;
+ }
+
+ fds = (struct pollfd *)&cont[1];
+ error = copyin(uap->fds, fds, nfds * sizeof(struct pollfd));
+ if (error)
+ goto out;
+
+ if (uap->timeout != -1) {
+ struct timeval rtv;
+
+ atv.tv_sec = uap->timeout / 1000;
+ atv.tv_usec = (uap->timeout % 1000) * 1000;
+ if (itimerfix(&atv)) {
+ error = EINVAL;
+ goto out;
+ }
+ getmicrouptime(&rtv);
+ timevaladd(&atv, &rtv);
+ } else {
+ atv.tv_sec = 0;
+ atv.tv_usec = 0;
+ }
+
+ /* JMM - all this P_SELECT stuff is bogus */
+ ncoll = nselcoll;
+ p->p_flag |= P_SELECT;
+
+ for (i = 0; i < nfds; i++) {
+ short events = fds[i].events;
+ struct kevent kev;
+ int kerror = 0;
+
+ /* per spec, ignore fd values below zero */
+ if (fds[i].fd < 0) {
+ fds[i].revents = 0;
+ continue;
+ }
+
+ /* convert the poll event into a kqueue kevent */
+ kev.ident = fds[i].fd;
+ kev.flags = EV_ADD | EV_ONESHOT | EV_POLL;
+ kev.fflags = NOTE_LOWAT;
+ kev.data = 1; /* efficiency be damned: any data should trigger */
+ kev.udata = CAST_USER_ADDR_T(&fds[i]);
+
+ /* Handle input events */
+ if (events & ( POLLIN | POLLRDNORM | POLLPRI | POLLRDBAND )) {
+ kev.filter = EVFILT_READ;
+ if (!(events & ( POLLIN | POLLRDNORM )))
+ kev.flags |= EV_OOBAND;
+ kerror = kevent_register(kq, &kev, p);
+ }
+
+ /* Handle output events */
+ if (kerror == 0 &&
+ events & ( POLLOUT | POLLWRNORM | POLLWRBAND )) {
+ kev.filter = EVFILT_WRITE;
+ kerror = kevent_register(kq, &kev, p);
+ }
+
+ /* Handle BSD extension vnode events */
+ if (kerror == 0 &&
+ events & ( POLLEXTEND | POLLATTRIB | POLLNLINK | POLLWRITE )) {
+ kev.filter = EVFILT_VNODE;
+ kev.fflags = 0;
+ if (events & POLLEXTEND)
+ kev.fflags |= NOTE_EXTEND;
+ if (events & POLLATTRIB)
+ kev.fflags |= NOTE_ATTRIB;
+ if (events & POLLNLINK)
+ kev.fflags |= NOTE_LINK;
+ if (events & POLLWRITE)
+ kev.fflags |= NOTE_WRITE;
+ kerror = kevent_register(kq, &kev, p);
+ }
+
+ if (kerror != 0) {
+ fds[i].revents = POLLNVAL;
+ rfds++;
+ } else
+ fds[i].revents = 0;
+ }
+
+ /* Did we have any trouble registering? */
+ if (rfds > 0)
+ goto done;
+
+ /* scan for, and possibly wait for, the kevents to trigger */
+ cont->pca_fds = uap->fds;
+ cont->pca_nfds = nfds;
+ cont->pca_rfds = rfds;
+ error = kevent_scan(kq, poll_callback, NULL, cont, &atv, p);
+ rfds = cont->pca_rfds;
+
+ done:
+ p->p_flag &= ~P_SELECT;
+ /* poll is not restarted after signals... */
+ if (error == ERESTART)
+ error = EINTR;
+ if (error == EWOULDBLOCK)
+ error = 0;
+ if (error == 0) {
+ error = copyout(fds, uap->fds, nfds * sizeof(struct pollfd));
+ *retval = rfds;
+ }
+
+ out:
+ if (NULL != cont)
+ FREE(cont, M_TEMP);
+
+ kqueue_dealloc(kq, p);
+ return (error);
+}