Jesse Keating 2f82dda
This patch is not upstream yet...
Jesse Keating 2f82dda
Jesse Keating 2f82dda
From 34c75818bfcd65e54fed9fe852fc41aba8cf233d Mon Sep 17 00:00:00 2001
Jesse Keating 2f82dda
From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Jesse Keating 2f82dda
Date: Thu, 4 Mar 2010 13:38:59 -0800
Jesse Keating 2f82dda
Subject: [PATCH 15/17] iwlwifi: Recover TX flow failure
Jesse Keating 2f82dda
Jesse Keating 2f82dda
Monitors the tx statistics to detect the drop in throughput.
Jesse Keating 2f82dda
When the throughput drops, the ratio of the actual_ack_count and the
Jesse Keating 2f82dda
expected_ack_count also drops.  At the same time, the aggregated
Jesse Keating 2f82dda
ba_timeout (the number of ba timeout retries) also rises.  If the
Jesse Keating 2f82dda
actual_ack_count/expected_ack_count ratio is 0 and the number of ba
Jesse Keating 2f82dda
timeout retries rises to BA_TIMEOUT_MAX, no tx packets can be delivered.
Jesse Keating 2f82dda
Reloading the uCode and bring the system back to normal operational
Jesse Keating 2f82dda
state.
Jesse Keating 2f82dda
Jesse Keating 2f82dda
Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
Jesse Keating 2f82dda
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Jesse Keating 2f82dda
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Jesse Keating 2f82dda
Jesse Keating 2f82dda
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
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c	2010-03-22 16:06:01.000000000 -0400
Jesse Keating 2f82dda
@@ -2559,10 +2559,21 @@ static int iwl_mac_ampdu_action(struct i
Jesse Keating 2f82dda
 			return ret;
Jesse Keating 2f82dda
 	case IEEE80211_AMPDU_TX_START:
Jesse Keating 2f82dda
 		IWL_DEBUG_HT(priv, "start Tx\n");
Jesse Keating 2f82dda
-		return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
Jesse Keating 2f82dda
+		ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn);
Jesse Keating 2f82dda
+		if (ret == 0) {
Jesse Keating 2f82dda
+			priv->_agn.agg_tids_count++;
Jesse Keating 2f82dda
+			IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
Jesse Keating 2f82dda
+				     priv->_agn.agg_tids_count);
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
+		return ret;
Jesse Keating 2f82dda
 	case IEEE80211_AMPDU_TX_STOP:
Jesse Keating 2f82dda
 		IWL_DEBUG_HT(priv, "stop Tx\n");
Jesse Keating 2f82dda
 		ret = iwl_tx_agg_stop(priv, sta->addr, tid);
Jesse Keating 2f82dda
+		if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) {
Jesse Keating 2f82dda
+			priv->_agn.agg_tids_count--;
Jesse Keating 2f82dda
+			IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
Jesse Keating 2f82dda
+				     priv->_agn.agg_tids_count);
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
 		if (test_bit(STATUS_EXIT_PENDING, &priv->status))
Jesse Keating 2f82dda
 			return 0;
Jesse Keating 2f82dda
 		else
Jesse Keating 2f82dda
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
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig	2010-03-22 16:08:56.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c	2010-03-22 16:08:53.000000000 -0400
Jesse Keating 2f82dda
@@ -1494,6 +1494,7 @@ int iwl_init_drv(struct iwl_priv *priv)
Jesse Keating 2f82dda
 	priv->band = IEEE80211_BAND_2GHZ;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	priv->iw_mode = NL80211_IFTYPE_STATION;
