ether82563: avoid deadlock due to icansleep() trying to acquire Rbpool.Lock

icansleep() violates the lock ordering due to the following cases:

rbfree(): ilock(Rbpool.Lock) -> wakeup(): spli(), lock(Rbpool.Rendez)
sleep(): splhi(), lock(Rbpool.Rendez) -> icansleep(): ilock(Rbpool.Lock)

erik fixed this moving the wakeup() out of the ilock() in rbfree(),
but i think it is an error to try acquiering a ilock in sleeps wait
condition function in general.

so this is what we do:

in the icansleep() function, we check for the *real* event we care about;
that is, if theres a buffer available in the Rbpool. this is to handle
the case when rbfree() makes a buffer available *before* it sees us
setting p->starve = 1.

p->starve is now just used to gate rbfree() from calling wakeup() as
an optimization.

this might cause spurious wakeups but they are not a problem. missed
wakeups is the thing we have to prevent.
This commit is contained in:
cinap_lenrek 2013-07-26 01:51:03 +02:00
parent 2759b81dec
commit ac52599eef

View file

@ -792,14 +792,9 @@ static int
icansleep(void *v)
{
Rbpool *p;
int r;
p = v;
ilock(p);
r = p->starve == 0;
iunlock(p);
return r;
return p->b != nil;
}
static Block*
@ -1058,9 +1053,7 @@ i82563replenish(Ctlr *ctlr, int maysleep)
print("i82563%d: no rx buffers\n", ctlr->pool);
if(maysleep == 0)
return -1;
ilock(p);
p->starve = 1;
iunlock(p);
sleep(p, icansleep, p);
goto redux;
}