Commit 003e5236 authored by Andrei Otcheretianski's avatar Andrei Otcheretianski Committed by Emmanuel Grumbach

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: default avatarAndrei Otcheretianski <andrei.otcheretianski@intel.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 7f0a7c67
...@@ -1237,6 +1237,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, ...@@ -1237,6 +1237,7 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_mvm_tx_resp *beacon_notify_hdr; struct iwl_mvm_tx_resp *beacon_notify_hdr;
struct ieee80211_vif *csa_vif; struct ieee80211_vif *csa_vif;
struct ieee80211_vif *tx_blocked_vif;
u64 tsf; u64 tsf;
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
...@@ -1267,6 +1268,30 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, ...@@ -1267,6 +1268,30 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
if (unlikely(csa_vif && csa_vif->csa_active)) if (unlikely(csa_vif && csa_vif->csa_active))
iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2); iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
lockdep_is_held(&mvm->mutex));
if (unlikely(tx_blocked_vif)) {
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(tx_blocked_vif);
/*
* The channel switch is started and we have blocked the
* stations. If this is the first beacon (the timeout wasn't
* set), set the unblock timeout, otherwise countdown
*/
if (!mvm->csa_tx_block_bcn_timeout)
mvm->csa_tx_block_bcn_timeout =
IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
else
mvm->csa_tx_block_bcn_timeout--;
/* Check if the timeout is expired, and unblock tx */
if (mvm->csa_tx_block_bcn_timeout == 0) {
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
}
}
return 0; return 0;
} }
......
...@@ -1614,6 +1614,11 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, ...@@ -1614,6 +1614,11 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
RCU_INIT_POINTER(mvm->csa_vif, NULL); RCU_INIT_POINTER(mvm->csa_vif, NULL);
} }
if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
mvm->csa_tx_block_bcn_timeout = 0;
}
mvmvif->ap_ibss_active = false; mvmvif->ap_ibss_active = false;
mvm->ap_last_beacon_gp2 = 0; mvm->ap_last_beacon_gp2 = 0;
...@@ -2491,6 +2496,12 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, ...@@ -2491,6 +2496,12 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
if (!vif->csa_active || !mvmvif->ap_ibss_active) if (!vif->csa_active || !mvmvif->ap_ibss_active)
goto out; goto out;
/* Set CS bit on all the stations */
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
/* Save blocked iface, the timeout is set on the next beacon */
rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
mvmvif->ap_ibss_active = false; mvmvif->ap_ibss_active = false;
break; break;
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
......
...@@ -95,6 +95,12 @@ ...@@ -95,6 +95,12 @@
*/ */
#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 #define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
/*
* Number of beacons to transmit on a new channel until we unblock tx to
* the stations, even if we didn't identify them on a new channel
*/
#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
enum iwl_mvm_tx_fifo { enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_BK = 0, IWL_MVM_TX_FIFO_BK = 0,
IWL_MVM_TX_FIFO_BE, IWL_MVM_TX_FIFO_BE,
...@@ -671,6 +677,8 @@ struct iwl_mvm { ...@@ -671,6 +677,8 @@ struct iwl_mvm {
bool ps_disabled; bool ps_disabled;
struct ieee80211_vif __rcu *csa_vif; struct ieee80211_vif __rcu *csa_vif;
struct ieee80211_vif __rcu *csa_tx_blocked_vif;
u8 csa_tx_block_bcn_timeout;
/* system time of last beacon (for AP/GO interface) */ /* system time of last beacon (for AP/GO interface) */
u32 ap_last_beacon_gp2; u32 ap_last_beacon_gp2;
......
...@@ -258,6 +258,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, ...@@ -258,6 +258,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
memset(&rx_status, 0, sizeof(rx_status)); memset(&rx_status, 0, sizeof(rx_status));
/*
* We have tx blocked stations (with CS bit). If we heard frames from
* a blocked station on a new channel we can TX to it again.
*/
if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
struct ieee80211_sta *sta;
rcu_read_lock();
sta = ieee80211_find_sta(
rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
if (sta)
iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
rcu_read_unlock();
}
/* /*
* drop the packet if it has failed being decrypted by HW * drop the packet if it has failed being decrypted by HW
*/ */
......
...@@ -1468,3 +1468,57 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, ...@@ -1468,3 +1468,57 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
if (ret) if (ret)
IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
} }
void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
bool disable)
{
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
spin_lock_bh(&mvm_sta->lock);
if (mvm_sta->disable_tx == disable) {
spin_unlock_bh(&mvm_sta->lock);
return;
}
mvm_sta->disable_tx = disable;
/*
* Tell mac80211 to start/stop queueing tx for this station,
* but don't stop queueing if there are still pending frames
* for this station.
*/
if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
ieee80211_sta_block_awake(mvm->hw, sta, disable);
iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
spin_unlock_bh(&mvm_sta->lock);
}
void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif,
bool disable)
{
struct ieee80211_sta *sta;
struct iwl_mvm_sta *mvm_sta;
int i;
lockdep_assert_held(&mvm->mutex);
/* Block/unblock all the stations of the given mvmvif */
for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
lockdep_is_held(&mvm->mutex));
if (IS_ERR_OR_NULL(sta))
continue;
mvm_sta = iwl_mvm_sta_from_mac80211(sta);
if (mvm_sta->mac_id_n_color !=
FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
continue;
iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
}
}
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
#include "rs.h" #include "rs.h"
struct iwl_mvm; struct iwl_mvm;
struct iwl_mvm_vif;
/** /**
* DOC: station table - introduction * DOC: station table - introduction
...@@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) ...@@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
* @tid_data: per tid data. Look at %iwl_mvm_tid_data. * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
* @tx_protection: reference counter for controlling the Tx protection. * @tx_protection: reference counter for controlling the Tx protection.
* @tt_tx_protection: is thermal throttling enable Tx protection? * @tt_tx_protection: is thermal throttling enable Tx protection?
* @disable_tx: is tx to this STA disabled?
* *
* When mac80211 creates a station it reserves some space (hw->sta_data_size) * When mac80211 creates a station it reserves some space (hw->sta_data_size)
* in the structure for use by driver. This structure is placed in that * in the structure for use by driver. This structure is placed in that
...@@ -317,6 +319,8 @@ struct iwl_mvm_sta { ...@@ -317,6 +319,8 @@ struct iwl_mvm_sta {
/* Temporary, until the new TLC will control the Tx protection */ /* Temporary, until the new TLC will control the Tx protection */
s8 tx_protection; s8 tx_protection;
bool tt_tx_protection; bool tt_tx_protection;
bool disable_tx;
}; };
static inline struct iwl_mvm_sta * static inline struct iwl_mvm_sta *
...@@ -406,5 +410,11 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, ...@@ -406,5 +410,11 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain); bool drain);
void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvmsta, bool disable); struct iwl_mvm_sta *mvmsta, bool disable);
void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
bool disable);
void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif,
bool disable);
#endif /* __sta_h__ */ #endif /* __sta_h__ */
...@@ -727,13 +727,21 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, ...@@ -727,13 +727,21 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
goto out; goto out;
if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
/* /*
* If there are no pending frames for this STA, notify * If there are no pending frames for this STA and
* mac80211 that this station can go to sleep in its * the tx to this station is not disabled, notify
* mac80211 that this station can now wake up in its
* STA table. * STA table.
* If mvmsta is not NULL, sta is valid. * If mvmsta is not NULL, sta is valid.
*/ */
ieee80211_sta_block_awake(mvm->hw, sta, false);
spin_lock_bh(&mvmsta->lock);
if (!mvmsta->disable_tx)
ieee80211_sta_block_awake(mvm->hw, sta, false);
spin_unlock_bh(&mvmsta->lock);
} }
if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment