|
@@ -820,6 +820,26 @@ out:
|
|
|
return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Simple function to compare two rate scale table types
|
|
|
+ */
|
|
|
+static bool table_type_matches(struct iwl_scale_tbl_info *a,
|
|
|
+ struct iwl_scale_tbl_info *b)
|
|
|
+{
|
|
|
+ return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) &&
|
|
|
+ (a->is_SGI == b->is_SGI);
|
|
|
+}
|
|
|
+/*
|
|
|
+ * Static function to get the expected throughput from an iwl_scale_tbl_info
|
|
|
+ * that wraps a NULL pointer check
|
|
|
+ */
|
|
|
+static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
|
|
|
+{
|
|
|
+ if (tbl->expected_tpt)
|
|
|
+ return tbl->expected_tpt[rs_index];
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* mac80211 sends us Tx status
|
|
|
*/
|
|
@@ -827,21 +847,19 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|
|
struct ieee80211_sta *sta, void *priv_sta,
|
|
|
struct sk_buff *skb)
|
|
|
{
|
|
|
- int status;
|
|
|
- u8 retries;
|
|
|
- int rs_index, mac_index, index = 0;
|
|
|
+ int legacy_success;
|
|
|
+ int retries;
|
|
|
+ int rs_index, mac_index, i;
|
|
|
struct iwl_lq_sta *lq_sta = priv_sta;
|
|
|
struct iwl_link_quality_cmd *table;
|
|
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
|
|
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
|
|
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
|
|
struct iwl_rate_scale_data *window = NULL;
|
|
|
- struct iwl_rate_scale_data *search_win = NULL;
|
|
|
enum mac80211_rate_control_flags mac_flags;
|
|
|
u32 tx_rate;
|
|
|
struct iwl_scale_tbl_info tbl_type;
|
|
|
- struct iwl_scale_tbl_info *curr_tbl, *search_tbl;
|
|
|
- u8 active_index = 0;
|
|
|
+ struct iwl_scale_tbl_info *curr_tbl, *other_tbl;
|
|
|
s32 tpt = 0;
|
|
|
|
|
|
IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
|
|
@@ -855,25 +873,10 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|
|
!(info->flags & IEEE80211_TX_STAT_AMPDU))
|
|
|
return;
|
|
|
|
|
|
- if (info->flags & IEEE80211_TX_STAT_AMPDU)
|
|
|
- retries = 0;
|
|
|
- else
|
|
|
- retries = info->status.rates[0].count - 1;
|
|
|
-
|
|
|
- if (retries > 15)
|
|
|
- retries = 15;
|
|
|
|
|
|
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
|
|
|
!lq_sta->ibss_sta_added)
|
|
|
- goto out;
|
|
|
-
|
|
|
- table = &lq_sta->lq;
|
|
|
- active_index = lq_sta->active_tbl;
|
|
|
-
|
|
|
- curr_tbl = &(lq_sta->lq_info[active_index]);
|
|
|
- search_tbl = &(lq_sta->lq_info[(1 - active_index)]);
|
|
|
- window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]);
|
|
|
- search_win = (struct iwl_rate_scale_data *)&(search_tbl->win[0]);
|
|
|
+ return;
|
|
|
|
|
|
/*
|
|
|
* Ignore this Tx frame response if its initial rate doesn't match
|
|
@@ -883,6 +886,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|
|
* to check "search" mode, or a prior "search" mode after we've moved
|
|
|
* to a new "search" mode (which might become the new "active" mode).
|
|
|
*/
|
|
|
+ table = &lq_sta->lq;
|
|
|
tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
|
|
|
rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index);
|
|
|
if (priv->band == IEEE80211_BAND_5GHZ)
|
|
@@ -901,7 +905,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|
|
if (priv->band == IEEE80211_BAND_2GHZ)
|
|
|
mac_index += IWL_FIRST_OFDM_RATE;
|
|
|
}
|
|
|
-
|
|
|
+ /* Here we actually compare this rate to the latest LQ command */
|
|
|
if ((mac_index < 0) ||
|
|
|
(tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
|
|
|
(tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
|
|
@@ -911,124 +915,106 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|
|
(!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
|
|
|
(rs_index != mac_index)) {
|
|
|
IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate);
|
|
|
- /* the last LQ command could failed so the LQ in ucode not
|
|
|
- * the same in driver sync up
|
|
|
+ /*
|
|
|
+ * Since rates mis-match, the last LQ command may have failed.
|
|
|
+ * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
|
|
|
+ * ... driver.
|
|
|
*/
|
|
|
lq_sta->missed_rate_counter++;
|
|
|
if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
|
|
|
lq_sta->missed_rate_counter = 0;
|
|
|
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
|
|
|
}
|
|
|
- goto out;
|
|
|
+ /* Regardless, ignore this status info for outdated rate */
|
|
|
+ return;
|
|
|
+ } else
|
|
|
+ /* Rate did match, so reset the missed_rate_counter */
|
|
|
+ lq_sta->missed_rate_counter = 0;
|
|
|
+
|
|
|
+ /* Figure out if rate scale algorithm is in active or search table */
|
|
|
+ if (table_type_matches(&tbl_type,
|
|
|
+ &(lq_sta->lq_info[lq_sta->active_tbl]))) {
|
|
|
+ curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
|
|
|
+ other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
|
|
|
+ } else if (table_type_matches(&tbl_type,
|
|
|
+ &lq_sta->lq_info[1 - lq_sta->active_tbl])) {
|
|
|
+ curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
|
|
|
+ other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
|
|
|
+ } else {
|
|
|
+ IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n");
|
|
|
+ return;
|
|
|
}
|
|
|
+ window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]);
|
|
|
|
|
|
- lq_sta->missed_rate_counter = 0;
|
|
|
- /* Update frame history window with "failure" for each Tx retry. */
|
|
|
- while (retries) {
|
|
|
- /* Look up the rate and other info used for each tx attempt.
|
|
|
- * Each tx attempt steps one entry deeper in the rate table. */
|
|
|
- tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags);
|
|
|
- rs_get_tbl_info_from_mcs(tx_rate, priv->band,
|
|
|
- &tbl_type, &rs_index);
|
|
|
-
|
|
|
- /* If type matches "search" table,
|
|
|
- * add failure to "search" history */
|
|
|
- if ((tbl_type.lq_type == search_tbl->lq_type) &&
|
|
|
- (tbl_type.ant_type == search_tbl->ant_type) &&
|
|
|
- (tbl_type.is_SGI == search_tbl->is_SGI)) {
|
|
|
- if (search_tbl->expected_tpt)
|
|
|
- tpt = search_tbl->expected_tpt[rs_index];
|
|
|
- else
|
|
|
- tpt = 0;
|
|
|
- rs_collect_tx_data(search_win, rs_index, tpt, 1, 0);
|
|
|
-
|
|
|
- /* Else if type matches "current/active" table,
|
|
|
- * add failure to "current/active" history */
|
|
|
- } else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
|
|
|
- (tbl_type.ant_type == curr_tbl->ant_type) &&
|
|
|
- (tbl_type.is_SGI == curr_tbl->is_SGI)) {
|
|
|
- if (curr_tbl->expected_tpt)
|
|
|
- tpt = curr_tbl->expected_tpt[rs_index];
|
|
|
- else
|
|
|
- tpt = 0;
|
|
|
- rs_collect_tx_data(window, rs_index, tpt, 1, 0);
|
|
|
+ /*
|
|
|
+ * Updating the frame history depends on whether packets were
|
|
|
+ * aggregated.
|
|
|
+ *
|
|
|
+ * For aggregation, all packets were transmitted at the same rate, the
|
|
|
+ * first index into rate scale table.
|
|
|
+ */
|
|
|
+ if (info->flags & IEEE80211_TX_STAT_AMPDU) {
|
|
|
+ tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags);
|
|
|
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type,
|
|
|
+ &rs_index);
|
|
|
+ tpt = get_expected_tpt(curr_tbl, rs_index);
|
|
|
+ rs_collect_tx_data(window, rs_index, tpt,
|
|
|
+ info->status.ampdu_ack_len,
|
|
|
+ info->status.ampdu_ack_map);
|
|
|
+
|
|
|
+ /* Update success/fail counts if not searching for new mode */
|
|
|
+ if (lq_sta->stay_in_tbl) {
|
|
|
+ lq_sta->total_success += info->status.ampdu_ack_map;
|
|
|
+ lq_sta->total_failed += (info->status.ampdu_ack_len -
|
|
|
+ info->status.ampdu_ack_map);
|
|
|
}
|
|
|
-
|
|
|
- /* If not searching for a new mode, increment failed counter
|
|
|
- * ... this helps determine when to start searching again */
|
|
|
- if (lq_sta->stay_in_tbl)
|
|
|
- lq_sta->total_failed++;
|
|
|
- --retries;
|
|
|
- index++;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
+ } else {
|
|
|
/*
|
|
|
- * Find (by rate) the history window to update with final Tx attempt;
|
|
|
- * if Tx was successful first try, use original rate,
|
|
|
- * else look up the rate that was, finally, successful.
|
|
|
+ * For legacy, update frame history with for each Tx retry.
|
|
|
*/
|
|
|
- tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags);
|
|
|
- lq_sta->last_rate_n_flags = tx_rate;
|
|
|
- rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index);
|
|
|
-
|
|
|
- /* Update frame history window with "success" if Tx got ACKed ... */
|
|
|
- status = !!(info->flags & IEEE80211_TX_STAT_ACK);
|
|
|
-
|
|
|
- /* If type matches "search" table,
|
|
|
- * add final tx status to "search" history */
|
|
|
- if ((tbl_type.lq_type == search_tbl->lq_type) &&
|
|
|
- (tbl_type.ant_type == search_tbl->ant_type) &&
|
|
|
- (tbl_type.is_SGI == search_tbl->is_SGI)) {
|
|
|
- if (search_tbl->expected_tpt)
|
|
|
- tpt = search_tbl->expected_tpt[rs_index];
|
|
|
- else
|
|
|
- tpt = 0;
|
|
|
- if (info->flags & IEEE80211_TX_STAT_AMPDU)
|
|
|
- rs_collect_tx_data(search_win, rs_index, tpt,
|
|
|
- info->status.ampdu_ack_len,
|
|
|
- info->status.ampdu_ack_map);
|
|
|
- else
|
|
|
- rs_collect_tx_data(search_win, rs_index, tpt,
|
|
|
- 1, status);
|
|
|
- /* Else if type matches "current/active" table,
|
|
|
- * add final tx status to "current/active" history */
|
|
|
- } else if ((tbl_type.lq_type == curr_tbl->lq_type) &&
|
|
|
- (tbl_type.ant_type == curr_tbl->ant_type) &&
|
|
|
- (tbl_type.is_SGI == curr_tbl->is_SGI)) {
|
|
|
- if (curr_tbl->expected_tpt)
|
|
|
- tpt = curr_tbl->expected_tpt[rs_index];
|
|
|
- else
|
|
|
- tpt = 0;
|
|
|
- if (info->flags & IEEE80211_TX_STAT_AMPDU)
|
|
|
- rs_collect_tx_data(window, rs_index, tpt,
|
|
|
- info->status.ampdu_ack_len,
|
|
|
- info->status.ampdu_ack_map);
|
|
|
- else
|
|
|
- rs_collect_tx_data(window, rs_index, tpt,
|
|
|
- 1, status);
|
|
|
- }
|
|
|
+ retries = info->status.rates[0].count - 1;
|
|
|
+ /* HW doesn't send more than 15 retries */
|
|
|
+ retries = min(retries, 15);
|
|
|
+
|
|
|
+ /* The last transmission may have been successful */
|
|
|
+ legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
|
|
|
+ /* Collect data for each rate used during failed TX attempts */
|
|
|
+ for (i = 0; i <= retries; ++i) {
|
|
|
+ tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags);
|
|
|
+ rs_get_tbl_info_from_mcs(tx_rate, priv->band,
|
|
|
+ &tbl_type, &rs_index);
|
|
|
+ /*
|
|
|
+ * Only collect stats if retried rate is in the same RS
|
|
|
+ * table as active/search.
|
|
|
+ */
|
|
|
+ if (table_type_matches(&tbl_type, curr_tbl))
|
|
|
+ tpt = get_expected_tpt(curr_tbl, rs_index);
|
|
|
+ else if (table_type_matches(&tbl_type, other_tbl))
|
|
|
+ tpt = get_expected_tpt(other_tbl, rs_index);
|
|
|
+ else
|
|
|
+ continue;
|
|
|
|
|
|
- /* If not searching for new mode, increment success/failed counter
|
|
|
- * ... these help determine when to start searching again */
|
|
|
- if (lq_sta->stay_in_tbl) {
|
|
|
- if (info->flags & IEEE80211_TX_STAT_AMPDU) {
|
|
|
- lq_sta->total_success += info->status.ampdu_ack_map;
|
|
|
- lq_sta->total_failed +=
|
|
|
- (info->status.ampdu_ack_len - info->status.ampdu_ack_map);
|
|
|
- } else {
|
|
|
- if (status)
|
|
|
- lq_sta->total_success++;
|
|
|
+ /* Constants mean 1 transmission, 0 successes */
|
|
|
+ if (i < retries)
|
|
|
+ rs_collect_tx_data(window, rs_index, tpt, 1,
|
|
|
+ 0);
|
|
|
else
|
|
|
- lq_sta->total_failed++;
|
|
|
+ rs_collect_tx_data(window, rs_index, tpt, 1,
|
|
|
+ legacy_success);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Update success/fail counts if not searching for new mode */
|
|
|
+ if (lq_sta->stay_in_tbl) {
|
|
|
+ lq_sta->total_success += legacy_success;
|
|
|
+ lq_sta->total_failed += retries + (1 - legacy_success);
|
|
|
}
|
|
|
}
|
|
|
+ /* The last TX rate is cached in lq_sta; it's set in if/else above */
|
|
|
+ lq_sta->last_rate_n_flags = tx_rate;
|
|
|
|
|
|
/* See if there's a better rate or modulation mode to try. */
|
|
|
if (sta && sta->supp_rates[sband->band])
|
|
|
rs_rate_scale_perform(priv, skb, sta, lq_sta);
|
|
|
-out:
|
|
|
- return;
|
|
|
}
|
|
|
|
|
|
/*
|