aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorWey-Yi Guy <wey-yi.w.guy@intel.com>2010-03-01 20:23:50 -0500
committerReinette Chatre <reinette.chatre@intel.com>2010-03-19 16:41:25 -0400
commitb74e31a9bc1013e69b85b139072485dc153453dd (patch)
treecf3fa8db0a9a5d58d9bc633c5fe7b8e641d28d43 /drivers/net/wireless/iwlwifi
parentc11362c01b280f8b2c728bc64793d484282b8734 (diff)
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>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c9
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c16
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c93
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c16
11 files changed, 167 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index e31e8a3459b..5a9cb70160f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -212,6 +212,7 @@ static struct iwl_lib_ops iwl1000_lib = {
212 .set_ct_kill = iwl1000_set_ct_threshold, 212 .set_ct_kill = iwl1000_set_ct_threshold,
213 }, 213 },
214 .add_bcast_station = iwl_add_bcast_station, 214 .add_bcast_station = iwl_add_bcast_station,
215 .recover_from_tx_stall = iwl_bg_monitor_recover,
215}; 216};
216 217
217static const struct iwl_ops iwl1000_ops = { 218static const struct iwl_ops iwl1000_ops = {
@@ -249,6 +250,7 @@ struct iwl_cfg iwl1000_bgn_cfg = {
249 .support_ct_kill_exit = true, 250 .support_ct_kill_exit = true,
250 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, 251 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
251 .chain_noise_scale = 1000, 252 .chain_noise_scale = 1000,
253 .monitor_recover_period = IWL_MONITORING_PERIOD,
252}; 254};
253 255
254struct iwl_cfg iwl1000_bg_cfg = { 256struct iwl_cfg iwl1000_bg_cfg = {
@@ -277,6 +279,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
277 .support_ct_kill_exit = true, 279 .support_ct_kill_exit = true,
278 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, 280 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
279 .chain_noise_scale = 1000, 281 .chain_noise_scale = 1000,
282 .monitor_recover_period = IWL_MONITORING_PERIOD,
280}; 283};
281 284
282MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); 285MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 11785e2c767..21ae61dd3d5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -2820,6 +2820,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
2820 .led_compensation = 64, 2820 .led_compensation = 64,
2821 .broken_powersave = true, 2821 .broken_powersave = true,
2822 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 2822 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
2823 .monitor_recover_period = IWL_MONITORING_PERIOD,
2823}; 2824};
2824 2825
2825static struct iwl_cfg iwl3945_abg_cfg = { 2826static struct iwl_cfg iwl3945_abg_cfg = {
@@ -2838,6 +2839,7 @@ static struct iwl_cfg iwl3945_abg_cfg = {
2838 .led_compensation = 64, 2839 .led_compensation = 64,
2839 .broken_powersave = true, 2840 .broken_powersave = true,
2840 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 2841 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
2842 .monitor_recover_period = IWL_MONITORING_PERIOD,
2841}; 2843};
2842 2844
2843DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = { 2845DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = {
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 7f9e448fe5e..acca89a635a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2255,6 +2255,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
2255 .led_compensation = 61, 2255 .led_compensation = 61,
2256 .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, 2256 .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
2257 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 2257 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
2258 .monitor_recover_period = IWL_MONITORING_PERIOD,
2258}; 2259};
2259 2260
2260/* Module firmware */ 2261/* Module firmware */
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 8ab28a76015..a01b3c59a5a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -1499,6 +1499,7 @@ struct iwl_lib_ops iwl5000_lib = {
1499 .set_ct_kill = iwl5000_set_ct_threshold, 1499 .set_ct_kill = iwl5000_set_ct_threshold,
1500 }, 1500 },
1501 .add_bcast_station = iwl_add_bcast_station, 1501 .add_bcast_station = iwl_add_bcast_station,
1502 .recover_from_tx_stall = iwl_bg_monitor_recover,
1502}; 1503};
1503 1504
1504static struct iwl_lib_ops iwl5150_lib = { 1505static struct iwl_lib_ops iwl5150_lib = {
@@ -1553,6 +1554,7 @@ static struct iwl_lib_ops iwl5150_lib = {
1553 .set_ct_kill = iwl5150_set_ct_threshold, 1554 .set_ct_kill = iwl5150_set_ct_threshold,
1554 }, 1555 },
1555 .add_bcast_station = iwl_add_bcast_station, 1556 .add_bcast_station = iwl_add_bcast_station,
1557 .recover_from_tx_stall = iwl_bg_monitor_recover,
1556}; 1558};
1557 1559
1558static const struct iwl_ops iwl5000_ops = { 1560static const struct iwl_ops iwl5000_ops = {
@@ -1602,6 +1604,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
1602 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1604 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1603 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1605 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1604 .chain_noise_scale = 1000, 1606 .chain_noise_scale = 1000,
1607 .monitor_recover_period = IWL_MONITORING_PERIOD,
1605}; 1608};
1606 1609
1607struct iwl_cfg iwl5100_bgn_cfg = { 1610struct iwl_cfg iwl5100_bgn_cfg = {
@@ -1628,6 +1631,7 @@ struct iwl_cfg iwl5100_bgn_cfg = {
1628 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1631 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1629 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1632 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1630 .chain_noise_scale = 1000, 1633 .chain_noise_scale = 1000,
1634 .monitor_recover_period = IWL_MONITORING_PERIOD,
1631}; 1635};
1632 1636
1633struct iwl_cfg iwl5100_abg_cfg = { 1637struct iwl_cfg iwl5100_abg_cfg = {
@@ -1652,6 +1656,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
1652 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1656 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1653 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1657 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1654 .chain_noise_scale = 1000, 1658 .chain_noise_scale = 1000,
1659 .monitor_recover_period = IWL_MONITORING_PERIOD,
1655}; 1660};
1656 1661
1657struct iwl_cfg iwl5100_agn_cfg = { 1662struct iwl_cfg iwl5100_agn_cfg = {
@@ -1678,6 +1683,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
1678 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1683 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1679 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1684 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1680 .chain_noise_scale = 1000, 1685 .chain_noise_scale = 1000,
1686 .monitor_recover_period = IWL_MONITORING_PERIOD,
1681}; 1687};
1682 1688
1683struct iwl_cfg iwl5350_agn_cfg = { 1689struct iwl_cfg iwl5350_agn_cfg = {
@@ -1704,6 +1710,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
1704 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1710 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1705 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1711 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1706 .chain_noise_scale = 1000, 1712 .chain_noise_scale = 1000,
1713 .monitor_recover_period = IWL_MONITORING_PERIOD,
1707}; 1714};
1708 1715
1709struct iwl_cfg iwl5150_agn_cfg = { 1716struct iwl_cfg iwl5150_agn_cfg = {
@@ -1730,6 +1737,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
1730 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1737 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1731 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1738 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1732 .chain_noise_scale = 1000, 1739 .chain_noise_scale = 1000,
1740 .monitor_recover_period = IWL_MONITORING_PERIOD,
1733}; 1741};
1734 1742
1735struct iwl_cfg iwl5150_abg_cfg = { 1743struct iwl_cfg iwl5150_abg_cfg = {
@@ -1754,6 +1762,7 @@ struct iwl_cfg iwl5150_abg_cfg = {
1754 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, 1762 .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
1755 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, 1763 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
1756 .chain_noise_scale = 1000, 1764 .chain_noise_scale = 1000,
1765 .monitor_recover_period = IWL_MONITORING_PERIOD,
1757}; 1766};
1758 1767
1759MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); 1768MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index fb7012289c3..4fbc38cfd91 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -278,6 +278,7 @@ static struct iwl_lib_ops iwl6000_lib = {
278 .set_ct_kill = iwl6000_set_ct_threshold, 278 .set_ct_kill = iwl6000_set_ct_threshold,
279 }, 279 },
280 .add_bcast_station = iwl_add_bcast_station, 280 .add_bcast_station = iwl_add_bcast_station,
281 .recover_from_tx_stall = iwl_bg_monitor_recover,
281}; 282};
282 283
283static const struct iwl_ops iwl6000_ops = { 284static const struct iwl_ops iwl6000_ops = {
@@ -343,6 +344,7 @@ static struct iwl_lib_ops iwl6050_lib = {
343 .set_calib_version = iwl6050_set_calib_version, 344 .set_calib_version = iwl6050_set_calib_version,
344 }, 345 },
345 .add_bcast_station = iwl_add_bcast_station, 346 .add_bcast_station = iwl_add_bcast_station,
347 .recover_from_tx_stall = iwl_bg_monitor_recover,
346}; 348};
347 349
348static const struct iwl_ops iwl6050_ops = { 350static const struct iwl_ops iwl6050_ops = {
@@ -386,6 +388,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
386 .support_ct_kill_exit = true, 388 .support_ct_kill_exit = true,
387 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 389 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
388 .chain_noise_scale = 1000, 390 .chain_noise_scale = 1000,
391 .monitor_recover_period = IWL_MONITORING_PERIOD,
389}; 392};
390 393
391struct iwl_cfg iwl6000i_2abg_cfg = { 394struct iwl_cfg iwl6000i_2abg_cfg = {
@@ -417,6 +420,7 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
417 .support_ct_kill_exit = true, 420 .support_ct_kill_exit = true,
418 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 421 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
419 .chain_noise_scale = 1000, 422 .chain_noise_scale = 1000,
423 .monitor_recover_period = IWL_MONITORING_PERIOD,
420}; 424};
421 425
422struct iwl_cfg iwl6000i_2bg_cfg = { 426struct iwl_cfg iwl6000i_2bg_cfg = {
@@ -448,6 +452,7 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
448 .support_ct_kill_exit = true, 452 .support_ct_kill_exit = true,
449 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 453 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
450 .chain_noise_scale = 1000, 454 .chain_noise_scale = 1000,
455 .monitor_recover_period = IWL_MONITORING_PERIOD,
451}; 456};
452 457
453struct iwl_cfg iwl6050_2agn_cfg = { 458struct iwl_cfg iwl6050_2agn_cfg = {
@@ -480,6 +485,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
480 .support_ct_kill_exit = true, 485 .support_ct_kill_exit = true,
481 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 486 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
482 .chain_noise_scale = 1500, 487 .chain_noise_scale = 1500,
488 .monitor_recover_period = IWL_MONITORING_PERIOD,
483}; 489};
484 490
485struct iwl_cfg iwl6050_2abg_cfg = { 491struct iwl_cfg iwl6050_2abg_cfg = {
@@ -511,6 +517,7 @@ struct iwl_cfg iwl6050_2abg_cfg = {
511 .support_ct_kill_exit = true, 517 .support_ct_kill_exit = true,
512 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 518 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
513 .chain_noise_scale = 1500, 519 .chain_noise_scale = 1500,
520 .monitor_recover_period = IWL_MONITORING_PERIOD,
514}; 521};
515 522
516struct iwl_cfg iwl6000_3agn_cfg = { 523struct iwl_cfg iwl6000_3agn_cfg = {
@@ -543,6 +550,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
543 .support_ct_kill_exit = true, 550 .support_ct_kill_exit = true,
544 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, 551 .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
545 .chain_noise_scale = 1000, 552 .chain_noise_scale = 1000,
553 .monitor_recover_period = IWL_MONITORING_PERIOD,
546}; 554};
547 555
548MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); 556MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 93931b61304..82b1a3fb54e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2074,6 +2074,13 @@ static void iwl_alive_start(struct iwl_priv *priv)
2074 /* After the ALIVE response, we can send host commands to the uCode */ 2074 /* After the ALIVE response, we can send host commands to the uCode */
2075 set_bit(STATUS_ALIVE, &priv->status); 2075 set_bit(STATUS_ALIVE, &priv->status);
2076 2076
2077 if (priv->cfg->ops->lib->recover_from_tx_stall) {
2078 /* Enable timer to monitor the driver queues */
2079 mod_timer(&priv->monitor_recover,
2080 jiffies +
2081 msecs_to_jiffies(priv->cfg->monitor_recover_period));
2082 }
2083
2077 if (iwl_is_rfkill(priv)) 2084 if (iwl_is_rfkill(priv))
2078 return; 2085 return;
2079 2086
@@ -3224,6 +3231,13 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
3224 priv->ucode_trace.data = (unsigned long)priv; 3231 priv->ucode_trace.data = (unsigned long)priv;
3225 priv->ucode_trace.function = iwl_bg_ucode_trace; 3232 priv->ucode_trace.function = iwl_bg_ucode_trace;
3226 3233
3234 if (priv->cfg->ops->lib->recover_from_tx_stall) {
3235 init_timer(&priv->monitor_recover);
3236 priv->monitor_recover.data = (unsigned long)priv;
3237 priv->monitor_recover.function =
3238 priv->cfg->ops->lib->recover_from_tx_stall;
3239 }
3240
3227 if (!priv->cfg->use_isr_legacy) 3241 if (!priv->cfg->use_isr_legacy)
3228 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) 3242 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
3229 iwl_irq_tasklet, (unsigned long)priv); 3243 iwl_irq_tasklet, (unsigned long)priv);
@@ -3243,6 +3257,8 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
3243 cancel_work_sync(&priv->beacon_update); 3257 cancel_work_sync(&priv->beacon_update);
3244 del_timer_sync(&priv->statistics_periodic); 3258 del_timer_sync(&priv->statistics_periodic);
3245 del_timer_sync(&priv->ucode_trace); 3259 del_timer_sync(&priv->ucode_trace);
3260 if (priv->cfg->ops->lib->recover_from_tx_stall)
3261 del_timer_sync(&priv->monitor_recover);
3246} 3262}
3247 3263
3248static void iwl_init_hw_rates(struct iwl_priv *priv, 3264static void iwl_init_hw_rates(struct iwl_priv *priv,
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index d4c2a3e17c5..5180fb24cd3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -3051,6 +3051,99 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
3051 } 3051 }
3052 return 0; 3052 return 0;
3053} 3053}
3054EXPORT_SYMBOL(iwl_force_reset);
3055
3056/**
3057 * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
3058 *
3059 * During normal condition (no queue is stuck), the timer is continually set to
3060 * execute every monitor_recover_period milliseconds after the last timer
3061 * expired. When the queue read_ptr is at the same place, the timer is
3062 * shorten to 100mSecs. This is
3063 * 1) to reduce the chance that the read_ptr may wrap around (not stuck)
3064 * 2) to detect the stuck queues quicker before the station and AP can
3065 * disassociate each other.
3066 *
3067 * This function monitors all the tx queues and recover from it if any
3068 * of the queues are stuck.
3069 * 1. It first check the cmd queue for stuck conditions. If it is stuck,
3070 * it will recover by resetting the firmware and return.
3071 * 2. Then, it checks for station association. If it associates it will check
3072 * other queues. If any queue is stuck, it will recover by resetting
3073 * the firmware.
3074 * Note: It the number of times the queue read_ptr to be at the same place to
3075 * be MAX_REPEAT+1 in order to consider to be stuck.
3076 */
3077/*
3078 * The maximum number of times the read pointer of the tx queue at the
3079 * same place without considering to be stuck.
3080 */
3081#define MAX_REPEAT (2)
3082static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
3083{
3084 struct iwl_tx_queue *txq;
3085 struct iwl_queue *q;
3086
3087 txq = &priv->txq[cnt];
3088 q = &txq->q;
3089 /* queue is empty, skip */
3090 if (q->read_ptr != q->write_ptr) {
3091 if (q->read_ptr == q->last_read_ptr) {
3092 /* a queue has not been read from last time */
3093 if (q->repeat_same_read_ptr > MAX_REPEAT) {
3094 IWL_ERR(priv,
3095 "queue %d stuck %d time. Fw reload.\n",
3096 q->id, q->repeat_same_read_ptr);
3097 q->repeat_same_read_ptr = 0;
3098 iwl_force_reset(priv, IWL_FW_RESET);
3099 } else {
3100 q->repeat_same_read_ptr++;
3101 IWL_DEBUG_RADIO(priv,
3102 "queue %d, not read %d time\n",
3103 q->id,
3104 q->repeat_same_read_ptr);
3105 mod_timer(&priv->monitor_recover, jiffies +
3106 msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS));
3107 }
3108 return 1;
3109 } else {
3110 q->last_read_ptr = q->read_ptr;
3111 q->repeat_same_read_ptr = 0;
3112 }
3113 }
3114 return 0;
3115}
3116
3117void iwl_bg_monitor_recover(unsigned long data)
3118{
3119 struct iwl_priv *priv = (struct iwl_priv *)data;
3120 int cnt;
3121
3122 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
3123 return;
3124
3125 /* monitor and check for stuck cmd queue */
3126 if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM))
3127 return;
3128
3129 /* monitor and check for other stuck queues */
3130 if (iwl_is_associated(priv)) {
3131 for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
3132 /* skip as we already checked the command queue */
3133 if (cnt == IWL_CMD_QUEUE_NUM)
3134 continue;
3135 if (iwl_check_stuck_queue(priv, cnt))
3136 return;
3137 }
3138 }
3139 /*
3140 * Reschedule the timer to occur in
3141 * priv->cfg->monitor_recover_period
3142 */
3143 mod_timer(&priv->monitor_recover,
3144 jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period));
3145}
3146EXPORT_SYMBOL(iwl_bg_monitor_recover);
3054 3147
3055#ifdef CONFIG_PM 3148#ifdef CONFIG_PM
3056 3149
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 2482a25d543..c4cd1deb3b3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -191,6 +191,8 @@ struct iwl_lib_ops {
191 struct iwl_temp_ops temp_ops; 191 struct iwl_temp_ops temp_ops;
192 /* station management */ 192 /* station management */
193 void (*add_bcast_station)(struct iwl_priv *priv); 193 void (*add_bcast_station)(struct iwl_priv *priv);
194 /* recover from tx queue stall */
195 void (*recover_from_tx_stall)(unsigned long data);
194}; 196};
195 197
196struct iwl_led_ops { 198struct iwl_led_ops {
@@ -295,6 +297,8 @@ struct iwl_cfg {
295 const bool support_wimax_coexist; 297 const bool support_wimax_coexist;
296 u8 plcp_delta_threshold; 298 u8 plcp_delta_threshold;
297 s32 chain_noise_scale; 299 s32 chain_noise_scale;
300 /* timer period for monitor the driver queues */
301 u32 monitor_recover_period;
298}; 302};
299 303
300/*************************** 304/***************************
@@ -568,6 +572,9 @@ static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
568 pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); 572 pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
569 return pci_lnk_ctl; 573 return pci_lnk_ctl;
570} 574}
575
576void iwl_bg_monitor_recover(unsigned long data);
577
571#ifdef CONFIG_PM 578#ifdef CONFIG_PM
572int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state); 579int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
573int iwl_pci_resume(struct pci_dev *pdev); 580int iwl_pci_resume(struct pci_dev *pdev);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 9c676ea420b..bb4cba508ac 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -183,6 +183,10 @@ struct iwl_queue {
183 int n_bd; /* number of BDs in this queue */ 183 int n_bd; /* number of BDs in this queue */
184 int write_ptr; /* 1-st empty entry (index) host_w*/ 184 int write_ptr; /* 1-st empty entry (index) host_w*/
185 int read_ptr; /* last used entry (index) host_r*/ 185 int read_ptr; /* last used entry (index) host_r*/
186 /* use for monitoring and recovering the stuck queue */
187 int last_read_ptr; /* storing the last read_ptr */
188 /* number of time read_ptr and last_read_ptr are the same */
189 u8 repeat_same_read_ptr;
186 dma_addr_t dma_addr; /* physical addr for BD's */ 190 dma_addr_t dma_addr; /* physical addr for BD's */
187 int n_window; /* safe queue window */ 191 int n_window; /* safe queue window */
188 u32 id; 192 u32 id;
@@ -1044,6 +1048,11 @@ struct iwl_event_log {
1044#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) 1048#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
1045#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) 1049#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
1046 1050
1051/* timer constants use to monitor and recover stuck tx queues in mSecs */
1052#define IWL_MONITORING_PERIOD (1000)
1053#define IWL_ONE_HUNDRED_MSECS (100)
1054#define IWL_SIXTY_SECS (60000)
1055
1047enum iwl_reset { 1056enum iwl_reset {
1048 IWL_RF_RESET = 0, 1057 IWL_RF_RESET = 0,
1049 IWL_FW_RESET, 1058 IWL_FW_RESET,
@@ -1354,6 +1363,7 @@ struct iwl_priv {
1354 struct work_struct run_time_calib_work; 1363 struct work_struct run_time_calib_work;
1355 struct timer_list statistics_periodic; 1364 struct timer_list statistics_periodic;
1356 struct timer_list ucode_trace; 1365 struct timer_list ucode_trace;
1366 struct timer_list monitor_recover;
1357 bool hw_ready; 1367 bool hw_ready;
1358 1368
1359 struct iwl_event_log event_log; 1369 struct iwl_event_log event_log;
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 045e4a67344..34c983833a8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -322,6 +322,8 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
322 q->high_mark = 2; 322 q->high_mark = 2;
323 323
324 q->write_ptr = q->read_ptr = 0; 324 q->write_ptr = q->read_ptr = 0;
325 q->last_read_ptr = 0;
326 q->repeat_same_read_ptr = 0;
325 327
326 return 0; 328 return 0;
327} 329}
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 6687b945655..4995134d7e4 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -2501,6 +2501,13 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
2501 /* After the ALIVE response, we can send commands to 3945 uCode */ 2501 /* After the ALIVE response, we can send commands to 3945 uCode */
2502 set_bit(STATUS_ALIVE, &priv->status); 2502 set_bit(STATUS_ALIVE, &priv->status);
2503 2503
2504 if (priv->cfg->ops->lib->recover_from_tx_stall) {
2505 /* Enable timer to monitor the driver queues */
2506 mod_timer(&priv->monitor_recover,
2507 jiffies +
2508 msecs_to_jiffies(priv->cfg->monitor_recover_period));
2509 }
2510
2504 if (iwl_is_rfkill(priv)) 2511 if (iwl_is_rfkill(priv))
2505 return; 2512 return;
2506 2513
@@ -3796,6 +3803,13 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv)
3796 3803
3797 iwl3945_hw_setup_deferred_work(priv); 3804 iwl3945_hw_setup_deferred_work(priv);
3798 3805
3806 if (priv->cfg->ops->lib->recover_from_tx_stall) {
3807 init_timer(&priv->monitor_recover);
3808 priv->monitor_recover.data = (unsigned long)priv;
3809 priv->monitor_recover.function =
3810 priv->cfg->ops->lib->recover_from_tx_stall;
3811 }
3812
3799 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) 3813 tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
3800 iwl3945_irq_tasklet, (unsigned long)priv); 3814 iwl3945_irq_tasklet, (unsigned long)priv);
3801} 3815}
@@ -3808,6 +3822,8 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv)
3808 cancel_delayed_work(&priv->scan_check); 3822 cancel_delayed_work(&priv->scan_check);
3809 cancel_delayed_work(&priv->alive_start); 3823 cancel_delayed_work(&priv->alive_start);
3810 cancel_work_sync(&priv->beacon_update); 3824 cancel_work_sync(&priv->beacon_update);
3825 if (priv->cfg->ops->lib->recover_from_tx_stall)
3826 del_timer_sync(&priv->monitor_recover);
3811} 3827}
3812 3828
3813static struct attribute *iwl3945_sysfs_entries[] = { 3829static struct attribute *iwl3945_sysfs_entries[] = {