aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-03-11 03:34:31 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-03-18 02:41:33 -0400
commitfe92e32ace11b21311fbeb8dbf14ac45b87e700d (patch)
treee565098169c5a85c6a2273bc0eab771ab035563c /drivers/net/wireless
parent9243efccd45373fd6431729f88deaa0cf0bbe0be (diff)
iwlwifi: mvm: properly flush the queues for buffering transport
There are transport that must buffer frames in the driver. This means that we have frames that are not in the op_mode and not visible to the firwmare. This causes issues when we flush the queues: the op_mode flushes a queue, and the firmware flushes all the frames that are *currently* on the rings, but if the transport buffers frames, it can submit these while we are flushing. This leads to a situation where we still have frames on the queues after we flushed them. Preventing those buffered frame from getting into the firmware is possible, but then, we have to run the Tx response path on frames that didn't reach the firmware which is not desirable. The way I solve this here is to let these frames go to the firmware, but make sure the firmware will not transmit them (by setting the station as draining). The op_mode then needs to wait until the transport itself is empty to be sure that the queue is really empty. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless')
-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;