Commit 2d38caba authored by Lennert Buytenhek's avatar Lennert Buytenhek Committed by Jeff Garzik

[PATCH] ep93xx_eth: fix RX/TXstatus ring full handling

Ray Lehtiniemi reported that an incoming UDP packet flood can lock up
the ep93xx ethernet driver.  Herbert Valerio Riedel noted that due to
the way ep93xx_eth manages the RX/TXstatus rings, it cannot distinguish
a full ring from an empty one, and correctly suggested that this was
likely to be causing this lockup to occur.

Instead of looking at the hardware's RX/TXstatus ring write pointers
to determine when to stop reading from those rings, we should just check
every individual RX/TXstatus descriptor's valid bit instead, since there
is no other way to distinguish an empty ring from a full ring, and if
there is a descriptor waiting, we take the hit of reading the descriptor
from memory anyway.
Signed-off-by: default avatarLennert Buytenhek <buytenh@wantstofly.org>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent d5b9b787
...@@ -193,12 +193,9 @@ static struct net_device_stats *ep93xx_get_stats(struct net_device *dev) ...@@ -193,12 +193,9 @@ static struct net_device_stats *ep93xx_get_stats(struct net_device *dev)
static int ep93xx_rx(struct net_device *dev, int *budget) static int ep93xx_rx(struct net_device *dev, int *budget)
{ {
struct ep93xx_priv *ep = netdev_priv(dev); struct ep93xx_priv *ep = netdev_priv(dev);
int tail_offset;
int rx_done; int rx_done;
int processed; int processed;
tail_offset = rdl(ep, REG_RXSTSQCURADD) - ep->descs_dma_addr;
rx_done = 0; rx_done = 0;
processed = 0; processed = 0;
while (*budget > 0) { while (*budget > 0) {
...@@ -211,28 +208,23 @@ static int ep93xx_rx(struct net_device *dev, int *budget) ...@@ -211,28 +208,23 @@ static int ep93xx_rx(struct net_device *dev, int *budget)
entry = ep->rx_pointer; entry = ep->rx_pointer;
rstat = ep->descs->rstat + entry; rstat = ep->descs->rstat + entry;
if ((void *)rstat - (void *)ep->descs == tail_offset) {
rstat0 = rstat->rstat0;
rstat1 = rstat->rstat1;
if (!(rstat0 & RSTAT0_RFP) || !(rstat1 & RSTAT1_RFP)) {
rx_done = 1; rx_done = 1;
break; break;
} }
rstat0 = rstat->rstat0;
rstat1 = rstat->rstat1;
rstat->rstat0 = 0; rstat->rstat0 = 0;
rstat->rstat1 = 0; rstat->rstat1 = 0;
if (!(rstat0 & RSTAT0_RFP))
printk(KERN_CRIT "ep93xx_rx: buffer not done "
" %.8x %.8x\n", rstat0, rstat1);
if (!(rstat0 & RSTAT0_EOF)) if (!(rstat0 & RSTAT0_EOF))
printk(KERN_CRIT "ep93xx_rx: not end-of-frame " printk(KERN_CRIT "ep93xx_rx: not end-of-frame "
" %.8x %.8x\n", rstat0, rstat1); " %.8x %.8x\n", rstat0, rstat1);
if (!(rstat0 & RSTAT0_EOB)) if (!(rstat0 & RSTAT0_EOB))
printk(KERN_CRIT "ep93xx_rx: not end-of-buffer " printk(KERN_CRIT "ep93xx_rx: not end-of-buffer "
" %.8x %.8x\n", rstat0, rstat1); " %.8x %.8x\n", rstat0, rstat1);
if (!(rstat1 & RSTAT1_RFP))
printk(KERN_CRIT "ep93xx_rx: buffer1 not done "
" %.8x %.8x\n", rstat0, rstat1);
if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry) if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry)
printk(KERN_CRIT "ep93xx_rx: entry mismatch " printk(KERN_CRIT "ep93xx_rx: entry mismatch "
" %.8x %.8x\n", rstat0, rstat1); " %.8x %.8x\n", rstat0, rstat1);
...@@ -301,13 +293,8 @@ static int ep93xx_rx(struct net_device *dev, int *budget) ...@@ -301,13 +293,8 @@ static int ep93xx_rx(struct net_device *dev, int *budget)
static int ep93xx_have_more_rx(struct ep93xx_priv *ep) static int ep93xx_have_more_rx(struct ep93xx_priv *ep)
{ {
struct ep93xx_rstat *rstat; struct ep93xx_rstat *rstat = ep->descs->rstat + ep->rx_pointer;
int tail_offset; return !!((rstat->rstat0 & RSTAT0_RFP) && (rstat->rstat1 & RSTAT1_RFP));
rstat = ep->descs->rstat + ep->rx_pointer;
tail_offset = rdl(ep, REG_RXSTSQCURADD) - ep->descs_dma_addr;
return !((void *)rstat - (void *)ep->descs == tail_offset);
} }
static int ep93xx_poll(struct net_device *dev, int *budget) static int ep93xx_poll(struct net_device *dev, int *budget)
...@@ -379,10 +366,8 @@ static int ep93xx_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -379,10 +366,8 @@ static int ep93xx_xmit(struct sk_buff *skb, struct net_device *dev)
static void ep93xx_tx_complete(struct net_device *dev) static void ep93xx_tx_complete(struct net_device *dev)
{ {
struct ep93xx_priv *ep = netdev_priv(dev); struct ep93xx_priv *ep = netdev_priv(dev);
int tail_offset;
int wake; int wake;
tail_offset = rdl(ep, REG_TXSTSQCURADD) - ep->descs_dma_addr;
wake = 0; wake = 0;
spin_lock(&ep->tx_pending_lock); spin_lock(&ep->tx_pending_lock);
...@@ -393,15 +378,13 @@ static void ep93xx_tx_complete(struct net_device *dev) ...@@ -393,15 +378,13 @@ static void ep93xx_tx_complete(struct net_device *dev)
entry = ep->tx_clean_pointer; entry = ep->tx_clean_pointer;
tstat = ep->descs->tstat + entry; tstat = ep->descs->tstat + entry;
if ((void *)tstat - (void *)ep->descs == tail_offset)
break;
tstat0 = tstat->tstat0; tstat0 = tstat->tstat0;
if (!(tstat0 & TSTAT0_TXFP))
break;
tstat->tstat0 = 0; tstat->tstat0 = 0;
if (!(tstat0 & TSTAT0_TXFP))
printk(KERN_CRIT "ep93xx_tx_complete: buffer not done "
" %.8x\n", tstat0);
if (tstat0 & TSTAT0_FA) if (tstat0 & TSTAT0_FA)
printk(KERN_CRIT "ep93xx_tx_complete: frame aborted " printk(KERN_CRIT "ep93xx_tx_complete: frame aborted "
" %.8x\n", tstat0); " %.8x\n", tstat0);
......
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