Jesse Keating 2f82dda
+	priv->_agn.agg_tids_count = 0;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
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
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h	2010-03-22 16:06:01.000000000 -0400
Jesse Keating 2f82dda
@@ -1206,6 +1206,16 @@ struct iwl_priv {
Jesse Keating 2f82dda
 	u16 beacon_int;
Jesse Keating 2f82dda
 	struct ieee80211_vif *vif;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	union {
Jesse Keating 2f82dda
+		struct {
Jesse Keating 2f82dda
+			/*
Jesse Keating 2f82dda
+			 * reporting the number of tids has AGG on. 0 means
Jesse Keating 2f82dda
+			 * no AGGREGATION
Jesse Keating 2f82dda
+			 */
Jesse Keating 2f82dda
+			u8 agg_tids_count;
Jesse Keating 2f82dda
+		} _agn;
Jesse Keating 2f82dda
+	};
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	/*Added for 3945 */
Jesse Keating 2f82dda
 	void *shared_virt;
Jesse Keating 2f82dda
 	dma_addr_t shared_phys;
Jesse Keating 2f82dda
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
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig	2010-03-22 16:02:35.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c	2010-03-22 16:06:01.000000000 -0400
Jesse Keating 2f82dda
@@ -550,9 +550,18 @@ static void iwl_rx_calc_noise(struct iwl
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 #define REG_RECALIB_PERIOD (60)
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
Jesse Keating 2f82dda
+#define ACK_CNT_RATIO (50)
Jesse Keating 2f82dda
+#define BA_TIMEOUT_CNT (5)
Jesse Keating 2f82dda
+#define BA_TIMEOUT_MAX (16)
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
Jesse Keating 2f82dda
 /*
Jesse Keating 2f82dda
- * This function checks for plcp error.
Jesse Keating 2f82dda
+ * This function checks for plcp error, ACK count ratios, aggregated BA
Jesse Keating 2f82dda
+ * timeout retries.
Jesse Keating 2f82dda
+ * - When the ACK count ratio is 0 and aggregated BA timeout retries is
Jesse Keating 2f82dda
+ * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting
Jesse Keating 2f82dda
+ * the firmware.
Jesse Keating 2f82dda
  * - When the plcp error is exceeding the thresholds, it will reset the radio
Jesse Keating 2f82dda
  * to improve the throughput.
Jesse Keating 2f82dda
  */
Jesse Keating 2f82dda
@@ -562,6 +571,36 @@ void iwl_recover_from_statistics(struct 
Jesse Keating 2f82dda
 	int combined_plcp_delta;
Jesse Keating 2f82dda
 	unsigned int plcp_msec;
Jesse Keating 2f82dda
 	unsigned long plcp_received_jiffies;
Jesse Keating 2f82dda
+	int actual_ack_cnt_delta;
Jesse Keating 2f82dda
+	int expected_ack_cnt_delta;
Jesse Keating 2f82dda
+	int ba_timeout_delta;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	actual_ack_cnt_delta =
Jesse Keating 2f82dda
+		le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
Jesse Keating 2f82dda
+		le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
Jesse Keating 2f82dda
+	expected_ack_cnt_delta =
Jesse Keating 2f82dda
+		le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
Jesse Keating 2f82dda
+		le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
Jesse Keating 2f82dda
+	ba_timeout_delta =
Jesse Keating 2f82dda
+		le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
Jesse Keating 2f82dda
+		le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
Jesse Keating 2f82dda
+	if ((priv->_agn.agg_tids_count > 0) &&
Jesse Keating 2f82dda
+	    (expected_ack_cnt_delta > 0) &&
Jesse Keating 2f82dda
+	    (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
Jesse Keating 2f82dda
+		< ACK_CNT_RATIO) &&
Jesse Keating 2f82dda
+	    (ba_timeout_delta > BA_TIMEOUT_CNT)) {
Jesse Keating 2f82dda
+		IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
Jesse Keating 2f82dda
+				" expected_ack_cnt = %d\n",
Jesse Keating 2f82dda
+				actual_ack_cnt_delta, expected_ack_cnt_delta);
Jesse Keating 2f82dda
+		IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
Jesse Keating 2f82dda
+				ba_timeout_delta);
Jesse Keating 2f82dda
+		if ((actual_ack_cnt_delta == 0) &&
Jesse Keating 2f82dda
+		    (ba_timeout_delta >= BA_TIMEOUT_MAX)) {
Jesse Keating 2f82dda
+			IWL_DEBUG_RADIO(priv,
Jesse Keating 2f82dda
+					"call iwl_force_reset(IWL_FW_RESET)\n");
Jesse Keating 2f82dda
+			iwl_force_reset(priv, IWL_FW_RESET);
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	/*
Jesse Keating 2f82dda
 	 * check for plcp_err and trigger radio reset if it exceeds