diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-core.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 93 |
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 | } |
3054 | EXPORT_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) | ||
3082 | static 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 | |||
3117 | void 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 | } | ||
3146 | EXPORT_SYMBOL(iwl_bg_monitor_recover); | ||
3054 | 3147 | ||
3055 | #ifdef CONFIG_PM | 3148 | #ifdef CONFIG_PM |
3056 | 3149 | ||