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