aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c24
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c14
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c8
3 files changed, 46 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 5f26557e4197..babe8922faeb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -1595,9 +1595,33 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
1595 u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif); 1595 u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
1596 1596
1597 if (tfd_msk) { 1597 if (tfd_msk) {
1598 /*
1599 * mac80211 first removes all the stations of the vif and
1600 * then removes the vif. When it removes a station it also
1601 * flushes the AMPDU session. So by now, all the AMPDU sessions
1602 * of all the stations of this vif are closed, and the queues
1603 * of these AMPDU sessions are properly closed.
1604 * We still need to take care of the shared queues of the vif.
1605 * Flush them here.
1606 */
1598 mutex_lock(&mvm->mutex); 1607 mutex_lock(&mvm->mutex);
1599 iwl_mvm_flush_tx_path(mvm, tfd_msk, true); 1608 iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
1600 mutex_unlock(&mvm->mutex); 1609 mutex_unlock(&mvm->mutex);
1610
1611 /*
1612 * There are transports that buffer a few frames in the host.
1613 * For these, the flush above isn't enough since while we were
1614 * flushing, the transport might have sent more frames to the
1615 * device. To solve this, wait here until the transport is
1616 * empty. Technically, this could have replaced the flush
1617 * above, but flush is much faster than draining. So flush
1618 * first, and drain to make sure we have no frames in the
1619 * transport anymore.
1620 * If a station still had frames on the shared queues, it is
1621 * already marked as draining, so to complete the draining, we
1622 * just need to wait until the transport is empty.
1623 */
1624 iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk);
1601 } 1625 }
1602 1626
1603 if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { 1627 if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 50f9288368af..9bf512bdbbd6 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -491,8 +491,18 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
491 491
492 if (vif->type == NL80211_IFTYPE_STATION && 492 if (vif->type == NL80211_IFTYPE_STATION &&
493 mvmvif->ap_sta_id == mvm_sta->sta_id) { 493 mvmvif->ap_sta_id == mvm_sta->sta_id) {
494 ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
495 if (ret)
496 return ret;
494 /* flush its queues here since we are freeing mvm_sta */ 497 /* flush its queues here since we are freeing mvm_sta */
495 ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true); 498 ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
499 if (ret)
500 return ret;
501 ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
502 mvm_sta->tfd_queue_msk);
503 if (ret)
504 return ret;
505 ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
496 506
497 /* if we are associated - we can't remove the AP STA now */ 507 /* if we are associated - we can't remove the AP STA now */
498 if (vif->bss_conf.assoc) 508 if (vif->bss_conf.assoc)
@@ -1120,8 +1130,12 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
1120 spin_unlock_bh(&mvmsta->lock); 1130 spin_unlock_bh(&mvmsta->lock);
1121 1131
1122 if (old_state >= IWL_AGG_ON) { 1132 if (old_state >= IWL_AGG_ON) {
1133 iwl_mvm_drain_sta(mvm, mvmsta, true);
1123 if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true)) 1134 if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
1124 IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); 1135 IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
1136 iwl_trans_wait_tx_queue_empty(mvm->trans,
1137 mvmsta->tfd_queue_msk);
1138 iwl_mvm_drain_sta(mvm, mvmsta, false);
1125 1139
1126 iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); 1140 iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
1127 1141
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 7906b97c81b9..d963439cd899 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -1047,6 +1047,14 @@ out:
1047 return 0; 1047 return 0;
1048} 1048}
1049 1049
1050/*
1051 * Note that there are transports that buffer frames before they reach
1052 * the firmware. This means that after flush_tx_path is called, the
1053 * queue might not be empty. The race-free way to handle this is to:
1054 * 1) set the station as draining
1055 * 2) flush the Tx path
1056 * 3) wait for the transport queues to be empty
1057 */
1050int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) 1058int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
1051{ 1059{
1052 int ret; 1060 int ret;