aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-core.c
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/iwl-core.c
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/iwl-core.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c93
1 files changed, 93 insertions, 0 deletions
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