aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Otcheretianski <andrei.otcheretianski@intel.com>2014-05-25 10:24:22 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-07-07 14:41:21 -0400
commit003e5236a1fcab3fc4576fe643e31a3d83027256 (patch)
treea02e051c9e0833c8e4112df7ab6503b9964a1366
parent7f0a7c671cdc0396f99b60b77bd02e2dee7c4838 (diff)
iwlwifi: mvm: Use CS tx block bit for AP/GO
An AP/GO may perform the channel switch slightly before its stations. This scenario may result in packet loss, since the transmission may start before the client is actually on a new channel. In order to prevent potential packet loss disable tx to all the stations when the channel switch flow starts. Clear the disable_tx bit when a station is seen on a target channel, or after IWL_MVM_CS_UNBLOCK_TX_TIMEOUT beacons on a new channel. In addition call ieee80211_sta_block_awake in order to inform mac80211 that the frames for this station should be buffered. Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com> Reviewed-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c25
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c11
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c17
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c54
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h10
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c14
7 files changed, 136 insertions, 3 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index a176d008dab1..96b9cf8137e7 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -1237,6 +1237,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
1237 struct iwl_rx_packet *pkt = rxb_addr(rxb); 1237 struct iwl_rx_packet *pkt = rxb_addr(rxb);
1238 struct iwl_mvm_tx_resp *beacon_notify_hdr; 1238 struct iwl_mvm_tx_resp *beacon_notify_hdr;
1239 struct ieee80211_vif *csa_vif; 1239 struct ieee80211_vif *csa_vif;
1240 struct ieee80211_vif *tx_blocked_vif;
1240 u64 tsf; 1241 u64 tsf;
1241 1242
1242 lockdep_assert_held(&mvm->mutex); 1243 lockdep_assert_held(&mvm->mutex);
@@ -1267,6 +1268,30 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
1267 if (unlikely(csa_vif && csa_vif->csa_active)) 1268 if (unlikely(csa_vif && csa_vif->csa_active))
1268 iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2); 1269 iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
1269 1270
1271 tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
1272 lockdep_is_held(&mvm->mutex));
1273 if (unlikely(tx_blocked_vif)) {
1274 struct iwl_mvm_vif *mvmvif =
1275 iwl_mvm_vif_from_mac80211(tx_blocked_vif);
1276
1277 /*
1278 * The channel switch is started and we have blocked the
1279 * stations. If this is the first beacon (the timeout wasn't
1280 * set), set the unblock timeout, otherwise countdown
1281 */
1282 if (!mvm->csa_tx_block_bcn_timeout)
1283 mvm->csa_tx_block_bcn_timeout =
1284 IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
1285 else
1286 mvm->csa_tx_block_bcn_timeout--;
1287
1288 /* Check if the timeout is expired, and unblock tx */
1289 if (mvm->csa_tx_block_bcn_timeout == 0) {
1290 iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
1291 RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
1292 }
1293 }
1294
1270 return 0; 1295 return 0;
1271} 1296}
1272 1297
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 4a0350fc4b4e..5ed6fb32087c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -1614,6 +1614,11 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
1614 RCU_INIT_POINTER(mvm->csa_vif, NULL); 1614 RCU_INIT_POINTER(mvm->csa_vif, NULL);
1615 } 1615 }
1616 1616
1617 if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
1618 RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
1619 mvm->csa_tx_block_bcn_timeout = 0;
1620 }
1621
1617 mvmvif->ap_ibss_active = false; 1622 mvmvif->ap_ibss_active = false;
1618 mvm->ap_last_beacon_gp2 = 0; 1623 mvm->ap_last_beacon_gp2 = 0;
1619 1624
@@ -2491,6 +2496,12 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
2491 if (!vif->csa_active || !mvmvif->ap_ibss_active) 2496 if (!vif->csa_active || !mvmvif->ap_ibss_active)
2492 goto out; 2497 goto out;
2493 2498
2499 /* Set CS bit on all the stations */
2500 iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
2501
2502 /* Save blocked iface, the timeout is set on the next beacon */
2503 rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
2504
2494 mvmvif->ap_ibss_active = false; 2505 mvmvif->ap_ibss_active = false;
2495 break; 2506 break;
2496 case NL80211_IFTYPE_STATION: 2507 case NL80211_IFTYPE_STATION:
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index db496c5c73fc..7b308c4834f6 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -95,6 +95,12 @@
95 */ 95 */
96#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 96#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
97 97
98/*
99 * Number of beacons to transmit on a new channel until we unblock tx to
100 * the stations, even if we didn't identify them on a new channel
101 */
102#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
103
98enum iwl_mvm_tx_fifo { 104enum iwl_mvm_tx_fifo {
99 IWL_MVM_TX_FIFO_BK = 0, 105 IWL_MVM_TX_FIFO_BK = 0,
100 IWL_MVM_TX_FIFO_BE, 106 IWL_MVM_TX_FIFO_BE,
@@ -671,6 +677,8 @@ struct iwl_mvm {
671 bool ps_disabled; 677 bool ps_disabled;
672 678
673 struct ieee80211_vif __rcu *csa_vif; 679 struct ieee80211_vif __rcu *csa_vif;
680 struct ieee80211_vif __rcu *csa_tx_blocked_vif;
681 u8 csa_tx_block_bcn_timeout;
674 682
675 /* system time of last beacon (for AP/GO interface) */ 683 /* system time of last beacon (for AP/GO interface) */
676 u32 ap_last_beacon_gp2; 684 u32 ap_last_beacon_gp2;
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index cf7276967acd..4b98987fc413 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -259,6 +259,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
259 memset(&rx_status, 0, sizeof(rx_status)); 259 memset(&rx_status, 0, sizeof(rx_status));
260 260
261 /* 261 /*
262 * We have tx blocked stations (with CS bit). If we heard frames from
263 * a blocked station on a new channel we can TX to it again.
264 */
265 if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
266 struct ieee80211_sta *sta;
267
268 rcu_read_lock();
269
270 sta = ieee80211_find_sta(
271 rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
272 if (sta)
273 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
274
275 rcu_read_unlock();
276 }
277
278 /*
262 * drop the packet if it has failed being decrypted by HW 279 * drop the packet if it has failed being decrypted by HW
263 */ 280 */
264 if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) { 281 if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index d3a6cf7558eb..812813964847 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -1468,3 +1468,57 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
1468 if (ret) 1468 if (ret)
1469 IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); 1469 IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
1470} 1470}
1471
1472void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
1473 struct ieee80211_sta *sta,
1474 bool disable)
1475{
1476 struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
1477
1478 spin_lock_bh(&mvm_sta->lock);
1479
1480 if (mvm_sta->disable_tx == disable) {
1481 spin_unlock_bh(&mvm_sta->lock);
1482 return;
1483 }
1484
1485 mvm_sta->disable_tx = disable;
1486
1487 /*
1488 * Tell mac80211 to start/stop queueing tx for this station,
1489 * but don't stop queueing if there are still pending frames
1490 * for this station.
1491 */
1492 if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
1493 ieee80211_sta_block_awake(mvm->hw, sta, disable);
1494
1495 iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
1496
1497 spin_unlock_bh(&mvm_sta->lock);
1498}
1499
1500void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
1501 struct iwl_mvm_vif *mvmvif,
1502 bool disable)
1503{
1504 struct ieee80211_sta *sta;
1505 struct iwl_mvm_sta *mvm_sta;
1506 int i;
1507
1508 lockdep_assert_held(&mvm->mutex);
1509
1510 /* Block/unblock all the stations of the given mvmvif */
1511 for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
1512 sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
1513 lockdep_is_held(&mvm->mutex));
1514 if (IS_ERR_OR_NULL(sta))
1515 continue;
1516
1517 mvm_sta = iwl_mvm_sta_from_mac80211(sta);
1518 if (mvm_sta->mac_id_n_color !=
1519 FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
1520 continue;
1521
1522 iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
1523 }
1524}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index 10c1a5352651..3b1c8bd6cb54 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -73,6 +73,7 @@
73#include "rs.h" 73#include "rs.h"
74 74
75struct iwl_mvm; 75struct iwl_mvm;
76struct iwl_mvm_vif;
76 77
77/** 78/**
78 * DOC: station table - introduction 79 * DOC: station table - introduction
@@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
295 * @tid_data: per tid data. Look at %iwl_mvm_tid_data. 296 * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
296 * @tx_protection: reference counter for controlling the Tx protection. 297 * @tx_protection: reference counter for controlling the Tx protection.
297 * @tt_tx_protection: is thermal throttling enable Tx protection? 298 * @tt_tx_protection: is thermal throttling enable Tx protection?
299 * @disable_tx: is tx to this STA disabled?
298 * 300 *
299 * When mac80211 creates a station it reserves some space (hw->sta_data_size) 301 * When mac80211 creates a station it reserves some space (hw->sta_data_size)
300 * in the structure for use by driver. This structure is placed in that 302 * in the structure for use by driver. This structure is placed in that
@@ -317,6 +319,8 @@ struct iwl_mvm_sta {
317 /* Temporary, until the new TLC will control the Tx protection */ 319 /* Temporary, until the new TLC will control the Tx protection */
318 s8 tx_protection; 320 s8 tx_protection;
319 bool tt_tx_protection; 321 bool tt_tx_protection;
322
323 bool disable_tx;
320}; 324};
321 325
322static inline struct iwl_mvm_sta * 326static inline struct iwl_mvm_sta *
@@ -406,5 +410,11 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
406 bool drain); 410 bool drain);
407void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, 411void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
408 struct iwl_mvm_sta *mvmsta, bool disable); 412 struct iwl_mvm_sta *mvmsta, bool disable);
413void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
414 struct ieee80211_sta *sta,
415 bool disable);
416void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
417 struct iwl_mvm_vif *mvmvif,
418 bool disable);
409 419
410#endif /* __sta_h__ */ 420#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index fa87a4ba25ec..f5c0982d297c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -727,13 +727,21 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
727 goto out; 727 goto out;
728 728
729 if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { 729 if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
730
730 /* 731 /*
731 * If there are no pending frames for this STA, notify 732 * If there are no pending frames for this STA and
732 * mac80211 that this station can go to sleep in its 733 * the tx to this station is not disabled, notify
734 * mac80211 that this station can now wake up in its
733 * STA table. 735 * STA table.
734 * If mvmsta is not NULL, sta is valid. 736 * If mvmsta is not NULL, sta is valid.
735 */ 737 */
736 ieee80211_sta_block_awake(mvm->hw, sta, false); 738
739 spin_lock_bh(&mvmsta->lock);
740
741 if (!mvmsta->disable_tx)
742 ieee80211_sta_block_awake(mvm->hw, sta, false);
743
744 spin_unlock_bh(&mvmsta->lock);
737 } 745 }
738 746
739 if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { 747 if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {