From a5fcdfa4fdd1be6cbf7ef2b25cdb6b660888925f Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Fri, 17 Aug 2012 12:40:09 -0700 Subject: [PATCH] kernel - Fix condvar races * Interlocked sleep was not using the PINTERLOCKED flag. * Misc other adjustments. --- sys/kern/kern_condvar.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/sys/kern/kern_condvar.c b/sys/kern/kern_condvar.c index a23be8b13e..8ec0c3e857 100644 --- a/sys/kern/kern_condvar.c +++ b/sys/kern/kern_condvar.c @@ -18,20 +18,27 @@ cv_destroy(struct cv *c) } int -_cv_timedwait(struct cv *c, struct lock *l, int timo, int wakesig) +_cv_timedwait(struct cv *c, struct lock *lk, int timo, int wakesig) { int flags = wakesig ? PCATCH : 0; int error; - spin_lock(&c->cv_lock); + /* + * Can interlock without critical section/spinlock as long + * as we don't block before calling *sleep(). PINTERLOCKED + * must be passed to the *sleep() to use the manual interlock + * (else a new one is created which opens a timing race). + */ tsleep_interlock(c, flags); + + spin_lock(&c->cv_lock); c->cv_waiters++; spin_unlock(&c->cv_lock); - if (l != NULL) - lockmgr(l, LK_RELEASE); - error = tsleep(c, flags, c->cv_desc, timo); - if (l != NULL) - lockmgr(l, LK_EXCLUSIVE); + + if (lk) + error = lksleep(c, lk, flags | PINTERLOCKED, c->cv_desc, timo); + else + error = tsleep(c, flags | PINTERLOCKED, c->cv_desc, timo); return (error); } @@ -40,19 +47,17 @@ void _cv_signal(struct cv *c, int broadcast) { spin_lock(&c->cv_lock); - if (c->cv_waiters == 0) - goto out; - - if (broadcast) { + if (c->cv_waiters == 0) { + spin_unlock(&c->cv_lock); + } else if (broadcast) { c->cv_waiters = 0; + spin_unlock(&c->cv_lock); /* must unlock first */ wakeup(c); } else { c->cv_waiters--; + spin_unlock(&c->cv_lock); /* must unlock first */ wakeup_one(c); } - -out: - spin_unlock(&c->cv_lock); } int -- 2.41.0