Add pselect syscall.
[dragonfly.git] / sys / kern / sys_generic.c
index 51c4bea..f466c2b 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)sys_generic.c       8.5 (Berkeley) 1/21/94
  * $FreeBSD: src/sys/kern/sys_generic.c,v 1.55.2.10 2001/03/17 10:39:32 peter Exp $
- * $DragonFly: src/sys/kern/sys_generic.c,v 1.29 2006/05/19 05:15:34 dillon Exp $
+ * $DragonFly: src/sys/kern/sys_generic.c,v 1.47 2008/01/10 22:30:27 nth Exp $
  */
 
 #include "opt_ktrace.h"
@@ -77,6 +77,8 @@ static MALLOC_DEFINE(M_IOCTLMAP, "ioctlmap", "mapped ioctl handler buffer");
 static MALLOC_DEFINE(M_SELECT, "select", "select() buffer");
 MALLOC_DEFINE(M_IOV, "iov", "large iov's");
 
+static int     doselect(int nd, fd_set *in, fd_set *ou, fd_set *ex,
+                       struct timeval *tv, int *res);
 static int     pollscan (struct proc *, struct pollfd *, u_int, int *);
 static int     selscan (struct proc *, fd_mask **, fd_mask **,
                        int, int *);
@@ -85,9 +87,11 @@ static int   dofilewrite(int, struct file *, struct uio *, int, int *);
 
 /*
  * Read system call.
+ *
+ * MPSAFE
  */
 int
