Jesse Keating 2f82dda
This patch is not yet upstream...
Jesse Keating 2f82dda
Jesse Keating 2f82dda
From a5e660b4e294556822913627544f661e59b39716 Mon Sep 17 00:00:00 2001
Jesse Keating 2f82dda
From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Jesse Keating 2f82dda
Date: Mon, 1 Mar 2010 17:23:50 -0800
Jesse Keating 2f82dda
Subject: [PATCH 13/17] iwlwifi: Recover TX flow stall due to stuck queue
Jesse Keating 2f82dda
Jesse Keating 2f82dda
Monitors the internal TX queues periodically.  When a queue is stuck
Jesse Keating 2f82dda
for some unknown conditions causing the throughput to drop and the
Jesse Keating 2f82dda
transfer is stop, the driver will force firmware reload and bring the
Jesse Keating 2f82dda
system back to normal operational state.
Jesse Keating 2f82dda
Jesse Keating 2f82dda
The iwlwifi devices behave differently in this regard so this feature is
Jesse Keating 2f82dda
made part of the ops infrastructure so we can have more control on how to
Jesse Keating 2f82dda
monitor and recover from tx queue stall case per device.
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-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig	2010-03-22 15:33:38.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -135,6 +135,7 @@ static struct iwl_lib_ops iwl1000_lib = 
Jesse Keating 2f82dda
 		.temperature = iwl5000_temperature,
Jesse Keating 2f82dda
 		.set_ct_kill = iwl1000_set_ct_threshold,
Jesse Keating 2f82dda
 	 },
