etheryuk: fix lockups (thanks burnzez for testing)

according to a comment in linux driver, reading Isrc2
register caused interrupts to be disabled. we used
to read Isrc2 in ifstat() and it was confirmed that
reading ifstat locks up ethernet. removing the Isrc2
read in ifstats, and also reenable interrupts after
reading Isrc2 when the interrupt was not for us.
(this is from the linux driver)

in replenish(), set ring software write pointer (Sring.wp)
*before* the hardware write index register. otherwise
rx() could get status notification for completed
receive but wont find the rx descriptor in the ring.

handle uint wrap arround when calculating ring fill
count and remaining count.
This commit is contained in:
cinap_lenrek 2014-01-12 12:08:10 +01:00
parent 5d9f0ed326
commit a5525457bd

View file

@ -49,6 +49,7 @@ enum {
Isrc2 = 0x001c/4, Isrc2 = 0x001c/4,
Eisr = 0x0024/4, Eisr = 0x0024/4,
Lisr = 0x0028/4, /* leave isr */ Lisr = 0x0028/4, /* leave isr */
Icr = 0x002c/4,
Macadr = 0x0100, /* mac address 2ports*3 */ Macadr = 0x0100, /* mac address 2ports*3 */
Pmd = 0x0119, Pmd = 0x0119,
Maccfg = 0x011a, Maccfg = 0x011a,
@ -831,7 +832,7 @@ getnslot(Sring *r, uint *wp, Status **t, uint n)
{ {
int i; int i;
if(r->rp + r->m - (n - 1) - wp[0] & ~r->m) if(r->m - (int)(wp[0] - r->rp) < n)
return -1; return -1;
for(i = 0; i < n; i++) for(i = 0; i < n; i++)
t[i] = r->r + (wp[0]++ & r->m); t[i] = r->r + (wp[0]++ & r->m);
@ -1150,7 +1151,6 @@ tproc(void *v)
while(getnslot(r, &r->wp, tab, 1 + is64()) == -1) while(getnslot(r, &r->wp, tab, 1 + is64()) == -1)
starve(&c->txmit); starve(&c->txmit);
t = tab[is64()]; t = tab[is64()];
assert(c->tbring[t - r->r] == nil);
c->tbring[t - r->r] = b; c->tbring[t - r->r] = b;
if(is64()){ if(is64()){
Status *t = tab[0]; Status *t = tab[0];
@ -1205,7 +1205,7 @@ rxscrew(Ether *e, Sring *r, Status *t, uint wp)
Ctlr *c; Ctlr *c;
c = e->ctlr; c = e->ctlr;
if(wp - r->rp > r->cnt){ if((int)(wp - r->rp) >= r->cnt){
print("rxscrew1 wp %ud(%ud) rp %ud %lud\n", wp, r->wp, r->rp, t-r->r); print("rxscrew1 wp %ud(%ud) rp %ud %lud\n", wp, r->wp, r->rp, t-r->r);
return -1; return -1;
} }
@ -1245,7 +1245,6 @@ replenish(Ether *e, Ctlr *c)
freeb(b); freeb(b);
break; break;
} }
assert(c->rbring[t - r->r] == nil);
c->rbring[t - r->r] = b; c->rbring[t - r->r] = b;
if(is64()){ if(is64()){
@ -1260,9 +1259,9 @@ replenish(Ether *e, Ctlr *c)
t->op = Opkt | Hw; t->op = Opkt | Hw;
} }
if(n>0){ if(n>0){
r->wp = wp;
sfence(); sfence();
prwrite16(c, Qr + Pputidx, wp & r->m); prwrite16(c, Qr + Pputidx, wp & r->m);
r->wp = wp;
dprint("yuk: replenish %d %ud-%ud [%d-%d]\n", n, r->rp, wp, r->rp&r->m, wp&r->m); dprint("yuk: replenish %d %ud-%ud [%d-%d]\n", n, r->rp, wp, r->rp&r->m, wp&r->m);
} }
return n == lim; return n == lim;
@ -1380,14 +1379,14 @@ link(Ether *e)
static void static void
txcleanup(Ctlr *c, uint end) txcleanup(Ctlr *c, uint end)
{ {
uint rp0, rp; uint rp;
Block *b; Block *b;
Sring *r; Sring *r;
Status *t; Status *t;
r = &c->tx; r = &c->tx;
rp0 = r->rp & r->m; end &= r->m;
for(rp = rp0; rp != end; rp = r->rp & r->m){ for(rp = r->rp & r->m; rp != end; rp = r->rp & r->m){
t = r->r + rp; t = r->r + rp;
r->rp++; r->rp++;
if((t->ctl & Eop) == 0) if((t->ctl & Eop) == 0)
@ -1397,7 +1396,6 @@ txcleanup(Ctlr *c, uint end)
if(b != nil) if(b != nil)
freeb(b); freeb(b);
} }
if(r->wp - r->rp > 16)
unstarve(&c->txmit); unstarve(&c->txmit);
} }
@ -1412,14 +1410,17 @@ rx(Ether *e, uint l, uint x, uint flag)
c = e->ctlr; c = e->ctlr;
r = &c->rx; r = &c->rx;
for(rp = r->rp;;){ for(rp = r->rp;;){
if(rp == r->wp) if(rp == r->wp){
print("#l%d: yuk rx empty\n", e->ctlrno);
return; return;
}
i = rp++&r->m; i = rp++&r->m;
b = c->rbring[i]; b = c->rbring[i];
c->rbring[i] = nil; c->rbring[i] = nil;
if(b != nil) if(b != nil)
break; break;
} }
r->rp = rp;
cnt = x>>16 & 0x7fff; cnt = x>>16 & 0x7fff;
if((cnt != l || x&Rxerror) && if((cnt != l || x&Rxerror) &&
!(c->type == Yukfep && c->rev == 0)){ !(c->type == Yukfep && c->rev == 0)){
@ -1430,7 +1431,7 @@ rx(Ether *e, uint l, uint x, uint flag)
b->flag |= flag; b->flag |= flag;
etheriq(e, b, 1); etheriq(e, b, 1);
} }
r->rp = rp; unstarve(&c->rxmit);
} }
static uint static uint
@ -1446,7 +1447,7 @@ cksum(Ctlr *c, uint ck, uint css)
static void static void
sring(Ether *e) sring(Ether *e)
{ {
uint i, p, lim, op, l, x; uint i, lim, op, l, x;
Ctlr *c; Ctlr *c;
Sring *r; Sring *r;
Status *s; Status *s;
@ -1455,7 +1456,6 @@ sring(Ether *e)
c = e->ctlr; c = e->ctlr;
r = &c->status; r = &c->status;
lim = c->reg16[Stathd] & r->m; lim = c->reg16[Stathd] & r->m;
p = 0;
for(;;){ for(;;){
i = r->rp & r->m; i = r->rp & r->m;
if(i == lim){ if(i == lim){
@ -1477,7 +1477,6 @@ sring(Ether *e)
x = getle(s->status, 4); x = getle(s->status, 4);
rx(e, l, x, cksum(c, ck, s->ctl)); rx(e, l, x, cksum(c, ck, s->ctl));
ck = Badck; ck = Badck;
p++;
break; break;
case Otxidx: case Otxidx:
l = getle(s->l, 2); l = getle(s->l, 2);
@ -1496,8 +1495,6 @@ sring(Ether *e)
s->op = 0; s->op = 0;
r->rp++; r->rp++;
} }
if(p != 0)
unstarve(&c->rxmit);
c->reg[Statctl] = Statirqclr; c->reg[Statctl] = Statirqclr;
} }
@ -1674,8 +1671,13 @@ interrupt(Ureg*, void *v)
e = v; e = v;
c = e->ctlr; c = e->ctlr;
/* reading Isrc2 masks interrupts */
cause = c->reg[Isrc2]; cause = c->reg[Isrc2];
if(cause != 0 && cause != ~0) if(cause == 0 || cause == ~0){
/* reenable interrupts */
c->reg[Icr] = 2;
return;
}
unstarve(&c->iproc); unstarve(&c->iproc);
} }
@ -1755,7 +1757,6 @@ ifstat(Ether *e0, void *a, long n, ulong offset)
p = seprint(p, e, "%s\t%ud\n", stattab[i].name, u); p = seprint(p, e, "%s\t%ud\n", stattab[i].name, u);
} }
p = seprint(p, e, "stat %.4ux ctl %.3ux\n", gmacread(c, Stat), gmacread(c, Ctl)); p = seprint(p, e, "stat %.4ux ctl %.3ux\n", gmacread(c, Stat), gmacread(c, Ctl));
p = seprint(p, e, "irq %.8ux\n", c->reg[Isrc2]);
p = seprint(p, e, "pref %.8ux %.4ux\n", prread32(c, Qr + Pctl), prread16(c, Qr + Pgetidx)); p = seprint(p, e, "pref %.8ux %.4ux\n", prread32(c, Qr + Pctl), prread16(c, Qr + Pgetidx));
if(debug){ if(debug){
p = dumppci(c, p, e); p = dumppci(c, p, e);