aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/agg-tx.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-18 17:47:05 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-01-24 09:43:51 -0500
commit8147dc7f54f0f2e13c3b65fd9628994ad576610b (patch)
treeb093d0d991d91077ddc622c3a8d811c4fdd37209 /net/mac80211/agg-tx.c
parent887da9176e011a044b12ec0deff62df5faadd67c (diff)
mac80211: fix aggregation state with current drivers
For drivers that don't actually flush their queues when aggregation stop with the IEEE80211_AMPDU_TX_STOP_FLUSH or IEEE80211_AMPDU_TX_STOP_FLUSH_CONT reasons is done, like iwlwifi or iwlegacy, mac80211 can then transmit on a TID that the driver still considers busy. This happens in the following way: - IEEE80211_AMPDU_TX_STOP_FLUSH requested - driver marks TID as emptying - mac80211 removes tid_tx data, this can copy packets to the TX pending queues and also let new packets through to the driver - driver gets unexpected TX as it wasn't completely converted to the new API In iwlwifi, this lead to the following warning: WARNING: at drivers/net/wireless/iwlwifi/dvm/tx.c:442 iwlagn_tx_skb+0xc47/0xce0 Tx while agg.state = 4 Modules linked in: [...] Pid: 0, comm: kworker/0:0 Tainted: G W 3.1.0 #1 Call Trace: [<c1046e42>] warn_slowpath_common+0x72/0xa0 [<c1046f13>] warn_slowpath_fmt+0x33/0x40 [<fddffa17>] iwlagn_tx_skb+0xc47/0xce0 [iwldvm] [<fddfcaa3>] iwlagn_mac_tx+0x23/0x40 [iwldvm] [<fd8c98b6>] __ieee80211_tx+0xf6/0x3c0 [mac80211] [<fd8cbe00>] ieee80211_tx+0xd0/0x100 [mac80211] [<fd8cc176>] ieee80211_xmit+0x96/0xe0 [mac80211] [<fd8cc578>] ieee80211_subif_start_xmit+0x348/0xc80 [mac80211] [<c1445207>] dev_hard_start_xmit+0x337/0x6d0 [<c145eee9>] sch_direct_xmit+0xa9/0x210 [<c14462c0>] dev_queue_xmit+0x1b0/0x8e0 Fortunately, solving this problem is easy as the station is being destroyed, so such transmit packets can only happen due to races. Instead of trying to close the race just let the race not reach the drivers by making two changes: 1) remove the explicit aggregation session teardown in the managed mode code, the same thing will be done when the station is removed, in __sta_info_destroy. 2) When aggregation stop with AGG_STOP_DESTROY_STA is requested, leave the tid_tx data around as stopped. It will be cleared and freed in cleanup_single_sta later, but until then any racy packets will be put onto the tid_tx pending queue instead of transmitted which is fine since the station is being removed. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r--net/mac80211/agg-tx.c17
1 files changed, 10 insertions, 7 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index e90911819ed4..13b7683de5a4 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -296,7 +296,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
296 IEEE80211_AMPDU_TX_STOP_FLUSH_CONT, 296 IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
297 &sta->sta, tid, NULL, 0); 297 &sta->sta, tid, NULL, 0);
298 WARN_ON_ONCE(ret); 298 WARN_ON_ONCE(ret);
299 goto remove_tid_tx; 299 return 0;
300 } 300 }
301 301
302 if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { 302 if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
@@ -354,12 +354,15 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
354 */ 354 */
355 } 355 }
356 356
357 if (reason == AGG_STOP_DESTROY_STA) { 357 /*
358 remove_tid_tx: 358 * In the case of AGG_STOP_DESTROY_STA, the driver won't
359 spin_lock_bh(&sta->lock); 359 * necessarily call ieee80211_stop_tx_ba_cb(), so this may
360 ieee80211_remove_tid_tx(sta, tid); 360 * seem like we can leave the tid_tx data pending forever.
361 spin_unlock_bh(&sta->lock); 361 * This is true, in a way, but "forever" is only until the
362 } 362 * station struct is actually destroyed. In the meantime,
363 * leaving it around ensures that we don't transmit packets
364 * to the driver on this TID which might confuse it.
365 */
363 366
364 return 0; 367 return 0;
365} 368}