msleep(9) and recursed mutex

Hans Petter Selasky hselasky at c2i.net
Mon Oct 11 08:27:05 PDT 2004


On Mon, Oct 11, 2004 at 03:10:45PM +0200, damien.bergamini at free.fr wrote:
> msleep(9) behaves strangely with recursed mutexes.
> In the attached code, calling foo_f() will make the kernel hang for
> two seconds.
> It seems like msleep does not release the mtx mutex completely but
> simply decrement its reference count by one. When calling msleep, the
> mtx mutex is still held which prevent the interrupt from happening.
> msleep will then return error code EWOULDBLOCK after two seconds.
> If I remove calls to mtx_lock/unlock from function foo_g(), it works
> without problem but this is not a solution since foo_g() can be
> called outside of function foo_f().
> Of course, the mtx mutex was created with the MTX_RECURSE flag set.
> 
> Is it an expected behaviour? In msleep(9) it is said:
> "The msleep() function is a variation on tsleep.  The parameter mtx
> is a mutex which will be released before sleeping and reacquired
> before msleep() returns."
>
> Seems like the mutex is not *really* released if it recurses.
>

Only the "Giant" mutex is released if it recurses. By the way there is
a bug in msleep:

mtx_lock(&Giant);
msleep(xxx, &Giant, 0, "foo", 0);
mtx_unlock(&Giant);

doesn't work. See attached patch.

> 
> Thanks,
> Damien Bergamini
> 
> ----
> 
> void foo_intr(void *arg)
> {
>     struct foo_softc *sc = arg;
> 
>     mtx_lock(&sc->mtx);
>     ...
>     wakeup(sc);
>     ...
>     mtx_unlock(&sc->mtx);
> }
> 
> void foo_f(struct foo_softc *sc)
> {
>     mtx_lock(&sc->mtx);
>     ...
>     foo_g(sc);
>     ...
>     mtx_unlock(&sc->mtx);
> }
> 
> void foo_g(struct foo_softc *sc)
> {
>     /* mtx will recurse if called from foo_f() */
>     mtx_lock(&sc->mtx);
>     ...
>     /* do something that will make hardware raise an interrupt */
>     ...
>     /* wait at most 2 second for the interrupt */
>     msleep(sc, &sc->mtx, 0, "foo", 2 * hz);
>     ...
>     mtx_unlock(&sc->mtx);
> }

-HPS
-------------- next part --------------
*** src/sys/kern/kern_synch.c.ref	Wed Sep 29 14:21:09 2004
--- src/sys/kern/kern_synch.c	Wed Sep 29 14:22:25 2004
***************
*** 182,193 ****
  	CTR5(KTR_PROC, "msleep: thread %p (pid %ld, %s) on %s (%p)",
  	    (void *)td, (long)p->p_pid, p->p_comm, wmesg, ident);
  
- 	DROP_GIANT();
  	if (mtx != NULL) {
  		mtx_assert(mtx, MA_OWNED | MA_NOTRECURSED);
  		WITNESS_SAVE(&mtx->mtx_object, mtx);
  		mtx_unlock(mtx);
  	}
  
  	/*
  	 * We put ourselves on the sleep queue and start our timeout
--- 182,194 ----
  	CTR5(KTR_PROC, "msleep: thread %p (pid %ld, %s) on %s (%p)",
  	    (void *)td, (long)p->p_pid, p->p_comm, wmesg, ident);
  
  	if (mtx != NULL) {
  		mtx_assert(mtx, MA_OWNED | MA_NOTRECURSED);
  		WITNESS_SAVE(&mtx->mtx_object, mtx);
  		mtx_unlock(mtx);
  	}
+ 	/* drop Giant after mtx in case mtx == &Giant */
+ 	DROP_GIANT();
  
  	/*
  	 * We put ourselves on the sleep queue and start our timeout


More information about the freebsd-hackers mailing list