diff options
author | Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com> | 2010-02-10 13:27:34 -0500 |
---|---|---|
committer | Reinette Chatre <reinette.chatre@intel.com> | 2010-02-11 13:27:41 -0500 |
commit | 1db5950f1d0b82e07371b211a48317b8972da063 (patch) | |
tree | 6fc4f38f2c8cfd3a212c5eb18e326dd5d409ca2c /drivers | |
parent | a9e10fb9b1c6ad16e73cf2656951fce3a817611e (diff) |
iwlwifi: Monitor and recover the aggregation TX flow failure
This change 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 16, no tx packets
(tcp, udp, or ping - icmp) can be delivered. The driver recovers from this
situation by reseting the uCode firmware. If the actual_ack_count/expected_
ack_count ratio drops below 50% (but not 0) and the aggregated ba_timeout
retries just exceed 5 (but not 16), then the driver can reset the radio to
bring the throughput up.
Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 14 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-rx.c | 46 |
3 files changed, 62 insertions, 1 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 31b156d58d7f..4157c6c8645f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
@@ -2941,10 +2941,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, | |||
2941 | return ret; | 2941 | return ret; |
2942 | case IEEE80211_AMPDU_TX_START: | 2942 | case IEEE80211_AMPDU_TX_START: |
2943 | IWL_DEBUG_HT(priv, "start Tx\n"); | 2943 | IWL_DEBUG_HT(priv, "start Tx\n"); |
2944 | return iwl_tx_agg_start(priv, sta->addr, tid, ssn); | 2944 | ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn); |
2945 | if (ret == 0) { | ||
2946 | priv->agg_tids_count++; | ||
2947 | IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", | ||
2948 | priv->agg_tids_count); | ||
2949 | } | ||
2950 | return ret; | ||
2945 | case IEEE80211_AMPDU_TX_STOP: | 2951 | case IEEE80211_AMPDU_TX_STOP: |
2946 | IWL_DEBUG_HT(priv, "stop Tx\n"); | 2952 | IWL_DEBUG_HT(priv, "stop Tx\n"); |
2947 | ret = iwl_tx_agg_stop(priv, sta->addr, tid); | 2953 | ret = iwl_tx_agg_stop(priv, sta->addr, tid); |
2954 | if ((ret == 0) && (priv->agg_tids_count > 0)) { | ||
2955 | priv->agg_tids_count--; | ||
2956 | IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", | ||
2957 | priv->agg_tids_count); | ||
2958 | } | ||
2948 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | 2959 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
2949 | return 0; | 2960 | return 0; |
2950 | else | 2961 | else |
@@ -3364,6 +3375,7 @@ static int iwl_init_drv(struct iwl_priv *priv) | |||
3364 | priv->iw_mode = NL80211_IFTYPE_STATION; | 3375 | priv->iw_mode = NL80211_IFTYPE_STATION; |
3365 | priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; | 3376 | priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; |
3366 | priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; | 3377 | priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; |
3378 | priv->agg_tids_count = 0; | ||
3367 | 3379 | ||
3368 | /* Choose which receivers/antennas to use */ | 3380 | /* Choose which receivers/antennas to use */ |
3369 | if (priv->cfg->ops->hcmd->set_rxon_chain) | 3381 | if (priv->cfg->ops->hcmd->set_rxon_chain) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 71cf1558e640..f81317d478ee 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h | |||
@@ -1072,6 +1072,9 @@ struct iwl_priv { | |||
1072 | /* storing the jiffies when the plcp error rate is received */ | 1072 | /* storing the jiffies when the plcp error rate is received */ |
1073 | unsigned long plcp_jiffies; | 1073 | unsigned long plcp_jiffies; |
1074 | 1074 | ||
1075 | /* reporting the number of tids has AGG on. 0 means no AGGREGATION */ | ||
1076 | u8 agg_tids_count; | ||
1077 | |||
1075 | /* force reset */ | 1078 | /* force reset */ |
1076 | unsigned long last_force_reset_jiffies; | 1079 | unsigned long last_force_reset_jiffies; |
1077 | 1080 | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index aba8f4c20c1b..fed554accedc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c | |||
@@ -616,6 +616,11 @@ 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 | void iwl_rx_statistics(struct iwl_priv *priv, | 625 | void iwl_rx_statistics(struct iwl_priv *priv, |
621 | struct iwl_rx_mem_buffer *rxb) | 626 | struct iwl_rx_mem_buffer *rxb) |
@@ -625,6 +630,9 @@ void iwl_rx_statistics(struct iwl_priv *priv, | |||
625 | int combined_plcp_delta; | 630 | int combined_plcp_delta; |
626 | unsigned int plcp_msec; | 631 | unsigned int plcp_msec; |
627 | unsigned long plcp_received_jiffies; | 632 | unsigned long plcp_received_jiffies; |
633 | int actual_ack_cnt_delta; | ||
634 | int expected_ack_cnt_delta; | ||
635 | int ba_timeout_delta; | ||
628 | 636 | ||
629 | IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", | 637 | IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", |
630 | (int)sizeof(priv->statistics), | 638 | (int)sizeof(priv->statistics), |
@@ -639,6 +647,44 @@ void iwl_rx_statistics(struct iwl_priv *priv, | |||
639 | #ifdef CONFIG_IWLWIFI_DEBUG | 647 | #ifdef CONFIG_IWLWIFI_DEBUG |
640 | iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); | 648 | iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); |
641 | #endif | 649 | #endif |
650 | actual_ack_cnt_delta = le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) - | ||
651 | le32_to_cpu(priv->statistics.tx.actual_ack_cnt); | ||
652 | expected_ack_cnt_delta = le32_to_cpu( | ||
653 | pkt->u.stats.tx.expected_ack_cnt) - | ||
654 | le32_to_cpu(priv->statistics.tx.expected_ack_cnt); | ||
655 | ba_timeout_delta = le32_to_cpu( | ||
656 | pkt->u.stats.tx.agg.ba_timeout) - | ||
657 | le32_to_cpu(priv->statistics.tx.agg.ba_timeout); | ||
658 | if ((priv->agg_tids_count > 0) && | ||
659 | (expected_ack_cnt_delta > 0) && | ||
660 | (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta) < | ||
661 | ACK_CNT_RATIO) && | ||
662 | (ba_timeout_delta > BA_TIMEOUT_CNT)) { | ||
663 | IWL_DEBUG_RADIO(priv, | ||
664 | "actual_ack_cnt delta = %d, expected_ack_cnt = %d\n", | ||
665 | actual_ack_cnt_delta, expected_ack_cnt_delta); | ||
666 | |||
667 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
668 | IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n", | ||
669 | priv->delta_statistics.tx.rx_detected_cnt); | ||
670 | IWL_DEBUG_RADIO(priv, | ||
671 | "ack_or_ba_timeout_collision delta = %d\n", | ||
672 | priv->delta_statistics.tx.ack_or_ba_timeout_collision); | ||
673 | #endif | ||
674 | IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", | ||
675 | ba_timeout_delta); | ||
676 | if ((actual_ack_cnt_delta == 0) && | ||
677 | (ba_timeout_delta >= | ||
678 | BA_TIMEOUT_MAX)) { | ||
679 | IWL_DEBUG_RADIO(priv, | ||
680 | "call iwl_force_reset(IWL_FW_RESET)\n"); | ||
681 | iwl_force_reset(priv, IWL_FW_RESET); | ||
682 | } else { | ||
683 | IWL_DEBUG_RADIO(priv, | ||
684 | "call iwl_force_reset(IWL_RF_RESET)\n"); | ||
685 | iwl_force_reset(priv, IWL_RF_RESET); | ||
686 | } | ||
687 | } | ||
642 | /* | 688 | /* |
643 | * check for plcp_err and trigger radio reset if it exceeds | 689 | * check for plcp_err and trigger radio reset if it exceeds |
644 | * the plcp error threshold plcp_delta. | 690 | * the plcp error threshold plcp_delta. |