summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDale Farnsworth <dale@farnsworth.org>2007-01-23 09:52:25 -0700
committerJeff Garzik <jeff@garzik.org>2007-01-23 16:28:53 -0500
commitd344bff9c36db17dc4765215495aaa7212c1eb6c (patch)
tree707848f6934b0a12be995a881e1aeaac04329e16 /drivers
parent3196265e97c661d9e9be04d5d64ca8ff3186536d (diff)
downloadlinux-stable-d344bff9c36db17dc4765215495aaa7212c1eb6c.tar.gz
linux-stable-d344bff9c36db17dc4765215495aaa7212c1eb6c.tar.bz2
linux-stable-d344bff9c36db17dc4765215495aaa7212c1eb6c.zip
mv643xx_eth: Fix race condition in mv643xx_eth_free_tx_descs
mv643xx_eth: Fix race condition in mv643xx_eth_free_tx_descs This bug was found and isolated by Thibaut VARENE <T-Bone@parisc-linux.org> and Jarek Poplawski <jarkao2@o2.pl>. This patch is a modification of their fixes. We acquire and release the lock for each descriptor that is freed to minimize the time the lock is held. Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/mv643xx_eth.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index c41ae4286eea..b3bf86422734 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -314,6 +314,13 @@ int mv643xx_eth_free_tx_descs(struct net_device *dev, int force)
while (mp->tx_desc_count > 0) {
spin_lock_irqsave(&mp->lock, flags);
+
+ /* tx_desc_count might have changed before acquiring the lock */
+ if (mp->tx_desc_count <= 0) {
+ spin_unlock_irqrestore(&mp->lock, flags);
+ return released;
+ }
+
tx_index = mp->tx_used_desc_q;
desc = &mp->p_tx_desc_area[tx_index];
cmd_sts = desc->cmd_sts;
@@ -332,13 +339,13 @@ int mv643xx_eth_free_tx_descs(struct net_device *dev, int force)
if (skb)
mp->tx_skb[tx_index] = NULL;
- spin_unlock_irqrestore(&mp->lock, flags);
-
if (cmd_sts & ETH_ERROR_SUMMARY) {
printk("%s: Error in TX\n", dev->name);
mp->stats.tx_errors++;
}
+ spin_unlock_irqrestore(&mp->lock, flags);
+
if (cmd_sts & ETH_TX_FIRST_DESC)
dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
else