-read(struct read_args *uap)
+sys_read(struct read_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
@@ -113,14 +117,17 @@ read(struct read_args *uap)
 
 /*
  * Positioned (Pread) read system call
+ *
+ * MPSAFE
  */
 int
-pread(struct pread_args *uap)
+sys_extpread(struct extpread_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
        struct iovec aiov;
        int error;
+       int flags;
 
        aiov.iov_base = uap->buf;
        aiov.iov_len = uap->nbyte;
@@ -132,18 +139,24 @@ pread(struct pread_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
+       flags = uap->flags & O_FMASK;
+       if (uap->offset != (off_t)-1)
+               flags |= O_FOFFSET;
+
        if (auio.uio_resid < 0)
                error = EINVAL;
        else
-               error = kern_preadv(uap->fd, &auio, FOF_OFFSET, &uap->sysmsg_result);
+               error = kern_preadv(uap->fd, &auio, flags, &uap->sysmsg_result);
        return(error);
 }
 
 /*
  * Scatter read system call.
+ *
+ * MPSAFE
  */
 int
-readv(struct readv_args *uap)
+sys_readv(struct readv_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
@@ -170,14 +183,17 @@ readv(struct readv_args *uap)
 
 /*
  * Scatter positioned read system call.
+ *
+ * MPSAFE
  */
 int
-preadv(struct preadv_args *uap)
+sys_extpreadv(struct extpreadv_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
        struct iovec aiov[UIO_SMALLIOV], *iov = NULL;
        int error;
+       int flags;
 
        error = iovec_copyin(uap->iovp, &iov, aiov, uap->iovcnt,
                             &auio.uio_resid);
@@ -190,27 +206,33 @@ preadv(struct preadv_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
-       error = kern_preadv(uap->fd, &auio, FOF_OFFSET, &uap->sysmsg_result);
+       flags = uap->flags & O_FMASK;
+       if (uap->offset != (off_t)-1)
+               flags |= O_FOFFSET;
+
+       error = kern_preadv(uap->fd, &auio, flags, &uap->sysmsg_result);
 
        iovec_free(&iov, aiov);
        return(error);
 }
 
+/*
+ * MPSAFE
+ */
 int
 kern_preadv(int fd, struct uio *auio, int flags, int *res)
 {
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
        struct file *fp;
-       struct filedesc *fdp = p->p_fd;
        int error;
 
        KKASSERT(p);
 
-       fp = holdfp(fdp, fd, FREAD);
+       fp = holdfp(p->p_fd, fd, FREAD);
        if (fp == NULL)
                return (EBADF);
-       if (flags & FOF_OFFSET && fp->f_type != DTYPE_VNODE) {
+       if (flags & O_FOFFSET && fp->f_type != DTYPE_VNODE) {
                error = ESPIPE;
        } else if (auio->uio_resid < 0) {
                error = EINVAL;
@@ -224,6 +246,8 @@ kern_preadv(int fd, struct uio *auio, int flags, int *res)
 /*
  * Common code for readv and preadv that reads data in
  * from a file using the passed in uio, offset, and flags.
+ *
+ * MPALMOSTSAFE - ktrace needs help
  */
 static int
 dofileread(int fd, struct file *fp, struct uio *auio, int flags, int *res)
@@ -261,7 +285,9 @@ dofileread(int fd, struct file *fp, struct uio *auio, int flags, int *res)
                if (error == 0) {
                        ktruio.uio_iov = ktriov;
                        ktruio.uio_resid = len - auio->uio_resid;
+                       get_mplock();
                        ktrgenio(p, fd, UIO_READ, &ktruio, error);
+                       rel_mplock();
                }
                FREE(ktriov, M_TEMP);
        }
@@ -274,9 +300,11 @@ dofileread(int fd, struct file *fp, struct uio *auio, int flags, int *res)
 
 /*
  * Write system call
+ *
+ * MPSAFE
  */
 int
-write(struct write_args *uap)
+sys_write(struct write_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
@@ -303,14 +331,17 @@ write(struct write_args *uap)
 
 /*
  * Pwrite system call
+ *
+ * MPSAFE
  */
 int
-pwrite(struct pwrite_args *uap)
+sys_extpwrite(struct extpwrite_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
        struct iovec aiov;
        int error;
+       int flags;
 
        aiov.iov_base = (void *)(uintptr_t)uap->buf;
        aiov.iov_len = uap->nbyte;
@@ -322,16 +353,23 @@ pwrite(struct pwrite_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
+       flags = uap->flags & O_FMASK;
+       if (uap->offset != (off_t)-1)
+               flags |= O_FOFFSET;
+
        if (auio.uio_resid < 0)
                error = EINVAL;
        else
-               error = kern_pwritev(uap->fd, &auio, FOF_OFFSET, &uap->sysmsg_result);
+               error = kern_pwritev(uap->fd, &auio, flags, &uap->sysmsg_result);
 
        return(error);
 }
 
+/*
+ * MPSAFE
+ */
 int
-writev(struct writev_args *uap)
+sys_writev(struct writev_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
@@ -358,14 +396,17 @@ writev(struct writev_args *uap)
 
 /*
  * Gather positioned write system call
+ *
+ * MPSAFE
  */
 int
-pwritev(struct pwritev_args *uap)
+sys_extpwritev(struct extpwritev_args *uap)
 {
        struct thread *td = curthread;
        struct uio auio;
        struct iovec aiov[UIO_SMALLIOV], *iov = NULL;
        int error;
+       int flags;
 
        error = iovec_copyin(uap->iovp, &iov, aiov, uap->iovcnt,
                             &auio.uio_resid);
@@ -378,27 +419,33 @@ pwritev(struct pwritev_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
-       error = kern_pwritev(uap->fd, &auio, FOF_OFFSET, &uap->sysmsg_result);
+       flags = uap->flags & O_FMASK;
+       if (uap->offset != (off_t)-1)
+               flags |= O_FOFFSET;
+
+       error = kern_pwritev(uap->fd, &auio, flags, &uap->sysmsg_result);
 
        iovec_free(&iov, aiov);
        return(error);
 }
 
+/*
+ * MPSAFE
+ */
 int
 kern_pwritev(int fd, struct uio *auio, int flags, int *res)
 {
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
        struct file *fp;
-       struct filedesc *fdp = p->p_fd;
        int error;
 
        KKASSERT(p);
 
-       fp = holdfp(fdp, fd, FWRITE);
+       fp = holdfp(p->p_fd, fd, FWRITE);
        if (fp == NULL)
                return (EBADF);
-       else if ((flags & FOF_OFFSET) && fp->f_type != DTYPE_VNODE) {
+       else if ((flags & O_FOFFSET) && fp->f_type != DTYPE_VNODE) {
                error = ESPIPE;
        } else {
                error = dofilewrite(fd, fp, auio, flags, res);
@@ -411,11 +458,14 @@ kern_pwritev(int fd, struct uio *auio, int flags, int *res)
 /*
  * Common code for writev and pwritev that writes data to
  * a file using the passed in uio, offset, and flags.
+ *
+ * MPALMOSTSAFE - ktrace needs help
  */
 static int
 dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
 {      
        struct thread *td = curthread;
+       struct lwp *lp = td->td_lwp;
        struct proc *p = td->td_proc;
        int error;
        int len;
@@ -445,15 +495,20 @@ dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
                    error == EINTR || error == EWOULDBLOCK))
                        error = 0;
                /* Socket layer is responsible for issuing SIGPIPE. */
-               if (error == EPIPE)
-                       psignal(p, SIGPIPE);
+               if (error == EPIPE) {
+                       get_mplock();
+                       lwpsignal(p, lp, SIGPIPE);
+                       rel_mplock();
+               }
        }
 #ifdef KTRACE
        if (ktriov != NULL) {
                if (error == 0) {
                        ktruio.uio_iov = ktriov;
                        ktruio.uio_resid = len - auio->uio_resid;
+                       get_mplock();
                        ktrgenio(p, fd, UIO_WRITE, &ktruio, error);
+                       rel_mplock();
                }
                FREE(ktriov, M_TEMP);
        }
@@ -469,7 +524,7 @@ dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
  */
 /* ARGSUSED */
 int
-ioctl(struct ioctl_args *uap)
+sys_ioctl(struct ioctl_args *uap)
 {
        return(mapped_ioctl(uap->fd, uap->com, uap->data, NULL));
 }
@@ -492,7 +547,6 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
        struct proc *p = td->td_proc;
        struct ucred *cred;
        struct file *fp;
-       struct filedesc *fdp;
        struct ioctl_map_range *iomc = NULL;
        int error;
        u_int size;
@@ -507,12 +561,9 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
 
        KKASSERT(p);
        cred = p->p_ucred;
-       fdp = p->p_fd;
-       if ((u_int)fd >= fdp->fd_nfiles ||
-           (fp = fdp->fd_files[fd].fp) == NULL)
-               return(EBADF);
 
-       if ((fp->f_flag & (FREAD | FWRITE)) == 0)
+       fp = holdfp(p->p_fd, fd, FREAD|FWRITE);
+       if (fp == NULL)
                return(EBADF);
 
        if (map != NULL) {      /* obey translation map */
@@ -540,11 +591,12 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                if (iomc == NULL ||
                    (iomc->start == 0 && iomc->maptocmd == 0
                     && iomc->wrapfunc == NULL && iomc->mapfunc == NULL)) {
-                       printf("%s: 'ioctl' fd=%d, cmd=0x%lx ('%c',%d) not implemented\n",
+                       kprintf("%s: 'ioctl' fd=%d, cmd=0x%lx ('%c',%d) not implemented\n",
                               map->sys, fd, maskcmd,
                               (int)((maskcmd >> 8) & 0xff),
                               (int)(maskcmd & 0xff));
-                       return(EINVAL);
+                       error = EINVAL;
+                       goto done;
                }
 
                /*
@@ -567,22 +619,23 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                                                    iomc->maptocmd, iomc->maptoend,
                                                    com, ocom);
                        } else {
-                               printf("%s: Invalid mapping for fd=%d, cmd=%#lx ('%c',%d)\n",
+                               kprintf("%s: Invalid mapping for fd=%d, cmd=%#lx ('%c',%d)\n",
                                       map->sys, fd, maskcmd,
                                       (int)((maskcmd >> 8) & 0xff),
                                       (int)(maskcmd & 0xff));
-                               return(EINVAL);
+                               error = EINVAL;
+                               goto done;
                        }
                }
        }
 
        switch (com) {
        case FIONCLEX:
-               fdp->fd_files[fd].fileflags &= ~UF_EXCLOSE;
-               return(0);
+               error = fclrfdflags(p->p_fd, fd, UF_EXCLOSE);
+               goto done;
        case FIOCLEX:
-               fdp->fd_files[fd].fileflags |= UF_EXCLOSE;
-               return(0);
+               error = fsetfdflags(p->p_fd, fd, UF_EXCLOSE);
+               goto done;
        }
 
        /*
@@ -590,14 +643,14 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
         * copied to/from the user's address space.
         */
        size = IOCPARM_LEN(com);
-       if (size > IOCPARM_MAX)
-               return(ENOTTY);
-
-       fhold(fp);
+       if (size > IOCPARM_MAX) {
+               error = ENOTTY;
+               goto done;
+       }
 
        memp = NULL;
        if (size > sizeof (ubuf.stkbuf)) {
-               memp = malloc(size, M_IOCTLOPS, M_WAITOK);
+               memp = kmalloc(size, M_IOCTLOPS, M_WAITOK);
                data = memp;
        } else {
                data = ubuf.stkbuf;
@@ -607,9 +660,8 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                        error = copyin(uspc_data, data, (u_int)size);
                        if (error) {
                                if (memp != NULL)
-                                       free(memp, M_IOCTLOPS);
-                               fdrop(fp);
-                               return(error);
+                                       kfree(memp, M_IOCTLOPS);
+                               goto done;
                        }
                } else {
                        *(caddr_t *)data = uspc_data;
@@ -625,13 +677,12 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
        }
 
        switch (com) {
-
        case FIONBIO:
                if ((tmp = *(int *)data))
                        fp->f_flag |= FNONBLOCK;
                else
                        fp->f_flag &= ~FNONBLOCK;
-               error = fo_ioctl(fp, FIONBIO, (caddr_t)&tmp, cred);
+               error = 0;
                break;
 
        case FIOASYNC:
@@ -660,7 +711,8 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                break;
        }
        if (memp != NULL)
-               free(memp, M_IOCTLOPS);
+               kfree(memp, M_IOCTLOPS);
+done:
        fdrop(fp);
        return(error);
 }
@@ -673,7 +725,7 @@ mapped_ioctl_register_handler(struct ioctl_map_handler *he)
        KKASSERT(he != NULL && he->map != NULL && he->cmd_ranges != NULL &&
                 he->subsys != NULL && *he->subsys != '\0');
 
-       ne = malloc(sizeof(struct ioctl_map_entry), M_IOCTLMAP, M_WAITOK);
+       ne = kmalloc(sizeof(struct ioctl_map_entry), M_IOCTLMAP, M_WAITOK);
 
        ne->subsys = he->subsys;
        ne->cmd_ranges = he->cmd_ranges;
@@ -694,7 +746,7 @@ mapped_ioctl_unregister_handler(struct ioctl_map_handler *he)
                if (ne->cmd_ranges != he->cmd_ranges)
                        continue;
                LIST_REMOVE(ne, entries);
-               free(ne, M_IOCTLMAP);
+               kfree(ne, M_IOCTLMAP);
                return(0);
        }
        return(EINVAL);
@@ -708,8 +760,121 @@ SYSCTL_INT(_kern, OID_AUTO, nselcoll, CTLFLAG_RD, &nselcoll, 0, "");
  * Select system call.
  */
 int
-select(struct select_args *uap)
+sys_select(struct select_args *uap)
+{
+       struct timeval ktv;
+       struct timeval *ktvp;
+       int error;
+
+       /*
+        * Get timeout if any.
+        */
+       if (uap->tv != NULL) {
+               error = copyin(uap->tv, &ktv, sizeof (ktv));
+               if (error)
+                       return (error);
+               error = itimerfix(&ktv);
+               if (error)
+                       return (error);
+               ktvp = &ktv;
+       } else {
+               ktvp = NULL;
+       }
+
+       /*
+        * Do real work.
+        */
+       error = doselect(uap->nd, uap->in, uap->ou, uap->ex, ktvp,
+                       &uap->sysmsg_result);
+
+       return (error);
+}
+
+
+/*
+ * Pselect system call.
+ */
+int
+sys_pselect(struct pselect_args *uap)
 {
+       struct thread *td = curthread;
+       struct lwp *lp = td->td_lwp;
+       struct timespec kts;
+       struct timeval ktv;
+       struct timeval *ktvp;
+       sigset_t sigmask;
+       int error;
+
+       /*
+        * Get timeout if any and convert it.
+        * Round up during conversion to avoid timeout going off early.
+        */
+       if (uap->ts != NULL) {
+               error = copyin(uap->ts, &kts, sizeof (kts));
+               if (error)
+                       return (error);
+               ktv.tv_sec = kts.tv_sec;
+               ktv.tv_usec = (kts.tv_nsec + 999) / 1000;
+               error = itimerfix(&ktv);
+               if (error)
+                       return (error);
+               ktvp = &ktv;
+       } else {
+               ktvp = NULL;
+       }
+
+       /*
+        * Install temporary signal mask if any provided.
+        */
+       if (uap->sigmask != NULL) {
+               error = copyin(uap->sigmask, &sigmask, sizeof(sigmask));
+               if (error)
+                       return (error);
+               lp->lwp_oldsigmask = lp->lwp_sigmask;
+               SIG_CANTMASK(sigmask);
+               lp->lwp_sigmask = sigmask;
+       }
+
+       /*
+        * Do real job.
+        */
+       error = doselect(uap->nd, uap->in, uap->ou, uap->ex, ktvp,
+                       &uap->sysmsg_result);
+
+       if (uap->sigmask != NULL) {
+               /* doselect() responsible for turning ERESTART into EINTR */
+               KKASSERT(error != ERESTART);
+               if (error == EINTR) {
+                       /*
+                        * We can't restore the previous signal mask now
+                        * because it could block the signal that interrupted
+                        * us.  So make a note to restore it after executing
+                        * the handler.
+                        */
+                       lp->lwp_flag |= LWP_OLDMASK;
+               } else {
+                       /*
+                        * No handler to run. Restore previous mask immediately.
+                        */
+                       lp->lwp_sigmask = lp->lwp_oldsigmask;
+               }
+       }
+
+       return (error);
+}
+
+/*
+ * Common code for sys_select() and sys_pselect().
+ *
+ * in, out and ex are userland pointers.  tv must point to validated
+ * kernel-side timeout value or NULL for infinite timeout.  res must
+ * point to syscall return value.
+ */
+static int
+doselect(int nd, fd_set *in, fd_set *ou, fd_set *ex, struct timeval *tv,
+               int *res)
+{
+       struct lwp *lp = curthread->td_lwp;
        struct proc *p = curproc;
 
        /*
@@ -724,28 +889,28 @@ select(struct select_args *uap)
        int ncoll, error, timo;
        u_int nbufbytes, ncpbytes, nfdbits;
 
-       if (uap->nd < 0)
+       if (nd < 0)
                return (EINVAL);
-       if (uap->nd > p->p_fd->fd_nfiles)
-               uap->nd = p->p_fd->fd_nfiles;   /* forgiving; slightly wrong */
+       if (nd > p->p_fd->fd_nfiles)
+               nd = p->p_fd->fd_nfiles;   /* forgiving; slightly wrong */
 
        /*
         * Allocate just enough bits for the non-null fd_sets.  Use the
         * preallocated auto buffer if possible.
         */
-       nfdbits = roundup(uap->nd, NFDBITS);
+       nfdbits = roundup(nd, NFDBITS);
        ncpbytes = nfdbits / NBBY;
        nbufbytes = 0;
-       if (uap->in != NULL)
+       if (in != NULL)
                nbufbytes += 2 * ncpbytes;
-       if (uap->ou != NULL)
+       if (ou != NULL)
                nbufbytes += 2 * ncpbytes;
-       if (uap->ex != NULL)
+       if (ex != NULL)
                nbufbytes += 2 * ncpbytes;
        if (nbufbytes <= sizeof s_selbits)
                selbits = &s_selbits[0];
        else
-               selbits = malloc(nbufbytes, M_SELECT, M_WAITOK);
+               selbits = kmalloc(nbufbytes, M_SELECT, M_WAITOK);
 
        /*
         * Assign pointers into the bit buffers and fetch the input bits.
@@ -755,13 +920,13 @@ select(struct select_args *uap)
        sbp = selbits;
 #define        getbits(name, x) \
        do {                                                            \
-               if (uap->name == NULL)                                  \
+               if (name == NULL)                                       \
                        ibits[x] = NULL;                                \
                else {                                                  \
                        ibits[x] = sbp + nbufbytes / 2 / sizeof *sbp;   \
                        obits[x] = sbp;                                 \
                        sbp += ncpbytes / sizeof *sbp;                  \
-                       error = copyin(uap->name, ibits[x], ncpbytes);  \
+                       error = copyin(name, ibits[x], ncpbytes);       \
                        if (error != 0)                                 \
                                goto done;                              \
                }                                                       \
@@ -773,15 +938,8 @@ select(struct select_args *uap)
        if (nbufbytes != 0)
                bzero(selbits, nbufbytes / 2);
 
-       if (uap->tv) {
-               error = copyin((caddr_t)uap->tv, (caddr_t)&atv,
-                       sizeof (atv));
-               if (error)
-                       goto done;
-               if (itimerfix(&atv)) {
-                       error = EINVAL;
-                       goto done;
-               }
+       if (tv != NULL) {
+               atv = *tv;
                getmicrouptime(&rtv);
                timevaladd(&atv, &rtv);
        } else {
@@ -791,13 +949,13 @@ select(struct select_args *uap)
        timo = 0;
 retry:
        ncoll = nselcoll;
-       p->p_flag |= P_SELECT;
-       error = selscan(p, ibits, obits, uap->nd, &uap->sysmsg_result);
-       if (error || uap->sysmsg_result)
+       lp->lwp_flag |= LWP_SELECT;
+       error = selscan(p, ibits, obits, nd, res);
+       if (error || *res)
                goto done;
        if (atv.tv_sec || atv.tv_usec) {
                getmicrouptime(&rtv);
-               if (timevalcmp(&rtv, &atv, >=)) 
+               if (timevalcmp(&rtv, &atv, >=))
                        goto done;
                ttv = atv;
                timevalsub(&ttv, &rtv);
@@ -805,11 +963,11 @@ retry:
                    24 * 60 * 60 * hz : tvtohz_high(&ttv);
        }
        crit_enter();
-       if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) {
+       if ((lp->lwp_flag & LWP_SELECT) == 0 || nselcoll != ncoll) {
                crit_exit();
                goto retry;
        }
-       p->p_flag &= ~P_SELECT;
+       lp->lwp_flag &= ~LWP_SELECT;
 
        error = tsleep((caddr_t)&selwait, PCATCH, "select", timo);
        
@@ -817,14 +975,14 @@ retry:
        if (error == 0)
                goto retry;
 done:
-       p->p_flag &= ~P_SELECT;
+       lp->lwp_flag &= ~LWP_SELECT;
        /* select is not restarted after signals... */
        if (error == ERESTART)
                error = EINTR;
        if (error == EWOULDBLOCK)
                error = 0;
 #define        putbits(name, x) \
-       if (uap->name && (error2 = copyout(obits[x], uap->name, ncpbytes))) \
+       if (name && (error2 = copyout(obits[x], name, ncpbytes))) \
                error = error2;
        if (error == 0) {
                int error2;
@@ -835,14 +993,13 @@ done:
 #undef putbits
        }
        if (selbits != &s_selbits[0])
-               free(selbits, M_SELECT);
+               kfree(selbits, M_SELECT);
        return (error);
 }
 
 static int
 selscan(struct proc *p, fd_mask **ibits, fd_mask **obits, int nfd, int *res)
 {
-       struct filedesc *fdp = p->p_fd;
        int msk, i, fd;
        fd_mask bits;
        struct file *fp;
@@ -859,7 +1016,7 @@ selscan(struct proc *p, fd_mask **ibits, fd_mask **obits, int nfd, int *res)
                        for (fd = i; bits && fd < nfd; fd++, bits >>= 1) {
                                if (!(bits & 1))
                                        continue;
-                               fp = fdp->fd_files[fd].fp;
+                               fp = holdfp(p->p_fd, fd, -1);
                                if (fp == NULL)
                                        return (EBADF);
                                if (fo_poll(fp, flag[msk], fp->f_cred)) {
@@ -867,6 +1024,7 @@ selscan(struct proc *p, fd_mask **ibits, fd_mask **obits, int nfd, int *res)
                                            ((fd_mask)1 << ((fd) % NFDBITS));
                                        n++;
                                }
+                               fdrop(fp);
                        }
                }
        }
@@ -878,7 +1036,7 @@ selscan(struct proc *p, fd_mask **ibits, fd_mask **obits, int nfd, int *res)
  * Poll system call.
  */
 int
-poll(struct poll_args *uap)
+sys_poll(struct poll_args *uap)
 {
        struct pollfd *bits;
        struct pollfd smallbits[32];
@@ -886,6 +1044,7 @@ poll(struct poll_args *uap)
        int ncoll, error = 0, timo;
        u_int nfds;
        size_t ni;
+       struct lwp *lp = curthread->td_lwp;
        struct proc *p = curproc;
 
        nfds = uap->nfds;
@@ -900,7 +1059,7 @@ poll(struct poll_args *uap)
                return (EINVAL);
        ni = nfds * sizeof(struct pollfd);
        if (ni > sizeof(smallbits))
-               bits = malloc(ni, M_TEMP, M_WAITOK);
+               bits = kmalloc(ni, M_TEMP, M_WAITOK);
        else
                bits = smallbits;
        error = copyin(uap->fds, bits, ni);
@@ -922,7 +1081,7 @@ poll(struct poll_args *uap)
        timo = 0;
 retry:
        ncoll = nselcoll;
-       p->p_flag |= P_SELECT;
+       lp->lwp_flag |= LWP_SELECT;
        error = pollscan(p, bits, nfds, &uap->sysmsg_result);
        if (error || uap->sysmsg_result)
                goto done;
@@ -936,17 +1095,17 @@ retry:
                    24 * 60 * 60 * hz : tvtohz_high(&ttv);
        } 
        crit_enter();
-       if ((p->p_flag & P_SELECT) == 0 || nselcoll != ncoll) {
+       if ((lp->lwp_flag & LWP_SELECT) == 0 || nselcoll != ncoll) {
                crit_exit();
                goto retry;
        }
-       p->p_flag &= ~P_SELECT;
+       lp->lwp_flag &= ~LWP_SELECT;
        error = tsleep((caddr_t)&selwait, PCATCH, "poll", timo);
        crit_exit();
        if (error == 0)
                goto retry;
 done:
-       p->p_flag &= ~P_SELECT;
+       lp->lwp_flag &= ~LWP_SELECT;
        /* poll is not restarted after signals... */
        if (error == ERESTART)
                error = EINTR;
@@ -959,26 +1118,25 @@ done:
        }
 out:
        if (ni > sizeof(smallbits))
-               free(bits, M_TEMP);
+               kfree(bits, M_TEMP);
        return (error);
 }
 
 static int
 pollscan(struct proc *p, struct pollfd *fds, u_int nfd, int *res)
 {
-       struct filedesc *fdp = p->p_fd;
        int i;
        struct file *fp;
        int n = 0;
 
        for (i = 0; i < nfd; i++, fds++) {
-               if (fds->fd >= fdp->fd_nfiles) {
+               if (fds->fd >= p->p_fd->fd_nfiles) {
                        fds->revents = POLLNVAL;
                        n++;
                } else if (fds->fd < 0) {
                        fds->revents = 0;
                } else {
-                       fp = fdp->fd_files[fds->fd].fp;
+                       fp = holdfp(p->p_fd, fds->fd, -1);
                        if (fp == NULL) {
                                fds->revents = POLLNVAL;
                                n++;
@@ -991,6 +1149,7 @@ pollscan(struct proc *p, struct pollfd *fds, u_int nfd, int *res)
                                                        fp->f_cred);
                                if (fds->revents != 0)
                                        n++;
+                               fdrop(fp);
                        }
                }
        }
@@ -1003,14 +1162,14 @@ pollscan(struct proc *p, struct pollfd *fds, u_int nfd, int *res)
  * XXX this isn't quite a true representation..  OpenBSD uses select ops.
  */
 int
-openbsd_poll(struct openbsd_poll_args *uap)
+sys_openbsd_poll(struct openbsd_poll_args *uap)
 {
-       return (poll((struct poll_args *)uap));
+       return (sys_poll((struct poll_args *)uap));
 }
 
 /*ARGSUSED*/
 int
-seltrue(dev_t dev, int events, struct thread *td)
+seltrue(cdev_t dev, int events)
 {
        return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM));
 }
@@ -1023,19 +1182,21 @@ void
 selrecord(struct thread *selector, struct selinfo *sip)
 {
        struct proc *p;
-       pid_t mypid;
+       struct lwp *lp = NULL;
 
-       if ((p = selector->td_proc) == NULL)
+       if (selector->td_lwp == NULL)
                panic("selrecord: thread needs a process");
 
-       mypid = p->p_pid;
-       if (sip->si_pid == mypid)
+       if (sip->si_pid == selector->td_proc->p_pid &&
+           sip->si_tid == selector->td_lwp->lwp_tid)
                return;
-       if (sip->si_pid && (p = pfind(sip->si_pid)) &&
-           p->p_wchan == (caddr_t)&selwait) {
+       if (sip->si_pid && (p = pfind(sip->si_pid)))
+               lp = lwp_rb_tree_RB_LOOKUP(&p->p_lwp_tree, sip->si_tid);
+       if (lp != NULL && lp->lwp_wchan == (caddr_t)&selwait) {
                sip->si_flags |= SI_COLL;
        } else {
-               sip->si_pid = mypid;
+               sip->si_pid = selector->td_proc->p_pid;
+               sip->si_tid = selector->td_lwp->lwp_tid;
        }
 }
 
@@ -1046,6 +1207,7 @@ void
 selwakeup(struct selinfo *sip)
 {
        struct proc *p;
+       struct lwp *lp = NULL;
 
        if (sip->si_pid == 0)
                return;
@@ -1056,21 +1218,25 @@ selwakeup(struct selinfo *sip)
        }
        p = pfind(sip->si_pid);
        sip->si_pid = 0;
-       if (p != NULL) {
-               crit_enter();
-               if (p->p_wchan == (caddr_t)&selwait) {
-                       /*
-                        * Flag the process to break the tsleep when 
-                        * setrunnable is called, but only call setrunnable
-                        * here if the process is not in a stopped state.
-                        */
-                       p->p_flag |= P_BREAKTSLEEP;
-                       if ((p->p_flag & P_STOPPED) == 0)
-                               setrunnable(p);
-               } else if (p->p_flag & P_SELECT) {
-                       p->p_flag &= ~P_SELECT;
-               }
-               crit_exit();
+       if (p == NULL)
+               return;
+       lp = lwp_rb_tree_RB_LOOKUP(&p->p_lwp_tree, sip->si_tid);
+       if (lp == NULL)
+               return;
+
+       crit_enter();
+       if (lp->lwp_wchan == (caddr_t)&selwait) {
+               /*
+                * Flag the process to break the tsleep when
+                * setrunnable is called, but only call setrunnable
+                * here if the process is not in a stopped state.
+                */
+               lp->lwp_flag |= LWP_BREAKTSLEEP;
+               if (p->p_stat != SSTOP)
+                       setrunnable(lp);
+       } else if (lp->lwp_flag & LWP_SELECT) {
+               lp->lwp_flag &= ~LWP_SELECT;
        }
+       crit_exit();
 }