Subject: [PATCH 2/4] forcedeth: xmit lock fix

From: Ayaz Abdulla <[email-masked]>
Date: Fri, 09 Jan 2009 16:03:44 -0500

This patch fixes a potential race condition between xmit thread and xmit 
completion thread. The calculation of empty tx descriptors is not 
performed under the lock. This could cause it to set the stop flag while 
the completion thread finishes all tx's. This will result in the tx 
queue in stopped state and no one to wake it up.

Signed-off-by: Ayaz Abdulla <[email-masked]>

--- old/drivers/net/forcedeth.c	2009-01-09 14:51:19.000000000 -0800
+++ new/drivers/net/forcedeth.c	2009-01-09 15:10:15.000000000 -0800
@@ -2096,14 +2096,15 @@
 			   ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
 	}
 
+	spin_lock_irqsave(&np->lock, flags);
 	empty_slots = nv_get_empty_tx_slots(np);
 	if (unlikely(empty_slots <= entries)) {
-		spin_lock_irqsave(&np->lock, flags);
 		netif_stop_queue(dev);
 		np->tx_stop = 1;
 		spin_unlock_irqrestore(&np->lock, flags);
 		return NETDEV_TX_BUSY;
 	}
+	spin_unlock_irqrestore(&np->lock, flags);
 
 	start_tx = put_tx = np->put_tx.orig;
 
@@ -2214,14 +2215,15 @@
 			   ((skb_shinfo(skb)->frags[i].size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
 	}
 
+	spin_lock_irqsave(&np->lock, flags);
 	empty_slots = nv_get_empty_tx_slots(np);
 	if (unlikely(empty_slots <= entries)) {
-		spin_lock_irqsave(&np->lock, flags);
 		netif_stop_queue(dev);
 		np->tx_stop = 1;
 		spin_unlock_irqrestore(&np->lock, flags);
 		return NETDEV_TX_BUSY;
 	}
+	spin_unlock_irqrestore(&np->lock, flags);
 
 	start_tx = put_tx = np->put_tx.ex;
 	start_tx_ctx = np->put_tx_ctx;