aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00/rt2x00mac.c
diff options
context:
space:
mode:
authorStanislaw Gruszka <sgruszka@redhat.com>2012-03-09 06:39:54 -0500
committerJohn W. Linville <linville@tuxdriver.com>2012-03-12 14:00:17 -0400
commit3780d038fdf4b5ef26ead10b0604ab1f46dd9510 (patch)
tree26dd6359017398e1b61e51ae05a24a98d96e9fe1 /drivers/net/wireless/rt2x00/rt2x00mac.c
parent210787e82a0ac1ffb5d7be1c796f0c51050849ad (diff)
rt2x00: fix random stalls
Is possible that we stop queue and then do not wake up it again, especially when packets are transmitted fast. That can be easily reproduced with modified tx queue entry_num to some small value e.g. 16. If mac80211 already hold local->queue_stop_reason_lock, then we can wait on that lock in both rt2x00queue_pause_queue() and rt2x00queue_unpause_queue(). After drooping ->queue_stop_reason_lock is possible that __ieee80211_wake_queue() will be performed before __ieee80211_stop_queue(), hence we stop queue and newer wake up it again. Another race condition is possible when between rt2x00queue_threshold() check and rt2x00queue_pause_queue() we will process all pending tx buffers on different cpu. This might happen if for example interrupt will be triggered on cpu performing rt2x00mac_tx(). To prevent race conditions serialize pause/unpause by queue->tx_lock. Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com> Acked-by: Gertjan van Wingerde <gwingerde@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00mac.c')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c9
1 files changed, 9 insertions, 0 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index ede3c58e6783..2df2eb6d3e06 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -152,13 +152,22 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
152 if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false))) 152 if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false)))
153 goto exit_fail; 153 goto exit_fail;
154 154
155 /*
156 * Pausing queue has to be serialized with rt2x00lib_txdone(). Note
157 * we should not use spin_lock_bh variant as bottom halve was already
158 * disabled before ieee80211_xmit() call.
159 */
160 spin_lock(&queue->tx_lock);
155 if (rt2x00queue_threshold(queue)) 161 if (rt2x00queue_threshold(queue))
156 rt2x00queue_pause_queue(queue); 162 rt2x00queue_pause_queue(queue);
163 spin_unlock(&queue->tx_lock);
157 164
158 return; 165 return;
159 166
160 exit_fail: 167 exit_fail:
168 spin_lock(&queue->tx_lock);
161 rt2x00queue_pause_queue(queue); 169 rt2x00queue_pause_queue(queue);
170 spin_unlock(&queue->tx_lock);
162 exit_free_skb: 171 exit_free_skb:
163 ieee80211_free_txskb(hw, skb); 172 ieee80211_free_txskb(hw, skb);
164} 173}