aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorStanislaw Gruszka <sgruszka@redhat.com>2012-03-09 06:39:54 -0500
committerLuis Henriques <luis.henriques@canonical.com>2012-03-26 05:27:02 -0400
commit31486437b5f71754c37e058bfaafd39989abe7f3 (patch)
tree58eda670ef2a7a7989065ab7eddcf92d9c2525f0 /drivers/net/wireless
parentf1a34795be5c850e3ec343a335195ed9b48e7561 (diff)
rt2x00: fix random stalls
BugLink: http://bugs.launchpad.net/bugs/959470 commit 3780d038fdf4b5ef26ead10b0604ab1f46dd9510 upstream. 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. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Tim Gardner <tim.gardner@canonical.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 dffaa8f45f1..5bd2c55c991 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -410,10 +410,14 @@ void rt2x00lib_txdone(struct queue_entry *entry,
410 /* 410 /*
411 * If the data queue was below the threshold before the txdone 411 * If the data queue was below the threshold before the txdone
412 * handler we must make sure the packet queue in the mac80211 stack 412 * handler we must make sure the packet queue in the mac80211 stack
413 * is reenabled when the txdone handler has finished. 413 * is reenabled when the txdone handler has finished. This has to be
414 * serialized with rt2x00mac_tx(), otherwise we can wake up queue
415 * before it was stopped.
414 */ 416 */
417 spin_lock_bh(&entry->queue->tx_lock);
415 if (!rt2x00queue_threshold(entry->queue)) 418 if (!rt2x00queue_threshold(entry->queue))
416 rt2x00queue_unpause_queue(entry->queue); 419 rt2x00queue_unpause_queue(entry->queue);
420 spin_unlock_bh(&entry->queue->tx_lock);
417} 421}
418EXPORT_SYMBOL_GPL(rt2x00lib_txdone); 422EXPORT_SYMBOL_GPL(rt2x00lib_txdone);
419 423
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index a76fdbe6d7d..bc159bed4f6 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 dev_kfree_skb_any(skb); 172 dev_kfree_skb_any(skb);
164} 173}
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 2886d250de5..76f26ad044a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -562,6 +562,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
562 u8 rate_idx, rate_flags; 562 u8 rate_idx, rate_flags;
563 int ret = 0; 563 int ret = 0;
564 564
565 /*
566 * That function must be called with bh disabled.
567 */
565 spin_lock(&queue->tx_lock); 568 spin_lock(&queue->tx_lock);
566 569
567 entry = rt2x00queue_get_entry(queue, Q_INDEX); 570 entry = rt2x00queue_get_entry(queue, Q_INDEX);