aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/dvm/tx.c
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2013-04-07 03:13:44 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-04-08 07:32:20 -0400
commit2d055afdcada4bd8b510e9d2a8566fbded3c9696 (patch)
tree3462550733d3646acf56b9048772ed21f6ead8f5 /drivers/net/wireless/iwlwifi/dvm/tx.c
parentff40231282d4eb57c5008ed48fef6dd1be9f3130 (diff)
iwlwifi: dvm: handle FLUSH ampdu actions from mac80211
Until now we didn't handle properly the FLUSH ampdu action coming from mac80211. This could result in SCD queue leak: mac80211 would STOP_FLUSH an AMPDU Tx session and remove the station. If we had still packets on the ring, we wouldn't deallocate the SCD queue and wait for it to be empty. The indication of the queue being empty comes from the Tx response flow which relies on the tid_data structure. The problem is that this structure has been cleared when the station has been removed. In order to solve this issue, block in the STOP_FLUSH ampdu_action until the SCD queue is flushed, and only then, let mac80211 move forward to remove the station. iwlagn_txfifo_flush had to be enhanced to allow this. The bug fixed here caused the "txq_id mismatch: 12 0" print. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/dvm/tx.c')
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tx.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index cc1e0c1a6f48..2c9e9897a3f1 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -674,6 +674,51 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
674 return ret; 674 return ret;
675} 675}
676 676
677int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif,
678 struct ieee80211_sta *sta, u16 tid)
679{
680 struct iwl_tid_data *tid_data;
681 enum iwl_agg_state agg_state;
682 int sta_id, txq_id;
683 sta_id = iwl_sta_id(sta);
684
685 /*
686 * First set the agg state to OFF to avoid calling
687 * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty.
688 */
689 spin_lock_bh(&priv->sta_lock);
690
691 tid_data = &priv->tid_data[sta_id][tid];
692 txq_id = tid_data->agg.txq_id;
693 agg_state = tid_data->agg.state;
694 IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n",
695 sta_id, tid, txq_id, tid_data->agg.state);
696
697 tid_data->agg.state = IWL_AGG_OFF;
698
699 spin_unlock_bh(&priv->sta_lock);
700
701 if (iwlagn_txfifo_flush(priv, BIT(txq_id)))
702 IWL_ERR(priv, "Couldn't flush the AGG queue\n");
703
704 if (test_bit(txq_id, priv->agg_q_alloc)) {
705 /*
706 * If the transport didn't know that we wanted to start
707 * agreggation, don't tell it that we want to stop them.
708 * This can happen when we don't get the addBA response on
709 * time, or we hadn't time to drain the AC queues.
710 */
711 if (agg_state == IWL_AGG_ON)
712 iwl_trans_txq_disable(priv->trans, txq_id);
713 else
714 IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n",
715 agg_state);
716 iwlagn_dealloc_agg_txq(priv, txq_id);
717 }
718
719 return 0;
720}
721
677int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, 722int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif,
678 struct ieee80211_sta *sta, u16 tid, u8 buf_size) 723 struct ieee80211_sta *sta, u16 tid, u8 buf_size)
679{ 724{