Allow the kernel to be compile without KTRACE option.
[dragonfly.git] / sys / kern / sys_generic.c
index 9ee35cb..5403dec 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.46 2007/08/15 03:15:06 dillon Exp $
+ * $DragonFly: src/sys/kern/sys_generic.c,v 1.49 2008/05/05 22:09:44 dillon Exp $
  */
 
 #include "opt_ktrace.h"
@@ -68,7 +68,9 @@
 #endif
 #include <vm/vm.h>
 #include <vm/vm_page.h>
+
 #include <sys/file2.h>
+#include <sys/mplock2.h>
 
 #include <machine/limits.h>
 
@@ -77,11 +79,13 @@ 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 *);
-static int     dofileread(int, struct file *, struct uio *, int, int *);
-static int     dofilewrite(int, struct file *, struct uio *, int, int *);
+static int     dofileread(int, struct file *, struct uio *, int, size_t *);
+static int     dofilewrite(int, struct file *, struct uio *, int, size_t *);
 
 /*
  * Read system call.
@@ -96,6 +100,9 @@ sys_read(struct read_args *uap)
        struct iovec aiov;
        int error;
 
+       if ((ssize_t)uap->nbyte < 0)
+               error = EINVAL;
+
        aiov.iov_base = uap->buf;
        aiov.iov_len = uap->nbyte;
        auio.uio_iov = &aiov;
@@ -106,10 +113,7 @@ sys_read(struct read_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
-       if (auio.uio_resid < 0)
-               error = EINVAL;
-       else
-               error = kern_preadv(uap->fd, &auio, 0, &uap->sysmsg_result);
+       error = kern_preadv(uap->fd, &auio, 0, &uap->sysmsg_szresult);
        return(error);
 }
 
@@ -127,6 +131,9 @@ sys_extpread(struct extpread_args *uap)
        int error;
        int flags;
 
+       if ((ssize_t)uap->nbyte < 0)
+               return(EINVAL);
+
        aiov.iov_base = uap->buf;
        aiov.iov_len = uap->nbyte;
        auio.uio_iov = &aiov;
@@ -141,10 +148,7 @@ sys_extpread(struct extpread_args *uap)
        if (uap->offset != (off_t)-1)
                flags |= O_FOFFSET;
 
-       if (auio.uio_resid < 0)
-               error = EINVAL;
-       else
-               error = kern_preadv(uap->fd, &auio, flags, &uap->sysmsg_result);
+       error = kern_preadv(uap->fd, &auio, flags, &uap->sysmsg_szresult);
        return(error);
 }
 
@@ -172,7 +176,7 @@ sys_readv(struct readv_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
-       error = kern_preadv(uap->fd, &auio, 0, &uap->sysmsg_result);
+       error = kern_preadv(uap->fd, &auio, 0, &uap->sysmsg_szresult);
 
        iovec_free(&iov, aiov);
        return (error);
@@ -208,7 +212,7 @@ sys_extpreadv(struct extpreadv_args *uap)
        if (uap->offset != (off_t)-1)
                flags |= O_FOFFSET;
 
-       error = kern_preadv(uap->fd, &auio, flags, &uap->sysmsg_result);
+       error = kern_preadv(uap->fd, &auio, flags, &uap->sysmsg_szresult);
 
        iovec_free(&iov, aiov);
        return(error);
@@ -218,7 +222,7 @@ sys_extpreadv(struct extpreadv_args *uap)
  * MPSAFE
  */
 int
