Commit 2f652923 authored by François Romieu's avatar François Romieu Committed by Jeff Garzik

[PATCH] via-velocity: received ring wrong index and missing barriers

There were several receive ring related bugs.

In velocity_give_many_rx_descs(), index calculation was incorrect.
This and bugs in velocity_rx_srv() described in the following paragraph
caused packet loss, truncation and infinite error interrupt generation.

In velocity_rx_srv(), velocity_rx_refill() could be called without any
dirty slot.  With proper timing, This can result in refilling yet
unreceived packets and pushing dirty pointer ahead of the current pointer.
And vptr->rd_curr which is used by velocity_rx_refill() was updated after
calling velocity_rx_refill() thus screwing receive descriptor ring.
Also, between checking owner and reading the packet, rmb() is missing.
Signed-off-by: default avatarTejun Heo <tj@home-tj.org>
parent 9030eb03
...@@ -1018,8 +1018,8 @@ static inline void velocity_give_many_rx_descs(struct velocity_info *vptr) ...@@ -1018,8 +1018,8 @@ static inline void velocity_give_many_rx_descs(struct velocity_info *vptr)
wmb(); wmb();
unusable = vptr->rd_filled | 0x0003; unusable = vptr->rd_filled & 0x0003;
dirty = vptr->rd_dirty - unusable + 1; dirty = vptr->rd_dirty - unusable;
for (avail = vptr->rd_filled & 0xfffc; avail; avail--) { for (avail = vptr->rd_filled & 0xfffc; avail; avail--) {
dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1;
vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC; vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC;
...@@ -1232,15 +1232,17 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status) ...@@ -1232,15 +1232,17 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status)
int rd_curr = vptr->rd_curr; int rd_curr = vptr->rd_curr;
int works = 0; int works = 0;
while (1) { do {
struct rx_desc *rd = vptr->rd_ring + rd_curr; struct rx_desc *rd = vptr->rd_ring + rd_curr;
if (!vptr->rd_info[rd_curr].skb || (works++ > 15)) if (!vptr->rd_info[rd_curr].skb)
break; break;
if (rd->rdesc0.owner == OWNED_BY_NIC) if (rd->rdesc0.owner == OWNED_BY_NIC)
break; break;
rmb();
/* /*
* Don't drop CE or RL error frame although RXOK is off * Don't drop CE or RL error frame although RXOK is off
*/ */
...@@ -1263,14 +1265,15 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status) ...@@ -1263,14 +1265,15 @@ static int velocity_rx_srv(struct velocity_info *vptr, int status)
rd_curr++; rd_curr++;
if (rd_curr >= vptr->options.numrx) if (rd_curr >= vptr->options.numrx)
rd_curr = 0; rd_curr = 0;
} } while (++works <= 15);
if (velocity_rx_refill(vptr) < 0) { vptr->rd_curr = rd_curr;
if (works > 0 && velocity_rx_refill(vptr) < 0) {
VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR
"%s: rx buf allocation failure\n", vptr->dev->name); "%s: rx buf allocation failure\n", vptr->dev->name);
} }
vptr->rd_curr = rd_curr;
VAR_USED(stats); VAR_USED(stats);
return works; return works;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment