diff options
author | Dale Farnsworth | 2007-01-23 17:52:25 +0100 |
---|---|---|
committer | Jeff Garzik | 2007-01-23 22:28:53 +0100 |
commit | d344bff9c36db17dc4765215495aaa7212c1eb6c (patch) | |
tree | 707848f6934b0a12be995a881e1aeaac04329e16 /drivers/net/mv643xx_eth.c | |
parent | s2io bogus memset (diff) | |
download | kernel-qcow2-linux-d344bff9c36db17dc4765215495aaa7212c1eb6c.tar.gz kernel-qcow2-linux-d344bff9c36db17dc4765215495aaa7212c1eb6c.tar.xz kernel-qcow2-linux-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/net/mv643xx_eth.c')
-rw-r--r-- | drivers/net/mv643xx_eth.c | 11 |
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 |