-kern_preadv(int fd, struct uio *auio, int flags, int *res)
+kern_preadv(int fd, struct uio *auio, int flags, size_t *res)
 {
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
@@ -232,8 +236,6 @@ kern_preadv(int fd, struct uio *auio, int flags, int *res)
                return (EBADF);
        if (flags & O_FOFFSET && fp->f_type != DTYPE_VNODE) {
                error = ESPIPE;
-       } else if (auio->uio_resid < 0) {
-               error = EINVAL;
        } else {
                error = dofileread(fd, fp, auio, flags, res);
        }
@@ -248,13 +250,12 @@ kern_preadv(int fd, struct uio *auio, int flags, int *res)
  * MPALMOSTSAFE - ktrace needs help
  */
 static int
-dofileread(int fd, struct file *fp, struct uio *auio, int flags, int *res)
+dofileread(int fd, struct file *fp, struct uio *auio, int flags, size_t *res)
 {
-       struct thread *td = curthread;
-       struct proc *p = td->td_proc;
        int error;
-       int len;
+       size_t len;
 #ifdef KTRACE
+       struct thread *td = curthread;
        struct iovec *ktriov = NULL;
        struct uio ktruio;
 #endif
@@ -284,7 +285,7 @@ dofileread(int fd, struct file *fp, struct uio *auio, int flags, int *res)
                        ktruio.uio_iov = ktriov;
                        ktruio.uio_resid = len - auio->uio_resid;
                        get_mplock();
-                       ktrgenio(p, fd, UIO_READ, &ktruio, error);
+                       ktrgenio(td->td_lwp, fd, UIO_READ, &ktruio, error);
                        rel_mplock();
                }
                FREE(ktriov, M_TEMP);
@@ -309,6 +310,9 @@ sys_write(struct write_args *uap)
        struct iovec aiov;
        int error;
 
+       if ((ssize_t)uap->nbyte < 0)
+               error = EINVAL;
+
        aiov.iov_base = (void *)(uintptr_t)uap->buf;
        aiov.iov_len = uap->nbyte;
        auio.uio_iov = &aiov;
@@ -319,10 +323,7 @@ sys_write(struct write_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
-       if (auio.uio_resid < 0)
-               error = EINVAL;
-       else
-               error = kern_pwritev(uap->fd, &auio, 0, &uap->sysmsg_result);
+       error = kern_pwritev(uap->fd, &auio, 0, &uap->sysmsg_szresult);
 
        return(error);
 }
@@ -341,6 +342,9 @@ sys_extpwrite(struct extpwrite_args *uap)
        int error;
        int flags;
 
+       if ((ssize_t)uap->nbyte < 0)
+               error = EINVAL;
+
        aiov.iov_base = (void *)(uintptr_t)uap->buf;
        aiov.iov_len = uap->nbyte;
        auio.uio_iov = &aiov;
@@ -354,12 +358,7 @@ sys_extpwrite(struct extpwrite_args *uap)
        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, flags, &uap->sysmsg_result);
-
+       error = kern_pwritev(uap->fd, &auio, flags, &uap->sysmsg_szresult);
        return(error);
 }
 
@@ -385,7 +384,7 @@ sys_writev(struct writev_args *uap)
        auio.uio_segflg = UIO_USERSPACE;
        auio.uio_td = td;
 
-       error = kern_pwritev(uap->fd, &auio, 0, &uap->sysmsg_result);
+       error = kern_pwritev(uap->fd, &auio, 0, &uap->sysmsg_szresult);
 
        iovec_free(&iov, aiov);
        return (error);
@@ -421,7 +420,7 @@ sys_extpwritev(struct extpwritev_args *uap)
        if (uap->offset != (off_t)-1)
                flags |= O_FOFFSET;
 
-       error = kern_pwritev(uap->fd, &auio, flags, &uap->sysmsg_result);
+       error = kern_pwritev(uap->fd, &auio, flags, &uap->sysmsg_szresult);
 
        iovec_free(&iov, aiov);
        return(error);
@@ -431,7 +430,7 @@ sys_extpwritev(struct extpwritev_args *uap)
  * MPSAFE
  */
 int
-kern_pwritev(int fd, struct uio *auio, int flags, int *res)
+kern_pwritev(int fd, struct uio *auio, int flags, size_t *res)
 {
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
@@ -460,13 +459,12 @@ kern_pwritev(int fd, struct uio *auio, int flags, int *res)
  * MPALMOSTSAFE - ktrace needs help
  */
 static int
-dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
+dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, size_t *res)
 {      
        struct thread *td = curthread;
        struct lwp *lp = td->td_lwp;
-       struct proc *p = td->td_proc;
        int error;
-       int len;
+       size_t len;
 #ifdef KTRACE
        struct iovec *ktriov = NULL;
        struct uio ktruio;
@@ -485,8 +483,6 @@ dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
        }
 #endif
        len = auio->uio_resid;
