Index: sys/kern/sys_generic.c =================================================================== --- sys/kern/sys_generic.c +++ sys/kern/sys_generic.c @@ -160,6 +160,8 @@ static uma_zone_t selfd_zone; static struct mtx_pool *mtxpool_select; +static struct mtx seldeferred_mtx; +static struct selfdlist global_deferred; #ifdef __LP64__ size_t @@ -1688,12 +1690,12 @@ { STAILQ_REMOVE(&stp->st_selq, sfp, selfd, sf_link); if (sfp->sf_si != NULL) { - mtx_lock(sfp->sf_mtx); + mtx_lock_spin(sfp->sf_mtx); if (sfp->sf_si != NULL) { TAILQ_REMOVE(&sfp->sf_si->si_tdlist, sfp, sf_threads); refcount_release(&sfp->sf_refs); } - mtx_unlock(sfp->sf_mtx); + mtx_unlock_spin(sfp->sf_mtx); } if (refcount_release(&sfp->sf_refs)) uma_zfree(selfd_zone, sfp); @@ -1701,8 +1703,7 @@ /* Drain the waiters tied to all the selfd belonging the specified selinfo. */ void -seldrain(sip) - struct selinfo *sip; +seldrain(struct selinfo *sip) { /* @@ -1720,13 +1721,12 @@ * Record a select request. */ void -selrecord(selector, sip) - struct thread *selector; - struct selinfo *sip; +selrecord(struct thread *selector, struct selinfo *sip) { struct selfd *sfp; struct seltd *stp; struct mtx *mtxp; + struct selfdlist trash; stp = selector->td_sel; /* @@ -1734,6 +1734,7 @@ */ if (stp->st_flags & SELTD_RESCAN) return; + /* * Grab one of the preallocated descriptors. */ @@ -1757,7 +1758,7 @@ /* * Now that we've locked the sip, check for initialization. */ - mtx_lock(mtxp); + mtx_lock_spin(mtxp); if (sip->si_mtx == NULL) { sip->si_mtx = mtxp; TAILQ_INIT(&sip->si_tdlist); @@ -1766,22 +1767,30 @@ * Add this thread to the list of selfds listening on this selinfo. */ TAILQ_INSERT_TAIL(&sip->si_tdlist, sfp, sf_threads); - mtx_unlock(sip->si_mtx); + mtx_unlock_spin(sip->si_mtx); + + if (!TAILQ_EMPTY(&global_deferred)) { + TAILQ_INIT(&trash); + mtx_lock_spin(&seldeferred_mtx); + TAILQ_CONCAT(&trash, &global_deferred, sf_threads); + mtx_unlock_spin(&seldeferred_mtx); + while ((sfp = TAILQ_FIRST(&trash)) != NULL) { + TAILQ_REMOVE(&trash, sfp, sf_threads); + uma_zfree(selfd_zone, sfp); + } + } } /* Wake up a selecting thread. */ void -selwakeup(sip) - struct selinfo *sip; +selwakeup(struct selinfo *sip) { doselwakeup(sip, -1); } /* Wake up a selecting thread, and set its priority. */ void -selwakeuppri(sip, pri) - struct selinfo *sip; - int pri; +selwakeuppri(struct selinfo *sip, int pri) { doselwakeup(sip, pri); } @@ -1790,21 +1799,23 @@ * Do a wakeup when a selectable event occurs. */ static void -doselwakeup(sip, pri) - struct selinfo *sip; - int pri; +doselwakeup(struct selinfo *sip, int pri) { struct selfd *sfp; struct selfd *sfn; struct seltd *stp; + struct thread *td; + struct selfdlist trash; /* If it's not initialized there can't be any waiters. */ if (sip->si_mtx == NULL) return; + TAILQ_INIT(&trash); + td = curthread; /* * Locking the selinfo locks all selfds associated with it. */ - mtx_lock(sip->si_mtx); + mtx_lock_spin(sip->si_mtx); TAILQ_FOREACH_SAFE(sfp, &sip->si_tdlist, sf_threads, sfn) { /* * Once we remove this sfp from the list and clear the @@ -1813,14 +1824,24 @@ TAILQ_REMOVE(&sip->si_tdlist, sfp, sf_threads); sfp->sf_si = NULL; stp = sfp->sf_td; - mtx_lock(&stp->st_mtx); + mtx_lock_spin(&stp->st_mtx); stp->st_flags |= SELTD_PENDING; cv_broadcastpri(&stp->st_wait, pri); - mtx_unlock(&stp->st_mtx); + mtx_unlock_spin(&stp->st_mtx); if (refcount_release(&sfp->sf_refs)) - uma_zfree(selfd_zone, sfp); + TAILQ_INSERT_HEAD(&trash, sfp, sf_threads); + } + mtx_unlock_spin(sip->si_mtx); + if (td->td_critnest || td->td_intr_nesting_level) { + mtx_lock_spin(&seldeferred_mtx); + TAILQ_CONCAT(&global_deferred, &trash, sf_threads); + mtx_unlock_spin(&seldeferred_mtx); + return; + } + while ((sfp = TAILQ_FIRST(&trash)) != NULL) { + TAILQ_REMOVE(&trash, sfp, sf_threads); + uma_zfree(selfd_zone, sfp); } - mtx_unlock(sip->si_mtx); } static void @@ -1831,7 +1852,7 @@ if ((stp = td->td_sel) != NULL) goto out; td->td_sel = stp = malloc(sizeof(*stp), M_SELECT, M_WAITOK|M_ZERO); - mtx_init(&stp->st_mtx, "sellck", NULL, MTX_DEF); + mtx_init(&stp->st_mtx, "sellck", NULL, MTX_SPIN); cv_init(&stp->st_wait, "select"); out: stp->st_flags = 0; @@ -1849,13 +1870,13 @@ * An event of interest may occur while we do not hold the seltd * locked so check the pending flag before we sleep. */ - mtx_lock(&stp->st_mtx); + mtx_lock_spin(&stp->st_mtx); /* * Any further calls to selrecord will be a rescan. */ stp->st_flags |= SELTD_RESCAN; if (stp->st_flags & SELTD_PENDING) { - mtx_unlock(&stp->st_mtx); + mtx_unlock_spin(&stp->st_mtx); return (0); } if (sbt == 0) @@ -1865,7 +1886,7 @@ sbt, precision, C_ABSOLUTE); else error = cv_wait_sig(&stp->st_wait, &stp->st_mtx); - mtx_unlock(&stp->st_mtx); + mtx_unlock_spin(&stp->st_mtx); return (error); } @@ -1911,7 +1932,9 @@ selfd_zone = uma_zcreate("selfd", sizeof(struct selfd), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); - mtxpool_select = mtx_pool_create("select mtxpool", 128, MTX_DEF); + mtxpool_select = mtx_pool_create("select mtxpool", 128, MTX_SPIN); + TAILQ_INIT(&global_deferred); + mtx_init(&seldeferred_mtx, "seltrash", NULL, MTX_SPIN); } /*