473 lines
18 KiB
Diff
473 lines
18 KiB
Diff
|
Backport of the following upstream commit...
|
||
|
|
||
|
commit b74e31a9bc1013e69b85b139072485dc153453dd
|
||
|
Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
|
||
|
Date: Mon Mar 1 17:23:50 2010 -0800
|
||
|
|
||
|
iwlwifi: Recover TX flow stall due to stuck queue
|
||
|
|
||
|
Monitors the internal TX queues periodically. When a queue is stuck
|
||
|
for some unknown conditions causing the throughput to drop and the
|
||
|
transfer is stop, the driver will force firmware reload and bring the
|
||
|
system back to normal operational state.
|
||
|
|
||
|
The iwlwifi devices behave differently in this regard so this feature is
|
||
|
made part of the ops infrastructure so we can have more control on how to
|
||
|
monitor and recover from tx queue stall case per device.
|
||
|
|
||
|
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.33.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-04-13 14:34:23.000000000 -0400
|
||
|
@@ -138,6 +138,7 @@ static struct iwl_lib_ops iwl1000_lib =
|
||
|
.temperature = iwl5000_temperature,
|
||
|
.set_ct_kill = iwl1000_set_ct_threshold,
|
||
|
},
|
||
|
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
|
||
|
};
|
||
|
|
||
|
static struct iwl_ops iwl1000_ops = {
|
||
|
@@ -175,6 +176,7 @@ struct iwl_cfg iwl1000_bgn_cfg = {
|
||
|
.support_ct_kill_exit = true,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl1000_bg_cfg = {
|
||
|
@@ -202,6 +204,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.support_ct_kill_exit = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c 2010-04-13 14:33:49.000000000 -0400
|
||
|
@@ -2483,6 +2483,13 @@ static void iwl3945_alive_start(struct i
|
||
|
/* After the ALIVE response, we can send commands to 3945 uCode */
|
||
|
set_bit(STATUS_ALIVE, &priv->status);
|
||
|
|
||
|
+ if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||
|
+ /* Enable timer to monitor the driver queues */
|
||
|
+ mod_timer(&priv->monitor_recover,
|
||
|
+ jiffies +
|
||
|
+ msecs_to_jiffies(priv->cfg->monitor_recover_period));
|
||
|
+ }
|
||
|
+
|
||
|
if (iwl_is_rfkill(priv))
|
||
|
return;
|
||
|
|
||
|
@@ -3768,6 +3775,13 @@ static void iwl3945_setup_deferred_work(
|
||
|
|
||
|
iwl3945_hw_setup_deferred_work(priv);
|
||
|
|
||
|
+ if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||
|
+ init_timer(&priv->monitor_recover);
|
||
|
+ priv->monitor_recover.data = (unsigned long)priv;
|
||
|
+ priv->monitor_recover.function =
|
||
|
+ priv->cfg->ops->lib->recover_from_tx_stall;
|
||
|
+ }
|
||
|
+
|
||
|
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||
|
iwl3945_irq_tasklet, (unsigned long)priv);
|
||
|
}
|
||
|
@@ -3780,6 +3794,8 @@ static void iwl3945_cancel_deferred_work
|
||
|
cancel_delayed_work(&priv->scan_check);
|
||
|
cancel_delayed_work(&priv->alive_start);
|
||
|
cancel_work_sync(&priv->beacon_update);
|
||
|
+ if (priv->cfg->ops->lib->recover_from_tx_stall)
|
||
|
+ del_timer_sync(&priv->monitor_recover);
|
||
|
}
|
||
|
|
||
|
static struct attribute *iwl3945_sysfs_entries[] = {
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c 2010-04-13 14:33:49.000000000 -0400
|
||
|
@@ -2829,6 +2829,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
|
||
|
.led_compensation = 64,
|
||
|
.broken_powersave = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
static struct iwl_cfg iwl3945_abg_cfg = {
|
||
|
@@ -2847,6 +2848,7 @@ static struct iwl_cfg iwl3945_abg_cfg =
|
||
|
.led_compensation = 64,
|
||
|
.broken_powersave = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct pci_device_id iwl3945_hw_card_ids[] = {
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c 2010-04-13 14:33:49.000000000 -0400
|
||
|
@@ -2248,6 +2248,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
|
||
|
.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
/* Module firmware */
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c 2010-04-13 14:35:01.000000000 -0400
|
||
|
@@ -1506,6 +1506,7 @@ struct iwl_lib_ops iwl5000_lib = {
|
||
|
.temperature = iwl5000_temperature,
|
||
|
.set_ct_kill = iwl5000_set_ct_threshold,
|
||
|
},
|
||
|
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
|
||
|
};
|
||
|
|
||
|
static struct iwl_lib_ops iwl5150_lib = {
|
||
|
@@ -1558,6 +1559,7 @@ static struct iwl_lib_ops iwl5150_lib =
|
||
|
.temperature = iwl5150_temperature,
|
||
|
.set_ct_kill = iwl5150_set_ct_threshold,
|
||
|
},
|
||
|
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
|
||
|
};
|
||
|
|
||
|
static struct iwl_ops iwl5000_ops = {
|
||
|
@@ -1607,6 +1609,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5100_bgn_cfg = {
|
||
|
@@ -1632,6 +1635,7 @@ struct iwl_cfg iwl5100_bgn_cfg = {
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5100_abg_cfg = {
|
||
|
@@ -1655,6 +1659,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
|
||
|
.led_compensation = 51,
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5100_agn_cfg = {
|
||
|
@@ -1681,6 +1686,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5350_agn_cfg = {
|
||
|
@@ -1707,6 +1713,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5150_agn_cfg = {
|
||
|
@@ -1733,6 +1740,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5150_abg_cfg = {
|
||
|
@@ -1756,6 +1764,7 @@ struct iwl_cfg iwl5150_abg_cfg = {
|
||
|
.led_compensation = 51,
|
||
|
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c 2010-04-13 14:36:12.000000000 -0400
|
||
|
@@ -250,6 +250,7 @@ static struct iwl_lib_ops iwl6000_lib =
|
||
|
.temperature = iwl5000_temperature,
|
||
|
.set_ct_kill = iwl6000_set_ct_threshold,
|
||
|
},
|
||
|
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
|
||
|
};
|
||
|
|
||
|
static struct iwl_ops iwl6000_ops = {
|
||
|
@@ -308,6 +309,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
|
||
|
.support_ct_kill_exit = true,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6000i_2abg_cfg = {
|
||
|
@@ -338,6 +340,7 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
|
||
|
.adv_thermal_throttle = true,
|
||
|
.support_ct_kill_exit = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6000i_2bg_cfg = {
|
||
|
@@ -368,6 +371,7 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
|
||
|
.adv_thermal_throttle = true,
|
||
|
.support_ct_kill_exit = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6050_2agn_cfg = {
|
||
|
@@ -400,6 +404,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
|
||
|
.support_ct_kill_exit = true,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DYNAMIC,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6050_2abg_cfg = {
|
||
|
@@ -430,6 +435,7 @@ struct iwl_cfg iwl6050_2abg_cfg = {
|
||
|
.adv_thermal_throttle = true,
|
||
|
.support_ct_kill_exit = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6000_3agn_cfg = {
|
||
|
@@ -462,6 +468,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
|
||
|
.support_ct_kill_exit = true,
|
||
|
.sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c 2010-04-13 14:37:04.000000000 -0400
|
||
|
@@ -1912,6 +1912,13 @@ static void iwl_alive_start(struct iwl_p
|
||
|
/* After the ALIVE response, we can send host commands to the uCode */
|
||
|
set_bit(STATUS_ALIVE, &priv->status);
|
||
|
|
||
|
+ if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||
|
+ /* Enable timer to monitor the driver queues */
|
||
|
+ mod_timer(&priv->monitor_recover,
|
||
|
+ jiffies +
|
||
|
+ msecs_to_jiffies(priv->cfg->monitor_recover_period));
|
||
|
+ }
|
||
|
+
|
||
|
if (iwl_is_rfkill(priv))
|
||
|
return;
|
||
|
|
||
|
@@ -3126,6 +3133,13 @@ static void iwl_setup_deferred_work(stru
|
||
|
priv->statistics_periodic.data = (unsigned long)priv;
|
||
|
priv->statistics_periodic.function = iwl_bg_statistics_periodic;
|
||
|
|
||
|
+ if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||
|
+ init_timer(&priv->monitor_recover);
|
||
|
+ priv->monitor_recover.data = (unsigned long)priv;
|
||
|
+ priv->monitor_recover.function =
|
||
|
+ priv->cfg->ops->lib->recover_from_tx_stall;
|
||
|
+ }
|
||
|
+
|
||
|
if (!priv->cfg->use_isr_legacy)
|
||
|
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||
|
iwl_irq_tasklet, (unsigned long)priv);
|
||
|
@@ -3144,6 +3158,8 @@ static void iwl_cancel_deferred_work(str
|
||
|
cancel_delayed_work(&priv->alive_start);
|
||
|
cancel_work_sync(&priv->beacon_update);
|
||
|
del_timer_sync(&priv->statistics_periodic);
|
||
|
+ if (priv->cfg->ops->lib->recover_from_tx_stall)
|
||
|
+ del_timer_sync(&priv->monitor_recover);
|
||
|
}
|
||
|
|
||
|
static void iwl_init_hw_rates(struct iwl_priv *priv,
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-04-13 14:33:49.000000000 -0400
|
||
|
@@ -3263,6 +3263,99 @@ int iwl_force_reset(struct iwl_priv *pri
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
+EXPORT_SYMBOL(iwl_force_reset);
|
||
|
+
|
||
|
+/**
|
||
|
+ * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
|
||
|
+ *
|
||
|
+ * During normal condition (no queue is stuck), the timer is continually set to
|
||
|
+ * execute every monitor_recover_period milliseconds after the last timer
|
||
|
+ * expired. When the queue read_ptr is at the same place, the timer is
|
||
|
+ * shorten to 100mSecs. This is
|
||
|
+ * 1) to reduce the chance that the read_ptr may wrap around (not stuck)
|
||
|
+ * 2) to detect the stuck queues quicker before the station and AP can
|
||
|
+ * disassociate each other.
|
||
|
+ *
|
||
|
+ * This function monitors all the tx queues and recover from it if any
|
||
|
+ * of the queues are stuck.
|
||
|
+ * 1. It first check the cmd queue for stuck conditions. If it is stuck,
|
||
|
+ * it will recover by resetting the firmware and return.
|
||
|
+ * 2. Then, it checks for station association. If it associates it will check
|
||
|
+ * other queues. If any queue is stuck, it will recover by resetting
|
||
|
+ * the firmware.
|
||
|
+ * Note: It the number of times the queue read_ptr to be at the same place to
|
||
|
+ * be MAX_REPEAT+1 in order to consider to be stuck.
|
||
|
+ */
|
||
|
+/*
|
||
|
+ * The maximum number of times the read pointer of the tx queue at the
|
||
|
+ * same place without considering to be stuck.
|
||
|
+ */
|
||
|
+#define MAX_REPEAT (2)
|
||
|
+static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
|
||
|
+{
|
||
|
+ struct iwl_tx_queue *txq;
|
||
|
+ struct iwl_queue *q;
|
||
|
+
|
||
|
+ txq = &priv->txq[cnt];
|
||
|
+ q = &txq->q;
|
||
|
+ /* queue is empty, skip */
|
||
|
+ if (q->read_ptr != q->write_ptr) {
|
||
|
+ if (q->read_ptr == q->last_read_ptr) {
|
||
|
+ /* a queue has not been read from last time */
|
||
|
+ if (q->repeat_same_read_ptr > MAX_REPEAT) {
|
||
|
+ IWL_ERR(priv,
|
||
|
+ "queue %d stuck %d time. Fw reload.\n",
|
||
|
+ q->id, q->repeat_same_read_ptr);
|
||
|
+ q->repeat_same_read_ptr = 0;
|
||
|
+ iwl_force_reset(priv, IWL_FW_RESET);
|
||
|
+ } else {
|
||
|
+ q->repeat_same_read_ptr++;
|
||
|
+ IWL_DEBUG_RADIO(priv,
|
||
|
+ "queue %d, not read %d time\n",
|
||
|
+ q->id,
|
||
|
+ q->repeat_same_read_ptr);
|
||
|
+ mod_timer(&priv->monitor_recover, jiffies +
|
||
|
+ msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS));
|
||
|
+ }
|
||
|
+ return 1;
|
||
|
+ } else {
|
||
|
+ q->last_read_ptr = q->read_ptr;
|
||
|
+ q->repeat_same_read_ptr = 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+void iwl_bg_monitor_recover(unsigned long data)
|
||
|
+{
|
||
|
+ struct iwl_priv *priv = (struct iwl_priv *)data;
|
||
|
+ int cnt;
|
||
|
+
|
||
|
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||
|
+ return;
|
||
|
+
|
||
|
+ /* monitor and check for stuck cmd queue */
|
||
|
+ if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM))
|
||
|
+ return;
|
||
|
+
|
||
|
+ /* monitor and check for other stuck queues */
|
||
|
+ if (iwl_is_associated(priv)) {
|
||
|
+ for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
|
||
|
+ /* skip as we already checked the command queue */
|
||
|
+ if (cnt == IWL_CMD_QUEUE_NUM)
|
||
|
+ continue;
|
||
|
+ if (iwl_check_stuck_queue(priv, cnt))
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ /*
|
||
|
+ * Reschedule the timer to occur in
|
||
|
+ * priv->cfg->monitor_recover_period
|
||
|
+ */
|
||
|
+ mod_timer(&priv->monitor_recover,
|
||
|
+ jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period));
|
||
|
+}
|
||
|
+EXPORT_SYMBOL(iwl_bg_monitor_recover);
|
||
|
|
||
|
#ifdef CONFIG_PM
|
||
|
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-04-13 14:38:21.000000000 -0400
|
||
|
@@ -187,6 +187,8 @@ struct iwl_lib_ops {
|
||
|
|
||
|
/* temperature */
|
||
|
struct iwl_temp_ops temp_ops;
|
||
|
+ /* recover from tx queue stall */
|
||
|
+ void (*recover_from_tx_stall)(unsigned long data);
|
||
|
};
|
||
|
|
||
|
struct iwl_led_ops {
|
||
|
@@ -292,6 +294,8 @@ struct iwl_cfg {
|
||
|
u8 sm_ps_mode;
|
||
|
const bool support_wimax_coexist;
|
||
|
u8 plcp_delta_threshold;
|
||
|
+ /* timer period for monitor the driver queues */
|
||
|
+ u32 monitor_recover_period;
|
||
|
};
|
||
|
|
||
|
/***************************
|
||
|
@@ -579,6 +583,9 @@ static inline u16 iwl_pcie_link_ctl(stru
|
||
|
pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
|
||
|
return pci_lnk_ctl;
|
||
|
}
|
||
|
+
|
||
|
+void iwl_bg_monitor_recover(unsigned long data);
|
||
|
+
|
||
|
#ifdef CONFIG_PM
|
||
|
int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
|
||
|
int iwl_pci_resume(struct pci_dev *pdev);
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-04-13 14:38:52.000000000 -0400
|
||
|
@@ -183,6 +183,10 @@ struct iwl_queue {
|
||
|
int n_bd; /* number of BDs in this queue */
|
||
|
int write_ptr; /* 1-st empty entry (index) host_w*/
|
||
|
int read_ptr; /* last used entry (index) host_r*/
|
||
|
+ /* use for monitoring and recovering the stuck queue */
|
||
|
+ int last_read_ptr; /* storing the last read_ptr */
|
||
|
+ /* number of time read_ptr and last_read_ptr are the same */
|
||
|
+ u8 repeat_same_read_ptr;
|
||
|
dma_addr_t dma_addr; /* physical addr for BD's */
|
||
|
int n_window; /* safe queue window */
|
||
|
u32 id;
|
||
|
@@ -997,6 +1001,11 @@ struct iwl_switch_rxon {
|
||
|
#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
|
||
|
#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
|
||
|
|
||
|
+/* timer constants use to monitor and recover stuck tx queues in mSecs */
|
||
|
+#define IWL_MONITORING_PERIOD (1000)
|
||
|
+#define IWL_ONE_HUNDRED_MSECS (100)
|
||
|
+#define IWL_SIXTY_SECS (60000)
|
||
|
+
|
||
|
enum iwl_reset {
|
||
|
IWL_RF_RESET = 0,
|
||
|
IWL_FW_RESET,
|
||
|
@@ -1295,6 +1304,7 @@ struct iwl_priv {
|
||
|
u32 disable_tx_power_cal;
|
||
|
struct work_struct run_time_calib_work;
|
||
|
struct timer_list statistics_periodic;
|
||
|
+ struct timer_list monitor_recover;
|
||
|
bool hw_ready;
|
||
|
/*For 3945*/
|
||
|
#define IWL_DEFAULT_TX_POWER 0x0F
|
||
|
diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c
|
||
|
--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig 2010-04-13 14:33:10.000000000 -0400
|
||
|
+++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c 2010-04-13 14:33:49.000000000 -0400
|
||
|
@@ -288,6 +288,8 @@ static int iwl_queue_init(struct iwl_pri
|
||
|
q->high_mark = 2;
|
||
|
|
||
|
q->write_ptr = q->read_ptr = 0;
|
||
|
+ q->last_read_ptr = 0;
|
||
|
+ q->repeat_same_read_ptr = 0;
|
||
|
|
||
|
return 0;
|
||
|
}
|