-       if (fp->f_type == DTYPE_VNODE)
-               bwillwrite();
        error = fo_write(fp, auio, fp->f_cred, flags);
        if (error) {
                if (auio->uio_resid != len && (error == ERESTART ||
@@ -495,7 +491,7 @@ dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
                /* Socket layer is responsible for issuing SIGPIPE. */
                if (error == EPIPE) {
                        get_mplock();
-                       lwpsignal(p, lp, SIGPIPE);
+                       lwpsignal(lp->lwp_proc, lp, SIGPIPE);
                        rel_mplock();
                }
        }
@@ -505,7 +501,7 @@ dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
                        ktruio.uio_iov = ktriov;
                        ktruio.uio_resid = len - auio->uio_resid;
                        get_mplock();
-                       ktrgenio(p, fd, UIO_WRITE, &ktruio, error);
+                       ktrgenio(lp, fd, UIO_WRITE, &ktruio, error);
                        rel_mplock();
                }
                FREE(ktriov, M_TEMP);
@@ -519,12 +515,18 @@ dofilewrite(int fd, struct file *fp, struct uio *auio, int flags, int *res)
 
 /*
  * Ioctl system call
+ *
+ * MPALMOSTSAFE
  */
-/* ARGSUSED */
 int
 sys_ioctl(struct ioctl_args *uap)
 {
-       return(mapped_ioctl(uap->fd, uap->com, uap->data, NULL));
+       int error;
+
+       get_mplock();
+       error = mapped_ioctl(uap->fd, uap->com, uap->data, NULL, &uap->sysmsg);
+       rel_mplock();
+       return (error);
 }
 
 struct ioctl_map_entry {
@@ -539,7 +541,8 @@ struct ioctl_map_entry {
  * and appropriate conversions/conversion functions will be utilized.
  */
 int
-mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
+mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map,
+            struct sysmsg *msg)
 {
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
@@ -558,7 +561,7 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
        } ubuf;
 
        KKASSERT(p);
-       cred = p->p_ucred;
+       cred = td->td_ucred;
 
        fp = holdfp(p->p_fd, fd, FREAD|FWRITE);
        if (fp == NULL)
@@ -655,7 +658,7 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
        }
        if ((com & IOC_IN) != 0) {
                if (size != 0) {
-                       error = copyin(uspc_data, data, (u_int)size);
+                       error = copyin(uspc_data, data, (size_t)size);
                        if (error) {
                                if (memp != NULL)
                                        kfree(memp, M_IOCTLOPS);
@@ -669,7 +672,7 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                 * Zero the buffer so the user always
                 * gets back something deterministic.
                 */
-               bzero(data, size);
+               bzero(data, (size_t)size);
        } else if ((com & IOC_VOID) != 0) {
                *(caddr_t *)data = uspc_data;
        }
@@ -688,7 +691,7 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                        fp->f_flag |= FASYNC;
                else
                        fp->f_flag &= ~FASYNC;
-               error = fo_ioctl(fp, FIOASYNC, (caddr_t)&tmp, cred);
+               error = fo_ioctl(fp, FIOASYNC, (caddr_t)&tmp, cred, msg);
                break;
 
        default:
@@ -699,13 +702,13 @@ mapped_ioctl(int fd, u_long com, caddr_t uspc_data, struct ioctl_map *map)
                if (map != NULL && iomc->wrapfunc != NULL)
                        error = iomc->wrapfunc(fp, com, ocom, data, cred);
                else
-                       error = fo_ioctl(fp, com, data, cred);
+                       error = fo_ioctl(fp, com, data, cred, msg);
                /*
                 * Copy any data to user, size was
                 * already set and checked above.
                 */
                if (error == 0 && (com & IOC_OUT) != 0 && size != 0)
-                       error = copyout(data, uspc_data, (u_int)size);
+                       error = copyout(data, uspc_data, (size_t)size);
                break;
        }
        if (memp != NULL)
@@ -756,9 +759,131 @@ SYSCTL_INT(_kern, OID_AUTO, nselcoll, CTLFLAG_RD, &nselcoll, 0, "");
 
 /*
  * Select system call.
+ *
+ * MPALMOSTSAFE
  */
 int
 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.
+        */
+       get_mplock();
+       error = doselect(uap->nd, uap->in, uap->ou, uap->ex, ktvp,
+                       &uap->sysmsg_result);
+       rel_mplock();
+
+       return (error);
+}
+
+
+/*
+ * Pselect system call.
+ *
+ * MPALMOSTSAFE
+ */
+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);
+               get_mplock();
+               lp->lwp_oldsigmask = lp->lwp_sigmask;
+               SIG_CANTMASK(sigmask);
+               lp->lwp_sigmask = sigmask;
+       } else {
+               get_mplock();
+       }
+
+       /*
+        * 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;
+               }
+       }
+       rel_mplock();
+
+       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;
@@ -775,23 +900,23 @@ sys_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];
@@ -806,13 +931,13 @@ sys_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;                              \
                }                                                       \
@@ -824,15 +949,8 @@ sys_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 {
@@ -843,12 +961,12 @@ sys_select(struct select_args *uap)
 retry:
        ncoll = nselcoll;
        lp->lwp_flag |= LWP_SELECT;
-       error = selscan(p, ibits, obits, uap->nd, &uap->sysmsg_result);
-       if (error || uap->sysmsg_result)
+       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);
@@ -856,15 +974,15 @@ retry:
                    24 * 60 * 60 * hz : tvtohz_high(&ttv);
        }
        crit_enter();
+       tsleep_interlock(&selwait, PCATCH);
        if ((lp->lwp_flag & LWP_SELECT) == 0 || nselcoll != ncoll) {
                crit_exit();
                goto retry;
        }
        lp->lwp_flag &= ~LWP_SELECT;
-
-       error = tsleep((caddr_t)&selwait, PCATCH, "select", timo);
-       
+       error = tsleep(&selwait, PCATCH | PINTERLOCKED, "select", timo);
        crit_exit();
+
        if (error == 0)
                goto retry;
 done:
@@ -875,7 +993,7 @@ done:
        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;
@@ -927,6 +1045,8 @@ selscan(struct proc *p, fd_mask **ibits, fd_mask **obits, int nfd, int *res)
 
 /*
  * Poll system call.
+ *
+ * MPALMOSTSAFE
  */
 int
 sys_poll(struct poll_args *uap)
