Blob Blame Raw
This patch is not upstream yet...

From 34c75818bfcd65e54fed9fe852fc41aba8cf233d Mon Sep 17 00:00:00 2001
From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Date: Thu, 4 Mar 2010 13:38:59 -0800
Subject: [PATCH 15/17] iwlwifi: Recover TX flow failure

Monitors the tx statistics to detect the drop in throughput.
When the throughput drops, the ratio of the actual_ack_count and the
expected_ack_count also drops.  At the same time, the aggregated
ba_timeout (the number of ba timeout retries) also rises.  If the
actual_ack_count/expected_ack_count ratio is 0 and the number of ba
timeout retries rises to BA_TIMEOUT_MAX, no tx packets can be delivered.
Reloading the uCode and bring the system back to normal operational
state.

Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>

diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig	2010-03-22 15:48:54.000000000 -0400
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c	2010-03-22 16:06:01.000000000 -0400
@@ -2559,10 +2559,21 @@ static int iwl_mac_ampdu_action(struct i
 			return ret;
 	case IEEE80211_AMPDU_TX_START:
 		IWL_DEBUG_HT(priv, "start Tx\n");
-		return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
+		ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn);
+		if (ret == 0) {
+			priv->_agn.agg_tids_count++;
+			IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
+				     priv->_agn.agg_tids_count);
+		}
+		return ret;
 	case IEEE80211_AMPDU_TX_STOP:
 		IWL_DEBUG_HT(priv, "stop Tx\n");
 		ret = iwl_tx_agg_stop(priv, sta->addr, tid);
+		if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) {
+			priv->_agn.agg_tids_count--;
+			IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
+				     priv->_agn.agg_tids_count);
+		}
 		if (test_bit(STATUS_EXIT_PENDING, &priv->status))
 			return 0;
 		else
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig	2010-03-22 16:08:56.000000000 -0400
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c	2010-03-22 16:08:53.000000000 -0400
@@ -1494,6 +1494,7 @@ int iwl_init_drv(struct iwl_priv *priv)
 	priv->band = IEEE80211_BAND_2GHZ;
 
 	priv->iw_mode = NL80211_IFTYPE_STATION;
+	priv->_agn.agg_tids_count = 0;
 
 	priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
 
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig	2010-03-22 15:48:54.000000000 -0400
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h	2010-03-22 16:06:01.000000000 -0400
@@ -1206,6 +1206,16 @@ struct iwl_priv {
 	u16 beacon_int;
 	struct ieee80211_vif *vif;
 
+	union {
+		struct {
+			/*
+			 * reporting the number of tids has AGG on. 0 means
+			 * no AGGREGATION
+			 */
+			u8 agg_tids_count;
+		} _agn;
+	};
+
 	/*Added for 3945 */
 	void *shared_virt;
 	dma_addr_t shared_phys;
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig	2010-03-22 16:02:35.000000000 -0400
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c	2010-03-22 16:06:01.000000000 -0400
@@ -550,9 +550,18 @@ static void iwl_rx_calc_noise(struct iwl
 
 #define REG_RECALIB_PERIOD (60)
 
+/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
+#define ACK_CNT_RATIO (50)
+#define BA_TIMEOUT_CNT (5)
+#define BA_TIMEOUT_MAX (16)
+
 #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
 /*
- * This function checks for plcp error.
+ * This function checks for plcp error, ACK count ratios, aggregated BA
+ * timeout retries.
+ * - When the ACK count ratio is 0 and aggregated BA timeout retries is
+ * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting
+ * the firmware.
  * - When the plcp error is exceeding the thresholds, it will reset the radio
  * to improve the throughput.
  */
@@ -562,6 +571,36 @@ void iwl_recover_from_statistics(struct 
 	int combined_plcp_delta;
 	unsigned int plcp_msec;
 	unsigned long plcp_received_jiffies;
+	int actual_ack_cnt_delta;
+	int expected_ack_cnt_delta;
+	int ba_timeout_delta;
+
+	actual_ack_cnt_delta =
+		le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
+		le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
+	expected_ack_cnt_delta =
+		le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
+		le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
+	ba_timeout_delta =
+		le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
+		le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
+	if ((priv->_agn.agg_tids_count > 0) &&
+	    (expected_ack_cnt_delta > 0) &&
+	    (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
+		< ACK_CNT_RATIO) &&
+	    (ba_timeout_delta > BA_TIMEOUT_CNT)) {
+		IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
+				" expected_ack_cnt = %d\n",
+				actual_ack_cnt_delta, expected_ack_cnt_delta);
+		IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
+				ba_timeout_delta);
+		if ((actual_ack_cnt_delta == 0) &&
+		    (ba_timeout_delta >= BA_TIMEOUT_MAX)) {
+			IWL_DEBUG_RADIO(priv,
+					"call iwl_force_reset(IWL_FW_RESET)\n");
+			iwl_force_reset(priv, IWL_FW_RESET);
+		}
+	}
 
 	/*
 	 * check for plcp_err and trigger radio reset if it exceeds