cvs commit: src/sys/dev/md md.c

Don Lewis truckman at FreeBSD.org
Thu Sep 16 21:27:48 PDT 2004


On 16 Sep, Nate Lawson wrote:
> Pawel Jakub Dawidek wrote:
>> On Thu, Sep 16, 2004 at 12:40:23PM -0700, Nate Lawson wrote:
>> +> >@@ -379,9 +379,8 @@
>> +> > 	bp->bio_bcount = bp->bio_length;
>> +> > 	mtx_lock(&sc->queue_mtx);
>> +> > 	bioq_disksort(&sc->bio_queue, bp);
>> +> >-	mtx_unlock(&sc->queue_mtx);
>> +> >-
>> +> > 	wakeup(sc);
>> +> >+	mtx_unlock(&sc->queue_mtx);
>> +> > }
>> +> 
>> +> I think the original order is correct since you can occur 2 switches if 
>> +> you wakeup first and then unlock.

I think that is is only true of there is an idle CPU and
ADAPTIVE_MUTEXES are not enabled.  If there is not an idle CPU, the
other thread won't start running for a while.  If ADAPTIVE_MUTEXES are
enabled and there is an idle CPU, the other thread will start executing,
but it will spin for a a short period of time until the mutex is
unlocked.

>> Nope, this order was wrong:
>> 
>> thread1		thread2
>> -----------------------
>> mtx_lock(mtx)
>> ...
>> mtx_unlock(mtx)
>> 		mtx_lock(mtx)
>> wakeup(ptr)
>> 		msleep(ptr, mtx) <- Race, it will be never woken up.
> 
> You still have a race, like this:
> 
> thread1               thread2
> -----------------------------
> mtx_lock(mtx)
> wakeup(ptr)
> mtx_unlock(mtx)
>                        mtx_lock(mtx)
>                        msleep(ptr, mtx)
> 
> You should be checking the work condition in thread 2 while holding the 
> mutex but before going to sleep.  Adding work to the queue happens in 
> thread 1 where you write "..." and that is done with the mutex held so 
> there is no race.  The full diagram with this detail included is:
> 
> thread1               thread2
> -----------------------------
> mtx_lock(mtx)
> add work to queue
> mtx_unlock(mtx)
>                        mtx_lock(mtx)
> wakeup(ptr)
>                        check queue for work item
>                        if (!work item)
>                            msleep(ptr, mtx)
>                        else
>                            dequeue work item and loop
> 
> Since the work item is added in thread1 with the mutex held, the check 
> for it in thread2 is safe and race-free.  A wakeup is only there to 
> kickstart thread2 if it's asleep.  If it's running, it needs to check 
> atomically that there is no work before sleeping.  If it doesn't do 
> this, it's a bug.

What about the situation where you know that it is unlikely that there
is another thread sleeping on ptr?   Thread 2 would set a flag
(protected by mtx) recording that it wants to be awakened, and thread 1
would only call wakeup() if the flag was set.  In this case thread 1
would have to hold the mutex until after the wakeup(), otherwise there
would be a race on the wakeup wanted flag.

How expensive is an unnecessary wakeup()?




More information about the cvs-src mailing list