aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2011-02-22 17:22:24 -0500
committerLuciano Coelho <coelho@ti.com>2011-02-23 04:14:55 -0500
commitf4d08ddd3e60c79a141be36a5f3a7294c619291d (patch)
tree4b0289650beaa14e2f7c9ea7a66217a71d6cfbc5 /drivers/net
parent6dc9fb3c78a78982f6418b6cf457140f7afa658d (diff)
wl12xx: fix potential race condition with TX queue watermark
Check the conditions for the high/low TX queue watermarks when the spin-lock is taken. This prevents race conditions as tx_queue_count and the flag checked are only modified when the spin-lock is taken. The following race was in mind: - Queues are almost full and wl1271_op_tx() will stop the queues, but it doesn't get the spin-lock yet. - (on another CPU) tx_work_locked() dequeues 15 skbs from this queue and tx_queue_count is updated to reflect this - wl1271_op_tx() does not check tx_queue_count after taking the spin-lock and incorrectly stops the queue. Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/wl12xx/main.c24
1 files changed, 11 insertions, 13 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 13c7102f9864..9bb9ad31c2f5 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -987,6 +987,17 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
987 987
988 spin_lock_irqsave(&wl->wl_lock, flags); 988 spin_lock_irqsave(&wl->wl_lock, flags);
989 wl->tx_queue_count++; 989 wl->tx_queue_count++;
990
991 /*
992 * The workqueue is slow to process the tx_queue and we need stop
993 * the queue here, otherwise the queue will get too long.
994 */
995 if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
996 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
997 ieee80211_stop_queues(wl->hw);
998 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
999 }
1000
990 spin_unlock_irqrestore(&wl->wl_lock, flags); 1001 spin_unlock_irqrestore(&wl->wl_lock, flags);
991 1002
992 /* queue the packet */ 1003 /* queue the packet */
@@ -1001,19 +1012,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1001 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) 1012 if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
1002 ieee80211_queue_work(wl->hw, &wl->tx_work); 1013 ieee80211_queue_work(wl->hw, &wl->tx_work);
1003 1014
1004 /*
1005 * The workqueue is slow to process the tx_queue and we need stop
1006 * the queue here, otherwise the queue will get too long.
1007 */
1008 if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
1009 wl1271_debug(DEBUG_TX, "op_tx: stopping queues");
1010
1011 spin_lock_irqsave(&wl->wl_lock, flags);
1012 ieee80211_stop_queues(wl->hw);
1013 set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
1014 spin_unlock_irqrestore(&wl->wl_lock, flags);
1015 }
1016
1017 return NETDEV_TX_OK; 1015 return NETDEV_TX_OK;
1018} 1016}
1019 1017