diff options
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 24 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/sta.c | 14 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/tx.c | 8 |
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 | */ | ||
1050 | int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) | 1058 | int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) |
1051 | { | 1059 | { |
1052 | int ret; | 1060 | int ret; |