diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-rx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-rx.c | 162 |
1 files changed, 154 insertions, 8 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index fd84d53db3ac..feee76181ac5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c | |||
@@ -227,8 +227,158 @@ void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, | |||
227 | priv->measurement_status |= MEASUREMENT_READY; | 227 | priv->measurement_status |= MEASUREMENT_READY; |
228 | } | 228 | } |
229 | 229 | ||
230 | void iwl_recover_from_statistics(struct iwl_priv *priv, | 230 | /* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ |
231 | struct iwl_rx_packet *pkt) | 231 | #define ACK_CNT_RATIO (50) |
232 | #define BA_TIMEOUT_CNT (5) | ||
233 | #define BA_TIMEOUT_MAX (16) | ||
234 | |||
235 | /** | ||
236 | * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. | ||
237 | * | ||
238 | * When the ACK count ratio is low and aggregated BA timeout retries exceeding | ||
239 | * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal | ||
240 | * operation state. | ||
241 | */ | ||
242 | static bool iwl_good_ack_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) | ||
243 | { | ||
244 | int actual_delta, expected_delta, ba_timeout_delta; | ||
245 | struct statistics_tx *cur, *old; | ||
246 | |||
247 | if (priv->_agn.agg_tids_count) | ||
248 | return true; | ||
249 | |||
250 | if (iwl_bt_statistics(priv)) { | ||
251 | cur = &pkt->u.stats_bt.tx; | ||
252 | old = &priv->_agn.statistics_bt.tx; | ||
253 | } else { | ||
254 | cur = &pkt->u.stats.tx; | ||
255 | old = &priv->_agn.statistics.tx; | ||
256 | } | ||
257 | |||
258 | actual_delta = le32_to_cpu(cur->actual_ack_cnt) - | ||
259 | le32_to_cpu(old->actual_ack_cnt); | ||
260 | expected_delta = le32_to_cpu(cur->expected_ack_cnt) - | ||
261 | le32_to_cpu(old->expected_ack_cnt); | ||
262 | |||
263 | /* Values should not be negative, but we do not trust the firmware */ | ||
264 | if (actual_delta <= 0 || expected_delta <= 0) | ||
265 | return true; | ||
266 | |||
267 | ba_timeout_delta = le32_to_cpu(cur->agg.ba_timeout) - | ||
268 | le32_to_cpu(old->agg.ba_timeout); | ||
269 | |||
270 | if ((actual_delta * 100 / expected_delta) < ACK_CNT_RATIO && | ||
271 | ba_timeout_delta > BA_TIMEOUT_CNT) { | ||
272 | IWL_DEBUG_RADIO(priv, "deltas: actual %d expected %d ba_timeout %d\n", | ||
273 | actual_delta, expected_delta, ba_timeout_delta); | ||
274 | |||
275 | #ifdef CONFIG_IWLWIFI_DEBUGFS | ||
276 | /* | ||
277 | * This is ifdef'ed on DEBUGFS because otherwise the | ||
278 | * statistics aren't available. If DEBUGFS is set but | ||
279 | * DEBUG is not, these will just compile out. | ||
280 | */ | ||
281 | IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta %d\n", | ||
282 | priv->_agn.delta_statistics.tx.rx_detected_cnt); | ||
283 | IWL_DEBUG_RADIO(priv, | ||
284 | "ack_or_ba_timeout_collision delta %d\n", | ||
285 | priv->_agn.delta_statistics.tx.ack_or_ba_timeout_collision); | ||
286 | #endif | ||
287 | |||
288 | if (ba_timeout_delta >= BA_TIMEOUT_MAX) | ||
289 | return false; | ||
290 | } | ||
291 | |||
292 | return true; | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * iwl_good_plcp_health - checks for plcp error. | ||
297 | * | ||
298 | * When the plcp error is exceeding the thresholds, reset the radio | ||
299 | * to improve the throughput. | ||
300 | */ | ||
301 | static bool iwl_good_plcp_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) | ||
302 | { | ||
303 | bool rc = true; | ||
304 | int combined_plcp_delta; | ||
305 | unsigned int plcp_msec; | ||
306 | unsigned long plcp_received_jiffies; | ||
307 | |||
308 | if (priv->cfg->base_params->plcp_delta_threshold == | ||
309 | IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { | ||
310 | IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); | ||
311 | return rc; | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * check for plcp_err and trigger radio reset if it exceeds | ||
316 | * the plcp error threshold plcp_delta. | ||
317 | */ | ||
318 | plcp_received_jiffies = jiffies; | ||
319 | plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies - | ||
320 | (long) priv->plcp_jiffies); | ||
321 | priv->plcp_jiffies = plcp_received_jiffies; | ||
322 | /* | ||
323 | * check to make sure plcp_msec is not 0 to prevent division | ||
324 | * by zero. | ||
325 | */ | ||
326 | if (plcp_msec) { | ||
327 | struct statistics_rx_phy *ofdm; | ||
328 | struct statistics_rx_ht_phy *ofdm_ht; | ||
329 | |||
330 | if (iwl_bt_statistics(priv)) { | ||
331 | ofdm = &pkt->u.stats_bt.rx.ofdm; | ||
332 | ofdm_ht = &pkt->u.stats_bt.rx.ofdm_ht; | ||
333 | combined_plcp_delta = | ||
334 | (le32_to_cpu(ofdm->plcp_err) - | ||
335 | le32_to_cpu(priv->_agn.statistics_bt. | ||
336 | rx.ofdm.plcp_err)) + | ||
337 | (le32_to_cpu(ofdm_ht->plcp_err) - | ||
338 | le32_to_cpu(priv->_agn.statistics_bt. | ||
339 | rx.ofdm_ht.plcp_err)); | ||
340 | } else { | ||
341 | ofdm = &pkt->u.stats.rx.ofdm; | ||
342 | ofdm_ht = &pkt->u.stats.rx.ofdm_ht; | ||
343 | combined_plcp_delta = | ||
344 | (le32_to_cpu(ofdm->plcp_err) - | ||
345 | le32_to_cpu(priv->_agn.statistics. | ||
346 | rx.ofdm.plcp_err)) + | ||
347 | (le32_to_cpu(ofdm_ht->plcp_err) - | ||
348 | le32_to_cpu(priv->_agn.statistics. | ||
349 | rx.ofdm_ht.plcp_err)); | ||
350 | } | ||
351 | |||
352 | if ((combined_plcp_delta > 0) && | ||
353 | ((combined_plcp_delta * 100) / plcp_msec) > | ||
354 | priv->cfg->base_params->plcp_delta_threshold) { | ||
355 | /* | ||
356 | * if plcp_err exceed the threshold, | ||
357 | * the following data is printed in csv format: | ||
358 | * Text: plcp_err exceeded %d, | ||
359 | * Received ofdm.plcp_err, | ||
360 | * Current ofdm.plcp_err, | ||
361 | * Received ofdm_ht.plcp_err, | ||
362 | * Current ofdm_ht.plcp_err, | ||
363 | * combined_plcp_delta, | ||
364 | * plcp_msec | ||
365 | */ | ||
366 | IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " | ||
367 | "%u, %u, %u, %u, %d, %u mSecs\n", | ||
368 | priv->cfg->base_params->plcp_delta_threshold, | ||
369 | le32_to_cpu(ofdm->plcp_err), | ||
370 | le32_to_cpu(ofdm->plcp_err), | ||
371 | le32_to_cpu(ofdm_ht->plcp_err), | ||
372 | le32_to_cpu(ofdm_ht->plcp_err), | ||
373 | combined_plcp_delta, plcp_msec); | ||
374 | |||
375 | rc = false; | ||
376 | } | ||
377 | } | ||
378 | return rc; | ||
379 | } | ||
380 | |||
381 | void iwl_recover_from_statistics(struct iwl_priv *priv, struct iwl_rx_packet *pkt) | ||
232 | { | 382 | { |
233 | const struct iwl_mod_params *mod_params = priv->cfg->mod_params; | 383 | const struct iwl_mod_params *mod_params = priv->cfg->mod_params; |
234 | 384 | ||
@@ -236,17 +386,13 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, | |||
236 | !iwl_is_any_associated(priv)) | 386 | !iwl_is_any_associated(priv)) |
237 | return; | 387 | return; |
238 | 388 | ||
239 | if (mod_params->ack_check && | 389 | if (mod_params->ack_check && !iwl_good_ack_health(priv, pkt)) { |
240 | priv->cfg->ops->lib->check_ack_health && | ||
241 | !priv->cfg->ops->lib->check_ack_health(priv, pkt)) { | ||
242 | IWL_ERR(priv, "low ack count detected, restart firmware\n"); | 390 | IWL_ERR(priv, "low ack count detected, restart firmware\n"); |
243 | if (!iwl_force_reset(priv, IWL_FW_RESET, false)) | 391 | if (!iwl_force_reset(priv, IWL_FW_RESET, false)) |
244 | return; | 392 | return; |
245 | } | 393 | } |
246 | 394 | ||
247 | if (mod_params->plcp_check && | 395 | if (mod_params->plcp_check && !iwl_good_plcp_health(priv, pkt)) |
248 | priv->cfg->ops->lib->check_plcp_health && | ||
249 | !priv->cfg->ops->lib->check_plcp_health(priv, pkt)) | ||
250 | iwl_force_reset(priv, IWL_RF_RESET, false); | 396 | iwl_force_reset(priv, IWL_RF_RESET, false); |
251 | } | 397 | } |
252 | 398 | ||