diff options
author | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2010-03-04 16:38:59 -0500 |
---|---|---|
committer | Reinette Chatre <reinette.chatre@intel.com> | 2010-03-19 16:41:26 -0400 |
commit | d5a0ffa3eaf9e898f25a925813f1a723be7808f8 (patch) | |
tree | 37610c33a6802b6eac524cc2cd298705059cf18d /drivers/net/wireless | |
parent | beac5498b792ed8420885ee23e8d4f2885ee2d13 (diff) |
iwlwifi: Recover TX flow failure
Monitors the tx statistics to detect the drop in throughput.
When the throughput drops, the ratio of the actual_ack_count and the
expected_ack_count also drops. At the same time, the aggregated
ba_timeout (the number of ba timeout retries) also rises. If the
actual_ack_count/expected_ack_count ratio is 0 and the number of ba
timeout retries rises to BA_TIMEOUT_MAX, no tx packets can be delivered.
Reloading the uCode and bring the system back to normal operational
state.
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')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 14 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-rx.c | 50 |
3 files changed, 67 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 82b1a3fb54ef..e69e6256a41b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -2916,10 +2916,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, | |||
2916 | return ret; | 2916 | return ret; |
2917 | case IEEE80211_AMPDU_TX_START: | 2917 | case IEEE80211_AMPDU_TX_START: |
2918 | IWL_DEBUG_HT(priv, "start Tx\n"); | 2918 | IWL_DEBUG_HT(priv, "start Tx\n"); |
2919 | return iwl_tx_agg_start(priv, sta->addr, tid, ssn); | 2919 | ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn); |
2920 | if (ret == 0) { | ||
2921 | priv->_agn.agg_tids_count++; | ||
2922 | IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", | ||
2923 | priv->_agn.agg_tids_count); | ||
2924 | } | ||
2925 | return ret; | ||
2920 | case IEEE80211_AMPDU_TX_STOP: | 2926 | case IEEE80211_AMPDU_TX_STOP: |
2921 | IWL_DEBUG_HT(priv, "stop Tx\n"); | 2927 | IWL_DEBUG_HT(priv, "stop Tx\n"); |
2922 | ret = iwl_tx_agg_stop(priv, sta->addr, tid); | 2928 | ret = iwl_tx_agg_stop(priv, sta->addr, tid); |
2929 | if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) { | ||
2930 | priv->_agn.agg_tids_count--; | ||
2931 | IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", | ||
2932 | priv->_agn.agg_tids_count); | ||
2933 | } | ||
2923 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | 2934 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
2924 | return 0; | 2935 | return 0; |
2925 | else | 2936 | else |
@@ -3303,6 +3314,7 @@ static int iwl_init_drv(struct iwl_priv *priv) | |||
3303 | priv->iw_mode = NL80211_IFTYPE_STATION; | 3314 | priv->iw_mode = NL80211_IFTYPE_STATION; |
3304 | priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; | 3315 | priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; |
3305 | priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; | 3316 | priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; |
3317 | priv->_agn.agg_tids_count = 0; | ||
3306 | 3318 | ||
3307 | /* initialize force reset */ | 3319 | /* initialize force reset */ |
3308 | priv->force_reset[IWL_RF_RESET].reset_duration = | 3320 | priv->force_reset[IWL_RF_RESET].reset_duration = |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index bb4cba508acf..e847e6197a3d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -1300,6 +1300,11 @@ struct iwl_priv { | |||
1300 | int ict_index; | 1300 | int ict_index; |
1301 | u32 inta; | 1301 | u32 inta; |
1302 | bool use_ict; | 1302 | bool use_ict; |
1303 | /* | ||
1304 | * reporting the number of tids has AGG on. 0 means | ||
1305 | * no AGGREGATION | ||
1306 | */ | ||
1307 | u8 agg_tids_count; | ||
1303 | } _agn; | 1308 | } _agn; |
1304 | #endif | 1309 | #endif |
1305 | }; | 1310 | }; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index d368b8df8d87..7aef285d2818 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c | |||
@@ -616,9 +616,18 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, | |||
616 | 616 | ||
617 | #define REG_RECALIB_PERIOD (60) | 617 | #define REG_RECALIB_PERIOD (60) |
618 | 618 | ||
619 | /* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ | ||
620 | #define ACK_CNT_RATIO (50) | ||
621 | #define BA_TIMEOUT_CNT (5) | ||
622 | #define BA_TIMEOUT_MAX (16) | ||
623 | |||
619 | #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" | 624 | #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" |
620 | /* | 625 | /* |
621 | * This function checks for plcp error. | 626 | * This function checks for plcp error, ACK count ratios, aggregated BA |
627 | * timeout retries. | ||
628 | * - When the ACK count ratio is 0 and aggregated BA timeout retries is | ||
629 | * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting | ||
630 | * the firmware. | ||
622 | * - When the plcp error is exceeding the thresholds, it will reset the radio | 631 | * - When the plcp error is exceeding the thresholds, it will reset the radio |
623 | * to improve the throughput. | 632 | * to improve the throughput. |
624 | */ | 633 | */ |
@@ -628,6 +637,45 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, | |||
628 | int combined_plcp_delta; | 637 | int combined_plcp_delta; |
629 | unsigned int plcp_msec; | 638 | unsigned int plcp_msec; |
630 | unsigned long plcp_received_jiffies; | 639 | unsigned long plcp_received_jiffies; |
640 | int actual_ack_cnt_delta; | ||
641 | int expected_ack_cnt_delta; | ||
642 | int ba_timeout_delta; | ||
643 | |||
644 | actual_ack_cnt_delta = | ||
645 | le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) - | ||
646 | le32_to_cpu(priv->statistics.tx.actual_ack_cnt); | ||
647 | expected_ack_cnt_delta = | ||
648 | le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) - | ||
649 | le32_to_cpu(priv->statistics.tx.expected_ack_cnt); | ||
650 | ba_timeout_delta = | ||
651 | le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) - | ||
652 | le32_to_cpu(priv->statistics.tx.agg.ba_timeout); | ||
653 | if ((priv->_agn.agg_tids_count > 0) && | ||
654 | (expected_ack_cnt_delta > 0) && | ||
655 | (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta) | ||
656 | < ACK_CNT_RATIO) && | ||
657 | (ba_timeout_delta > BA_TIMEOUT_CNT)) { | ||
658 | IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d," | ||
659 | " expected_ack_cnt = %d\n", | ||
660 | actual_ack_cnt_delta, expected_ack_cnt_delta); | ||
661 | |||
662 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
663 | IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n", | ||
664 | priv->delta_statistics.tx.rx_detected_cnt); | ||
665 | IWL_DEBUG_RADIO(priv, | ||
666 | "ack_or_ba_timeout_collision delta = %d\n", | ||
667 | priv->delta_statistics.tx. | ||
668 | ack_or_ba_timeout_collision); | ||
669 | #endif | ||
670 | IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", | ||
671 | ba_timeout_delta); | ||
672 | if ((actual_ack_cnt_delta == 0) && | ||
673 | (ba_timeout_delta >= BA_TIMEOUT_MAX)) { | ||
674 | IWL_DEBUG_RADIO(priv, | ||
675 | "call iwl_force_reset(IWL_FW_RESET)\n"); | ||
676 | iwl_force_reset(priv, IWL_FW_RESET); | ||
677 | } | ||
678 | } | ||
631 | 679 | ||
632 | /* | 680 | /* |
633 | * check for plcp_err and trigger radio reset if it exceeds | 681 | * check for plcp_err and trigger radio reset if it exceeds |