diff options
author | Dale Farnsworth <dale@farnsworth.org> | 2007-01-23 11:52:25 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-01-23 16:28:53 -0500 |
commit | d344bff9c36db17dc4765215495aaa7212c1eb6c (patch) | |
tree | 707848f6934b0a12be995a881e1aeaac04329e16 /drivers/net/mv643xx_eth.c | |
parent | 3196265e97c661d9e9be04d5d64ca8ff3186536d (diff) |
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) | |||
314 | 314 | ||
315 | while (mp->tx_desc_count > 0) { | 315 | while (mp->tx_desc_count > 0) { |
316 | spin_lock_irqsave(&mp->lock, flags); | 316 | spin_lock_irqsave(&mp->lock, flags); |
317 | |||
318 | /* tx_desc_count might have changed before acquiring the lock */ | ||
319 | if (mp->tx_desc_count <= 0) { | ||
320 | spin_unlock_irqrestore(&mp->lock, flags); | ||
321 | return released; | ||
322 | } | ||
323 | |||
317 | tx_index = mp->tx_used_desc_q; | 324 | tx_index = mp->tx_used_desc_q; |
318 | desc = &mp->p_tx_desc_area[tx_index]; | 325 | desc = &mp->p_tx_desc_area[tx_index]; |
319 | cmd_sts = desc->cmd_sts; | 326 | cmd_sts = desc->cmd_sts; |
@@ -332,13 +339,13 @@ int mv643xx_eth_free_tx_descs(struct net_device *dev, int force) | |||
332 | if (skb) | 339 | if (skb) |
333 | mp->tx_skb[tx_index] = NULL; | 340 | mp->tx_skb[tx_index] = NULL; |
334 | 341 | ||
335 | spin_unlock_irqrestore(&mp->lock, flags); | ||
336 | |||
337 | if (cmd_sts & ETH_ERROR_SUMMARY) { | 342 | if (cmd_sts & ETH_ERROR_SUMMARY) { |
338 | printk("%s: Error in TX\n", dev->name); | 343 | printk("%s: Error in TX\n", dev->name); |
339 | mp->stats.tx_errors++; | 344 | mp->stats.tx_errors++; |
340 | } | 345 | } |
341 | 346 | ||
347 | spin_unlock_irqrestore(&mp->lock, flags); | ||
348 | |||
342 | if (cmd_sts & ETH_TX_FIRST_DESC) | 349 | if (cmd_sts & ETH_TX_FIRST_DESC) |
343 | dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE); | 350 | dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE); |
344 | else | 351 | else |