Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/kern Fix the races of direct select()/poll():



details:   https://anonhg.NetBSD.org/src/rev/2471cddd59db
branches:  trunk
changeset: 768000:2471cddd59db
user:      hannken <hannken%NetBSD.org@localhost>
date:      Sat Aug 06 11:04:25 2011 +0000

description:
Fix the races of direct select()/poll():

- When sel_do_scan() restarts do a full initialization with selclear() so
  we start from an empty set without registered events.  Defer the
  evaluation of l_selret after selclear() and add the count of direct events
  to the count of events.

- For selscan()/pollscan() zero the output descriptors before we poll and
  for selscan() take the sc_lock before we change them.

- Change sel_setevents() to not count events already set.

Reviewed by: YAMAMOTO Takashi <yamt%netbsd.org@localhost>

Should fix PR #44763 (select/poll direct-set optimization seems racy)
       and PR #45187 (select(2) sometimes doesn't wakeup)

diffstat:

 sys/kern/sys_select.c |  54 ++++++++++++++++++++++++++++++++++----------------
 1 files changed, 37 insertions(+), 17 deletions(-)

diffs (163 lines):

diff -r 930da3c1d893 -r 2471cddd59db sys/kern/sys_select.c
--- a/sys/kern/sys_select.c     Sat Aug 06 11:02:41 2011 +0000
+++ b/sys/kern/sys_select.c     Sat Aug 06 11:04:25 2011 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: sys_select.c,v 1.33 2011/05/28 15:33:41 christos Exp $ */
+/*     $NetBSD: sys_select.c,v 1.34 2011/08/06 11:04:25 hannken Exp $  */
 
 /*-
  * Copyright (c) 2007, 2008, 2009, 2010 The NetBSD Foundation, Inc.
@@ -84,7 +84,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_select.c,v 1.33 2011/05/28 15:33:41 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_select.c,v 1.34 2011/08/06 11:04:25 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -235,18 +235,19 @@
        sc = curcpu()->ci_data.cpu_selcluster;
        lock = sc->sc_lock;
        l->l_selcluster = sc;
-       SLIST_INIT(&l->l_selwait);
-
-       l->l_selret = 0;
        if (op == SELOP_SELECT) {
                l->l_selbits = fds;
                l->l_selni = ni;
        } else {
                l->l_selbits = NULL;
        }
+
        for (;;) {
                int ncoll;
 
+               SLIST_INIT(&l->l_selwait);
+               l->l_selret = 0;
+
                /*
                 * No need to lock.  If this is overwritten by another value
                 * while scanning, we will retry below.  We only need to see
@@ -276,18 +277,18 @@
                if (__predict_false(sc->sc_ncoll != ncoll)) {
                        /* Collision: perform re-scan. */
                        mutex_spin_exit(lock);
+                       selclear();
                        continue;
                }
                if (__predict_true(l->l_selflag == SEL_EVENT)) {
                        /* Events occured, they are set directly. */
                        mutex_spin_exit(lock);
-                       KASSERT(l->l_selret != 0);
-                       *retval = l->l_selret;
                        break;
                }
                if (__predict_true(l->l_selflag == SEL_RESET)) {
                        /* Events occured, but re-scan is requested. */
                        mutex_spin_exit(lock);
+                       selclear();
                        continue;
                }
                /* Nothing happen, therefore - sleep. */
@@ -304,6 +305,12 @@
        }
        selclear();
 
+       /* Add direct events if any. */
+       if (l->l_selflag == SEL_EVENT) {
+               KASSERT(l->l_selret != 0);
+               *retval += l->l_selret;
+       }
+
        if (__predict_false(mask))
                sigsuspendteardown(l);
 
@@ -371,11 +378,14 @@
        fd_mask *ibitp, *obitp;
        int msk, i, j, fd, n;
        file_t *fp;
+       kmutex_t *lock;
 
+       lock = curlwp->l_selcluster->sc_lock;
        ibitp = (fd_mask *)(bits + ni * 0);
        obitp = (fd_mask *)(bits + ni * 3);
        n = 0;
 
+       memset(obitp, 0, ni * 3);
        for (msk = 0; msk < 3; msk++) {
                for (i = 0; i < nfd; i += NFDBITS) {
                        fd_mask ibits, obits;
@@ -397,7 +407,12 @@
                                }
                                fd_putfile(fd);
                        }
-                       *obitp++ = obits;
+                       if (obits != 0) {
+                               mutex_spin_enter(lock);
+                               *obitp |= obits;
+                               mutex_spin_exit(lock);
+                       }
+                       obitp++;
                }
        }
        *retval = n;
@@ -505,14 +520,14 @@
 pollscan(struct pollfd *fds, const int nfd, register_t *retval)
 {
        file_t *fp;
-       int i, n = 0;
+       int i, n = 0, revents;
 
        for (i = 0; i < nfd; i++, fds++) {
+               fds->revents = 0;
                if (fds->fd < 0) {
-                       fds->revents = 0;
+                       revents = 0;
                } else if ((fp = fd_getfile(fds->fd)) == NULL) {
-                       fds->revents = POLLNVAL;
-                       n++;
+                       revents = POLLNVAL;
                } else {
                        /*
                         * Perform poll: registers select request or returns
@@ -520,12 +535,14 @@
                         * selrecord(), which is a pointer to struct pollfd.
                         */
                        curlwp->l_selrec = (uintptr_t)fds;
-                       fds->revents = (*fp->f_ops->fo_poll)(fp,
+                       revents = (*fp->f_ops->fo_poll)(fp,
                            fds->events | POLLERR | POLLHUP);
-                       if (fds->revents != 0)
-                               n++;
                        fd_putfile(fds->fd);
                }
+               if (revents) {
+                       fds->revents = revents;
+                       n++;
+               }
        }
        *retval = n;
        return (0);
@@ -626,7 +643,9 @@
                int n;
 
                for (n = 0; n < 3; n++) {
-                       if ((fds[idx] & fbit) != 0 && (sel_flag[n] & events)) {
+                       if ((fds[idx] & fbit) != 0 &&
+                           (ofds[idx] & fbit) == 0 &&
+                           (sel_flag[n] & events)) {
                                ofds[idx] |= fbit;
                                ret++;
                        }
@@ -638,8 +657,9 @@
                int revents = events & (pfd->events | POLLERR | POLLHUP);
 
                if (revents) {
+                       if (pfd->revents == 0)
+                               ret = 1;
                        pfd->revents |= revents;
-                       ret = 1;
                }
        }
        /* Check whether there are any events to return. */



Home | Main Index | Thread Index | Old Index