aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorDale Farnsworth <dale@farnsworth.org>2007-01-23 11:52:25 -0500
committerJeff Garzik <jeff@garzik.org>2007-01-23 16:28:53 -0500
commitd344bff9c36db17dc4765215495aaa7212c1eb6c (patch)
tree707848f6934b0a12be995a881e1aeaac04329e16 /drivers/net
parent3196265e97c661d9e9be04d5d64ca8ff3186536d (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')
-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)
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