iwlwifi: mvm: support IEEE80211_AMPDU_TX_STOP_FLUSH
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 19 Feb 2013 14:13:53 +0000 (16:13 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 6 Mar 2013 15:47:26 +0000 (16:47 +0100)
mac80211 tells us when we need to dump the frames from the
AGG queue instead of releasing them as single MPDUs.
Being able to differentiate between the different cases
(IEEE80211_AMPDU_TX_STOP_*) allows us to handle races better.

When the station is removed, mac80211 asks to flush and
removes the station right away.
This allows to avoid a case where we still have frames in
AGG queues, but the station has been remove already.

Note that we can have frames on the shared queues, but this
is not a problem: the station in the fw will be kept until
all the frames on the shared queues have been drained.
AGG queues are a special case since they are dynamically
allocated.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h

index d08ae2604d7bcb80787e4483b8ef544a8e6883c4..924cabf88b26e5af1f7b642908a335f5b788e985 100644 (file)
@@ -284,9 +284,11 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
                break;
        case IEEE80211_AMPDU_TX_STOP_CONT:
+               ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+               break;
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
-               ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+               ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid);
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
                ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
index ca7aba404502ba7df5f589fbb3e1863dfa5d04e6..8b8629317e9cd1fe5a9c8b0938b34207ffe9ddda 100644 (file)
@@ -834,6 +834,34 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return err;
 }
 
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       u16 txq_id;
+
+       /*
+        * First set the agg state to OFF to avoid calling
+        * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty.
+        */
+       spin_lock_bh(&mvmsta->lock);
+       txq_id = tid_data->txq_id;
+       IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n",
+                           mvmsta->sta_id, tid, txq_id, tid_data->state);
+       tid_data->state = IWL_AGG_OFF;
+       spin_unlock_bh(&mvmsta->lock);
+
+       if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
+               IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+
+       iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+       mvm->queue_to_mac80211[tid_data->txq_id] =
+                               IWL_INVALID_MAC80211_QUEUE;
+
+       return 0;
+}
+
 static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
 {
        int i;
index 119de72c6d180711b4cf1e8cafbc6202e309c76b..b0352df981e4539565ffac6afe7213604aa39e35 100644 (file)
@@ -348,6 +348,8 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta, u16 tid, u8 buf_size);
 int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                            struct ieee80211_sta *sta, u16 tid);
+int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid);
 
 int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
 int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,