447 lines
17 KiB
Diff
447 lines
17 KiB
Diff
|
This patch is not yet upstream...
|
||
|
|
||
|
From a5e660b4e294556822913627544f661e59b39716 Mon Sep 17 00:00:00 2001
|
||
|
From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
|
||
|
Date: Mon, 1 Mar 2010 17:23:50 -0800
|
||
|
Subject: [PATCH 13/17] 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.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2010-03-22 15:33:38.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -135,6 +135,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 = {
|
||
|
@@ -163,5 +164,6 @@ struct iwl_cfg iwl1000_bgn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
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
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig 2010-03-22 15:44:04.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -2453,6 +2453,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;
|
||
|
|
||
|
@@ -3730,6 +3737,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);
|
||
|
}
|
||
|
@@ -3742,6 +3756,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.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig 2010-03-22 14:20:28.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -2897,6 +2897,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
|
||
|
.ht_greenfield_support = false,
|
||
|
.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 = {
|
||
|
@@ -2913,6 +2914,7 @@ static struct iwl_cfg iwl3945_abg_cfg =
|
||
|
.ht_greenfield_support = false,
|
||
|
.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.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig 2010-03-22 14:24:14.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -2364,6 +2364,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
|
||
|
.ht_greenfield_support = false,
|
||
|
.broken_powersave = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
/* Module firmware */
|
||
|
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
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig 2010-03-22 14:27:05.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -1579,6 +1579,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 = {
|
||
|
@@ -1631,6 +1632,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,
|
||
|
};
|
||
|
|
||
|
struct iwl_ops iwl5000_ops = {
|
||
|
@@ -1673,6 +1675,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5100_bg_cfg = {
|
||
|
@@ -1691,6 +1694,7 @@ struct iwl_cfg iwl5100_bg_cfg = {
|
||
|
.need_pll_cfg = true,
|
||
|
.ht_greenfield_support = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5100_abg_cfg = {
|
||
|
@@ -1709,6 +1713,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
|
||
|
.valid_rx_ant = ANT_AB,
|
||
|
.need_pll_cfg = true,
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5100_agn_cfg = {
|
||
|
@@ -1728,6 +1733,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5350_agn_cfg = {
|
||
|
@@ -1747,6 +1753,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl5150_agn_cfg = {
|
||
|
@@ -1766,6 +1773,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.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.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig 2010-03-22 14:28:04.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c 2010-03-22 15:51:12.000000000 -0400
|
||
|
@@ -137,6 +137,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_hcmd_utils_ops iwl6000_hcmd_utils = {
|
||
|
@@ -177,6 +178,7 @@ struct iwl_cfg iwl6000h_2agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
@@ -202,6 +204,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6050_2agn_cfg = {
|
||
|
@@ -224,6 +227,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6000_3agn_cfg = {
|
||
|
@@ -246,6 +250,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||
|
+ .monitor_recover_period = IWL_MONITORING_PERIOD,
|
||
|
};
|
||
|
|
||
|
struct iwl_cfg iwl6050_3agn_cfg = {
|
||
|
@@ -268,6 +273,7 @@ struct iwl_cfg iwl6050_3agn_cfg = {
|
||
|
.ht_greenfield_support = true,
|
||
|
.use_rts_for_ht = true, /* use rts/cts protection */
|
||
|
.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.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 2009-12-02 22:51:21.000000000 -0500
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -1755,6 +1755,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;
|
||
|
|
||
|
@@ -2829,6 +2836,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);
|
||
|
@@ -2847,6 +2861,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 struct attribute *iwl_sysfs_entries[] = {
|
||
|
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 15:40:48.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -3107,6 +3107,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.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 15:24:28.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -183,6 +183,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_ops {
|
||
|
@@ -260,6 +262,8 @@ struct iwl_cfg {
|
||
|
const bool broken_powersave;
|
||
|
bool use_rts_for_ht;
|
||
|
u8 plcp_delta_threshold;
|
||
|
+ /* timer period for monitor the driver queues */
|
||
|
+ u32 monitor_recover_period;
|
||
|
};
|
||
|
|
||
|
/***************************
|
||
|
@@ -543,6 +547,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.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:37:04.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -184,6 +184,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;
|
||
|
@@ -976,6 +980,11 @@ struct traffic_stats {
|
||
|
#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,
|
||
|
@@ -1275,6 +1284,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.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c
|
||
|
--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig 2010-03-22 11:07:02.000000000 -0400
|
||
|
+++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c 2010-03-22 15:48:54.000000000 -0400
|
||
|
@@ -291,6 +291,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;
|
||
|
}
|