Jesse Keating 2f82dda
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 static struct iwl_ops iwl1000_ops = {
Jesse Keating 2f82dda
@@ -163,5 +164,6 @@ struct iwl_cfg iwl1000_bgn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig	2010-03-22 15:44:04.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -2453,6 +2453,13 @@ static void iwl3945_alive_start(struct i
Jesse Keating 2f82dda
 	/* After the ALIVE response, we can send commands to 3945 uCode */
Jesse Keating 2f82dda
 	set_bit(STATUS_ALIVE, &priv->status);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
Jesse Keating 2f82dda
+		/* Enable timer to monitor the driver queues */
Jesse Keating 2f82dda
+		mod_timer(&priv->monitor_recover,
Jesse Keating 2f82dda
+			jiffies +
Jesse Keating 2f82dda
+			msecs_to_jiffies(priv->cfg->monitor_recover_period));
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	if (iwl_is_rfkill(priv))
Jesse Keating 2f82dda
 		return;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -3730,6 +3737,13 @@ static void iwl3945_setup_deferred_work(
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	iwl3945_hw_setup_deferred_work(priv);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
Jesse Keating 2f82dda
+		init_timer(&priv->monitor_recover);
Jesse Keating 2f82dda
+		priv->monitor_recover.data = (unsigned long)priv;
Jesse Keating 2f82dda
+		priv->monitor_recover.function =
Jesse Keating 2f82dda
+			priv->cfg->ops->lib->recover_from_tx_stall;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
Jesse Keating 2f82dda
 		     iwl3945_irq_tasklet, (unsigned long)priv);
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
@@ -3742,6 +3756,8 @@ static void iwl3945_cancel_deferred_work
Jesse Keating 2f82dda
 	cancel_delayed_work(&priv->scan_check);
Jesse Keating 2f82dda
 	cancel_delayed_work(&priv->alive_start);
Jesse Keating 2f82dda
 	cancel_work_sync(&priv->beacon_update);
Jesse Keating 2f82dda
+	if (priv->cfg->ops->lib->recover_from_tx_stall)
Jesse Keating 2f82dda
+		del_timer_sync(&priv->monitor_recover);
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 static struct attribute *iwl3945_sysfs_entries[] = {
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig	2010-03-22 14:20:28.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -2897,6 +2897,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = false,
Jesse Keating 2f82dda
 	.broken_powersave = true,
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 static struct iwl_cfg iwl3945_abg_cfg = {
Jesse Keating 2f82dda
@@ -2913,6 +2914,7 @@ static struct iwl_cfg iwl3945_abg_cfg = 
Jesse Keating 2f82dda
 	.ht_greenfield_support = false,
Jesse Keating 2f82dda
 	.broken_powersave = true,
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct pci_device_id iwl3945_hw_card_ids[] = {
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig	2010-03-22 14:24:14.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -2364,6 +2364,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = false,
Jesse Keating 2f82dda
 	.broken_powersave = true,
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 /* Module firmware */
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig	2010-03-22 14:27:05.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -1579,6 +1579,7 @@ struct iwl_lib_ops iwl5000_lib = {
Jesse Keating 2f82dda
 		.temperature = iwl5000_temperature,
Jesse Keating 2f82dda
 		.set_ct_kill = iwl5000_set_ct_threshold,
Jesse Keating 2f82dda
 	 },
Jesse Keating 2f82dda
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 static struct iwl_lib_ops iwl5150_lib = {
Jesse Keating 2f82dda
@@ -1631,6 +1632,7 @@ static struct iwl_lib_ops iwl5150_lib = 
Jesse Keating 2f82dda
 		.temperature = iwl5150_temperature,
Jesse Keating 2f82dda
 		.set_ct_kill = iwl5150_set_ct_threshold,
Jesse Keating 2f82dda
 	 },
Jesse Keating 2f82dda
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_ops iwl5000_ops = {
Jesse Keating 2f82dda
@@ -1673,6 +1675,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl5100_bg_cfg = {
Jesse Keating 2f82dda
@@ -1691,6 +1694,7 @@ struct iwl_cfg iwl5100_bg_cfg = {
Jesse Keating 2f82dda
 	.need_pll_cfg = true,
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl5100_abg_cfg = {
Jesse Keating 2f82dda
@@ -1709,6 +1713,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
Jesse Keating 2f82dda
 	.valid_rx_ant = ANT_AB,
Jesse Keating 2f82dda
 	.need_pll_cfg = true,
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl5100_agn_cfg = {
Jesse Keating 2f82dda
@@ -1728,6 +1733,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl5350_agn_cfg = {
Jesse Keating 2f82dda
@@ -1747,6 +1753,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl5150_agn_cfg = {
Jesse Keating 2f82dda
@@ -1766,6 +1773,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig	2010-03-22 14:28:04.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c	2010-03-22 15:51:12.000000000 -0400
Jesse Keating 2f82dda
@@ -137,6 +137,7 @@ static struct iwl_lib_ops iwl6000_lib = 
Jesse Keating 2f82dda
 		.temperature = iwl5000_temperature,
Jesse Keating 2f82dda
 		.set_ct_kill = iwl6000_set_ct_threshold,
Jesse Keating 2f82dda
 	 },
Jesse Keating 2f82dda
+	.recover_from_tx_stall = iwl_bg_monitor_recover,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = {
Jesse Keating 2f82dda
@@ -177,6 +178,7 @@ struct iwl_cfg iwl6000h_2agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 /*
Jesse Keating 2f82dda
@@ -202,6 +204,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl6050_2agn_cfg = {
Jesse Keating 2f82dda
@@ -224,6 +227,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl6000_3agn_cfg = {
Jesse Keating 2f82dda
@@ -246,6 +250,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_cfg iwl6050_3agn_cfg = {
Jesse Keating 2f82dda
@@ -268,6 +273,7 @@ struct iwl_cfg iwl6050_3agn_cfg = {
Jesse Keating 2f82dda
 	.ht_greenfield_support = true,
Jesse Keating 2f82dda
 	.use_rts_for_ht = true, /* use rts/cts protection */
Jesse Keating 2f82dda
 	.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
Jesse Keating 2f82dda
+	.monitor_recover_period = IWL_MONITORING_PERIOD,
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
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	2009-12-02 22:51:21.000000000 -0500
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -1755,6 +1755,13 @@ static void iwl_alive_start(struct iwl_p
Jesse Keating 2f82dda
 	/* After the ALIVE response, we can send host commands to the uCode */
Jesse Keating 2f82dda
 	set_bit(STATUS_ALIVE, &priv->status);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
Jesse Keating 2f82dda
+		/* Enable timer to monitor the driver queues */
Jesse Keating 2f82dda
+		mod_timer(&priv->monitor_recover,
Jesse Keating 2f82dda
+			jiffies +
Jesse Keating 2f82dda
+			msecs_to_jiffies(priv->cfg->monitor_recover_period));
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	if (iwl_is_rfkill(priv))
Jesse Keating 2f82dda
 		return;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
@@ -2829,6 +2836,13 @@ static void iwl_setup_deferred_work(stru
Jesse Keating 2f82dda
 	priv->statistics_periodic.data = (unsigned long)priv;
Jesse Keating 2f82dda
 	priv->statistics_periodic.function = iwl_bg_statistics_periodic;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+	if (priv->cfg->ops->lib->recover_from_tx_stall) {
Jesse Keating 2f82dda
+		init_timer(&priv->monitor_recover);
Jesse Keating 2f82dda
+		priv->monitor_recover.data = (unsigned long)priv;
Jesse Keating 2f82dda
+		priv->monitor_recover.function =
Jesse Keating 2f82dda
+			priv->cfg->ops->lib->recover_from_tx_stall;
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 	if (!priv->cfg->use_isr_legacy)
Jesse Keating 2f82dda
 		tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
Jesse Keating 2f82dda
 			iwl_irq_tasklet, (unsigned long)priv);
Jesse Keating 2f82dda
@@ -2847,6 +2861,8 @@ static void iwl_cancel_deferred_work(str
Jesse Keating 2f82dda
 	cancel_delayed_work(&priv->alive_start);
Jesse Keating 2f82dda
 	cancel_work_sync(&priv->beacon_update);
Jesse Keating 2f82dda
 	del_timer_sync(&priv->statistics_periodic);
Jesse Keating 2f82dda
+	if (priv->cfg->ops->lib->recover_from_tx_stall)
Jesse Keating 2f82dda
+		del_timer_sync(&priv->monitor_recover);
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 static struct attribute *iwl_sysfs_entries[] = {
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 15:40:48.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -3107,6 +3107,99 @@ int iwl_force_reset(struct iwl_priv *pri
Jesse Keating 2f82dda
 	}
Jesse Keating 2f82dda
 	return 0;
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
+EXPORT_SYMBOL(iwl_force_reset);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+/**
Jesse Keating 2f82dda
+ * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
Jesse Keating 2f82dda
+ *
Jesse Keating 2f82dda
+ * During normal condition (no queue is stuck), the timer is continually set to
Jesse Keating 2f82dda
+ * execute every monitor_recover_period milliseconds after the last timer
Jesse Keating 2f82dda
+ * expired.  When the queue read_ptr is at the same place, the timer is
Jesse Keating 2f82dda
+ * shorten to 100mSecs.  This is
Jesse Keating 2f82dda
+ *      1) to reduce the chance that the read_ptr may wrap around (not stuck)
Jesse Keating 2f82dda
+ *      2) to detect the stuck queues quicker before the station and AP can
Jesse Keating 2f82dda
+ *      disassociate each other.
Jesse Keating 2f82dda
+ *
Jesse Keating 2f82dda
+ * This function monitors all the tx queues and recover from it if any
Jesse Keating 2f82dda
+ * of the queues are stuck.
Jesse Keating 2f82dda
+ * 1. It first check the cmd queue for stuck conditions.  If it is stuck,
Jesse Keating 2f82dda
+ *      it will recover by resetting the firmware and return.
Jesse Keating 2f82dda
+ * 2. Then, it checks for station association.  If it associates it will check
Jesse Keating 2f82dda
+ *      other queues.  If any queue is stuck, it will recover by resetting
Jesse Keating 2f82dda
+ *      the firmware.
Jesse Keating 2f82dda
+ * Note: It the number of times the queue read_ptr to be at the same place to
Jesse Keating 2f82dda
+ *      be MAX_REPEAT+1 in order to consider to be stuck.
Jesse Keating 2f82dda
+ */
Jesse Keating 2f82dda
+/*
Jesse Keating 2f82dda
+ * The maximum number of times the read pointer of the tx queue at the
Jesse Keating 2f82dda
+ * same place without considering to be stuck.
Jesse Keating 2f82dda
+ */
Jesse Keating 2f82dda
+#define MAX_REPEAT      (2)
Jesse Keating 2f82dda
+static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	struct iwl_tx_queue *txq;
Jesse Keating 2f82dda
+	struct iwl_queue *q;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	txq = &priv->txq[cnt];
Jesse Keating 2f82dda
+	q = &txq->q;
Jesse Keating 2f82dda
+	/* queue is empty, skip */
Jesse Keating 2f82dda
+	if (q->read_ptr != q->write_ptr) {
Jesse Keating 2f82dda
+		if (q->read_ptr == q->last_read_ptr) {
Jesse Keating 2f82dda
+			/* a queue has not been read from last time */
Jesse Keating 2f82dda
+			if (q->repeat_same_read_ptr > MAX_REPEAT) {
Jesse Keating 2f82dda
+				IWL_ERR(priv,
Jesse Keating 2f82dda
+					"queue %d stuck %d time. Fw reload.\n",
Jesse Keating 2f82dda
+					q->id, q->repeat_same_read_ptr);
Jesse Keating 2f82dda
+				q->repeat_same_read_ptr = 0;
Jesse Keating 2f82dda
+				iwl_force_reset(priv, IWL_FW_RESET);
Jesse Keating 2f82dda
+			} else {
Jesse Keating 2f82dda
+				q->repeat_same_read_ptr++;
Jesse Keating 2f82dda
+				IWL_DEBUG_RADIO(priv,
Jesse Keating 2f82dda
+						"queue %d, not read %d time\n",
Jesse Keating 2f82dda
+						q->id,
Jesse Keating 2f82dda
+						q->repeat_same_read_ptr);
Jesse Keating 2f82dda
+				mod_timer(&priv->monitor_recover, jiffies +
Jesse Keating 2f82dda
+					msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS));
Jesse Keating 2f82dda
+			}
Jesse Keating 2f82dda
+			return 1;
Jesse Keating 2f82dda
+		} else {
Jesse Keating 2f82dda
+			q->last_read_ptr = q->read_ptr;
Jesse Keating 2f82dda
+			q->repeat_same_read_ptr = 0;
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	return 0;
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+void iwl_bg_monitor_recover(unsigned long data)
Jesse Keating 2f82dda
+{
Jesse Keating 2f82dda
+	struct iwl_priv *priv = (struct iwl_priv *)data;
Jesse Keating 2f82dda
+	int cnt;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
Jesse Keating 2f82dda
+		return;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	/* monitor and check for stuck cmd queue */
Jesse Keating 2f82dda
+	if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM))
Jesse Keating 2f82dda
+		return;
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+	/* monitor and check for other stuck queues */
Jesse Keating 2f82dda
+	if (iwl_is_associated(priv)) {
Jesse Keating 2f82dda
+		for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
Jesse Keating 2f82dda
+			/* skip as we already checked the command queue */
Jesse Keating 2f82dda
+			if (cnt == IWL_CMD_QUEUE_NUM)
Jesse Keating 2f82dda
+				continue;
Jesse Keating 2f82dda
+			if (iwl_check_stuck_queue(priv, cnt))
Jesse Keating 2f82dda
+				return;
Jesse Keating 2f82dda
+		}
Jesse Keating 2f82dda
+	}
Jesse Keating 2f82dda
+	/*
Jesse Keating 2f82dda
+	 * Reschedule the timer to occur in
Jesse Keating 2f82dda
+	 * priv->cfg->monitor_recover_period
Jesse Keating 2f82dda
+	 */
Jesse Keating 2f82dda
+	mod_timer(&priv->monitor_recover,
Jesse Keating 2f82dda
+		jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period));
Jesse Keating 2f82dda
+}
Jesse Keating 2f82dda
+EXPORT_SYMBOL(iwl_bg_monitor_recover);
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 #ifdef CONFIG_PM
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig	2010-03-22 15:24:28.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -183,6 +183,8 @@ struct iwl_lib_ops {
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	/* temperature */
Jesse Keating 2f82dda
 	struct iwl_temp_ops temp_ops;
Jesse Keating 2f82dda
+	/* recover from tx queue stall */
Jesse Keating 2f82dda
+	void (*recover_from_tx_stall)(unsigned long data);
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 struct iwl_ops {
Jesse Keating 2f82dda
@@ -260,6 +262,8 @@ struct iwl_cfg {
Jesse Keating 2f82dda
 	const bool broken_powersave;
Jesse Keating 2f82dda
 	bool use_rts_for_ht;
Jesse Keating 2f82dda
 	u8 plcp_delta_threshold;
Jesse Keating 2f82dda
+	/* timer period for monitor the driver queues */
Jesse Keating 2f82dda
+	u32 monitor_recover_period;
Jesse Keating 2f82dda
 };
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 /***************************
Jesse Keating 2f82dda
@@ -543,6 +547,9 @@ static inline u16 iwl_pcie_link_ctl(stru
Jesse Keating 2f82dda
 	pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
Jesse Keating 2f82dda
 	return pci_lnk_ctl;
Jesse Keating 2f82dda
 }
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
+void iwl_bg_monitor_recover(unsigned long data);
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 #ifdef CONFIG_PM
Jesse Keating 2f82dda
 int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
Jesse Keating 2f82dda
 int iwl_pci_resume(struct pci_dev *pdev);
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:37:04.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -184,6 +184,10 @@ struct iwl_queue {
Jesse Keating 2f82dda
 	int n_bd;              /* number of BDs in this queue */
Jesse Keating 2f82dda
 	int write_ptr;       /* 1-st empty entry (index) host_w*/
Jesse Keating 2f82dda
 	int read_ptr;         /* last used entry (index) host_r*/
Jesse Keating 2f82dda
+	/* use for monitoring and recovering the stuck queue */
Jesse Keating 2f82dda
+	int last_read_ptr;      /* storing the last read_ptr */
Jesse Keating 2f82dda
+	/* number of time read_ptr and last_read_ptr are the same */
Jesse Keating 2f82dda
+	u8 repeat_same_read_ptr;
Jesse Keating 2f82dda
 	dma_addr_t dma_addr;   /* physical addr for BD's */
Jesse Keating 2f82dda
 	int n_window;	       /* safe queue window */
Jesse Keating 2f82dda
 	u32 id;
Jesse Keating 2f82dda
@@ -976,6 +980,11 @@ struct traffic_stats {
Jesse Keating 2f82dda
 #define IWL_DELAY_NEXT_FORCE_RF_RESET  (HZ*3)
Jesse Keating 2f82dda
 #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
+/* timer constants use to monitor and recover stuck tx queues in mSecs */
Jesse Keating 2f82dda
+#define IWL_MONITORING_PERIOD  (1000)
Jesse Keating 2f82dda
+#define IWL_ONE_HUNDRED_MSECS   (100)
Jesse Keating 2f82dda
+#define IWL_SIXTY_SECS          (60000)
Jesse Keating 2f82dda
+
Jesse Keating 2f82dda
 enum iwl_reset {
Jesse Keating 2f82dda
 	IWL_RF_RESET = 0,
Jesse Keating 2f82dda
 	IWL_FW_RESET,
Jesse Keating 2f82dda
@@ -1275,6 +1284,7 @@ struct iwl_priv {
Jesse Keating 2f82dda
 	u32 disable_tx_power_cal;
Jesse Keating 2f82dda
 	struct work_struct run_time_calib_work;
Jesse Keating 2f82dda
 	struct timer_list statistics_periodic;
Jesse Keating 2f82dda
+	struct timer_list monitor_recover;
Jesse Keating 2f82dda
 	bool hw_ready;
Jesse Keating 2f82dda
 	/*For 3945*/
Jesse Keating 2f82dda
 #define IWL_DEFAULT_TX_POWER 0x0F
Jesse Keating 2f82dda
diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c
Jesse Keating 2f82dda
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig	2010-03-22 11:07:02.000000000 -0400
Jesse Keating 2f82dda
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c	2010-03-22 15:48:54.000000000 -0400
Jesse Keating 2f82dda
@@ -291,6 +291,8 @@ static int iwl_queue_init(struct iwl_pri
Jesse Keating 2f82dda
 		q->high_mark = 2;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	q->write_ptr = q->read_ptr = 0;
Jesse Keating 2f82dda
+	q->last_read_ptr = 0;
Jesse Keating 2f82dda
+	q->repeat_same_read_ptr = 0;
Jesse Keating 2f82dda
 
Jesse Keating 2f82dda
 	return 0;
Jesse Keating 2f82dda
 }