aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
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
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')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c3
3 files changed, 17 insertions, 1 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index d2a1ea98d0f..fd356b7c047 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -426,10 +426,14 @@ void rt2x00lib_txdone(struct queue_entry *entry,
426 /* 426 /*
427 * If the data queue was below the threshold before the txdone 427 * If the data queue was below the threshold before the txdone
428 * handler we must make sure the packet queue in the mac80211 stack 428 * handler we must make sure the packet queue in the mac80211 stack
429 * is reenabled when the txdone handler has finished. 429 * is reenabled when the txdone handler has finished. This has to be
430 * serialized with rt2x00mac_tx(), otherwise we can wake up queue
431 * before it was stopped.
430 */ 432 */
433 spin_lock_bh(&entry->queue->tx_lock);
431 if (!rt2x00queue_threshold(entry->queue)) 434 if (!rt2x00queue_threshold(entry->queue))
432 rt2x00queue_unpause_queue(entry->queue); 435 rt2x00queue_unpause_queue(entry->queue);
436 spin_unlock_bh(&entry->queue->tx_lock);
433} 437}
434EXPORT_SYMBOL_GPL(rt2x00lib_txdone); 438EXPORT_SYMBOL_GPL(rt2x00lib_txdone);
435 439
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index ede3c58e678..2df2eb6d3e0 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}
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 5adfb3eab9c..9b1b2b7a780 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -619,6 +619,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
619 else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags)) 619 else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags))
620 rt2x00queue_align_frame(skb); 620 rt2x00queue_align_frame(skb);
621 621
622 /*
623 * That function must be called with bh disabled.
624 */
622 spin_lock(&queue->tx_lock); 625 spin_lock(&queue->tx_lock);
623 626
624 if (unlikely(rt2x00queue_full(queue))) { 627 if (unlikely(rt2x00queue_full(queue))) {