82563: fixes TX overrun/replenish bug
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 17 Sep 2014 23:11:16 +0000 (16:11 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 17 Sep 2014 23:17:00 +0000 (16:17 -0700)
When replenish was called without being allowed to sleep, it could bail out of
its loop early and fail to update the RDT (both ctlr and HW).  If it bails out
after having replenished some buffers, the RDT no longer matches where block
pointers should/should not be.

For reference, RDT points to an empty buffer (bp = 0).  RDH points to the first
full one.  Replenish will fill up to one slot prior to RDH, so that the queue
is full when there is one empty spot at RDH - 1.

kern/drivers/net/ether82563.c

index b0522b4..d432b7b 100644 (file)
@@ -1071,15 +1071,15 @@ static void i82563tproc(void *v)
 
 static int i82563replenish(struct ctlr *ctlr, int maysleep)
 {
-       unsigned int rdt, m, i;
+       unsigned int rdt, m;
        struct block *bp;
        Rbpool *p;
        Rd *rd;
+       int retval = 0;
 
        rdt = ctlr->rdt;
        m = ctlr->nrd;
        p = rbtab + ctlr->pool;
-       i = 0;
        for (; NEXT_RING(rdt, m) != ctlr->rdh; rdt = NEXT_RING(rdt, m)) {
                rd = &ctlr->rdba[rdt];
                if (ctlr->rb[rdt] != NULL) {
@@ -1092,27 +1092,29 @@ redux:
                        if (rdt - ctlr->rdh >= 16)
                                break;
                        printd("%s: pool %d: no rx buffers\n", cname(ctlr), ctlr->pool);
-                       if (maysleep == 0)
-                               return -1;
+                       if (maysleep == 0) {
+                               retval = -1;
+                               goto out;
+                       }
                        spin_lock_irqsave(&p->lock);
                        p->starve = 1;
                        spin_unlock_irqsave(&p->lock);
                        rendez_sleep(&p->r, icansleep, p);
                        goto redux;
                }
-               i++;
                ctlr->rb[rdt] = bp;
                rd->addr[0] = paddr_low32(bp->rp);
                rd->addr[1] = paddr_high32(bp->rp);
                rd->status = 0;
                ctlr->rdfree++;
        }
-       if (i != 0) {
+out:
+       if (ctlr->rdt != rdt) {
                ctlr->rdt = rdt;
                wmb_f();
                csr32w(ctlr, Rdt, rdt);
        }
-       return 0;
+       return retval;
 }
 
 static void i82563rxinit(struct ctlr *ctlr)