@@ -957,13 +1077,13 @@ sys_poll(struct poll_args *uap)
                bits = smallbits;
        error = copyin(uap->fds, bits, ni);
        if (error)
-               goto done;
+               goto done2;
        if (uap->timeout != INFTIM) {
                atv.tv_sec = uap->timeout / 1000;
                atv.tv_usec = (uap->timeout % 1000) * 1000;
                if (itimerfix(&atv)) {
                        error = EINVAL;
-                       goto done;
+                       goto done2;
                }
                getmicrouptime(&rtv);
                timevaladd(&atv, &rtv);
@@ -972,32 +1092,37 @@ sys_poll(struct poll_args *uap)
                atv.tv_usec = 0;
        }
        timo = 0;
+       get_mplock();
 retry:
        ncoll = nselcoll;
        lp->lwp_flag |= LWP_SELECT;
        error = pollscan(p, bits, nfds, &uap->sysmsg_result);
        if (error || uap->sysmsg_result)
-               goto done;
+               goto done1;
        if (atv.tv_sec || atv.tv_usec) {
                getmicrouptime(&rtv);
                if (timevalcmp(&rtv, &atv, >=))
-                       goto done;
+                       goto done1;
                ttv = atv;
                timevalsub(&ttv, &rtv);
                timo = ttv.tv_sec > 24 * 60 * 60 ?
                    24 * 60 * 60 * hz : tvtohz_high(&ttv);
        } 
        crit_enter();
+       tsleep_interlock(&selwait, PCATCH);
        if ((lp->lwp_flag & LWP_SELECT) == 0 || nselcoll != ncoll) {
                crit_exit();
                goto retry;
        }
        lp->lwp_flag &= ~LWP_SELECT;
-       error = tsleep((caddr_t)&selwait, PCATCH, "poll", timo);
+       error = tsleep(&selwait, PCATCH | PINTERLOCKED, "poll", timo);
        crit_exit();
+
        if (error == 0)
                goto retry;
-done:
+done1:
+       rel_mplock();
+done2:
        lp->lwp_flag &= ~LWP_SELECT;
        /* poll is not restarted after signals... */
        if (error == ERESTART)
@@ -1053,6 +1178,8 @@ pollscan(struct proc *p, struct pollfd *fds, u_int nfd, int *res)
 /*
  * OpenBSD poll system call.
  * XXX this isn't quite a true representation..  OpenBSD uses select ops.
+ *
+ * MPSAFE
  */
 int
 sys_openbsd_poll(struct openbsd_poll_args *uap)
@@ -1117,8 +1244,16 @@ selwakeup(struct selinfo *sip)
        if (lp == NULL)
                return;
 
+       /*
+        * This is a temporary hack until the code can be rewritten.
+        * Check LWP_SELECT before assuming we can setrunnable().
+        * Otherwise we might catch the lwp before it actually goes to
+        * sleep.
+        */
        crit_enter();
-       if (lp->lwp_wchan == (caddr_t)&selwait) {
+       if (lp->lwp_flag & LWP_SELECT) {
+               lp->lwp_flag &= ~LWP_SELECT;
+       } else if (lp->lwp_wchan == (caddr_t)&selwait) {
                /*
                 * Flag the process to break the tsleep when
                 * setrunnable is called, but only call setrunnable
@@ -1127,8 +1262,6 @@ selwakeup(struct selinfo *sip)
                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();
 }