aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-08-23 10:57:17 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-08-25 14:34:54 -0400
commit18c121d7558a550e8e48956fbd389759a850ab53 (patch)
treeeeef362906bcd2d2394052e9c58f202d62ce72e9 /drivers
parentbefe8c469baebe8a0fb5bd9b7cd4afd8c54ebbd5 (diff)
iwlwifi: disable aggregation queue if stopped early
When aggregation is stopped again for some reason before the queue we selected has drained, we will currently leak the TX queue and keep it enabled for aggregation. Normally this doesn't happen, so the problem is rarely seen. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index e2497e7ba926..a51a7cfa5a14 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -1036,7 +1036,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
1036int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, 1036int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
1037 struct ieee80211_sta *sta, u16 tid) 1037 struct ieee80211_sta *sta, u16 tid)
1038{ 1038{
1039 int tx_fifo_id, txq_id, sta_id, ssn = -1; 1039 int tx_fifo_id, txq_id, sta_id, ssn;
1040 struct iwl_tid_data *tid_data; 1040 struct iwl_tid_data *tid_data;
1041 int write_ptr, read_ptr; 1041 int write_ptr, read_ptr;
1042 unsigned long flags; 1042 unsigned long flags;
@@ -1054,21 +1054,26 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
1054 1054
1055 spin_lock_irqsave(&priv->sta_lock, flags); 1055 spin_lock_irqsave(&priv->sta_lock, flags);
1056 1056
1057 if (priv->stations[sta_id].tid[tid].agg.state ==
1058 IWL_EMPTYING_HW_QUEUE_ADDBA) {
1059 IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
1060 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1061 priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
1062 spin_unlock_irqrestore(&priv->sta_lock, flags);
1063 return 0;
1064 }
1065
1066 if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_ON)
1067 IWL_WARN(priv, "Stopping AGG while state not ON or starting\n");
1068
1069 tid_data = &priv->stations[sta_id].tid[tid]; 1057 tid_data = &priv->stations[sta_id].tid[tid];
1070 ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; 1058 ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4;
1071 txq_id = tid_data->agg.txq_id; 1059 txq_id = tid_data->agg.txq_id;
1060
1061 switch (priv->stations[sta_id].tid[tid].agg.state) {
1062 case IWL_EMPTYING_HW_QUEUE_ADDBA:
1063 /*
1064 * This can happen if the peer stops aggregation
1065 * again before we've had a chance to drain the
1066 * queue we selected previously, i.e. before the
1067 * session was really started completely.
1068 */
1069 IWL_DEBUG_HT(priv, "AGG stop before setup done\n");
1070 goto turn_off;
1071 case IWL_AGG_ON:
1072 break;
1073 default:
1074 IWL_WARN(priv, "Stopping AGG while state not ON or starting\n");
1075 }
1076
1072 write_ptr = priv->txq[txq_id].q.write_ptr; 1077 write_ptr = priv->txq[txq_id].q.write_ptr;
1073 read_ptr = priv->txq[txq_id].q.read_ptr; 1078 read_ptr = priv->txq[txq_id].q.read_ptr;
1074 1079
@@ -1082,6 +1087,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
1082 } 1087 }
1083 1088
1084 IWL_DEBUG_HT(priv, "HW queue is empty\n"); 1089 IWL_DEBUG_HT(priv, "HW queue is empty\n");
1090 turn_off:
1085 priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; 1091 priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF;
1086 1092
1087 /* do not restore/save irqs */ 1093 /* do not restore/save irqs */