Merge tag 'for-linus-2019-08-17' of git://git.kernel.dk/linux-block
[sfrench/cifs-2.6.git] / drivers / net / wireless / intel / iwlwifi / mvm / rs.c
index 8c9069f28a589c78c0df05c8c36f6fea3e2fa328..d3f04acfbacb9fa360388110423391f6032ea2ca 100644 (file)
@@ -1197,239 +1197,6 @@ static u8 rs_get_tid(struct ieee80211_hdr *hdr)
        return tid;
 }
 
-void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
-                         int tid, struct ieee80211_tx_info *info, bool ndp)
-{
-       int legacy_success;
-       int retries;
-       int i;
-       struct iwl_lq_cmd *table;
-       u32 lq_hwrate;
-       struct rs_rate lq_rate, tx_resp_rate;
-       struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
-       u32 tlc_info = (uintptr_t)info->status.status_driver_data[0];
-       u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK;
-       u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info);
-       u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
-       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
-       struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
-
-       /* Treat uninitialized rate scaling data same as non-existing. */
-       if (!lq_sta) {
-               IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
-               return;
-       } else if (!lq_sta->pers.drv) {
-               IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
-               return;
-       }
-
-       /* This packet was aggregated but doesn't carry status info */
-       if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
-           !(info->flags & IEEE80211_TX_STAT_AMPDU))
-               return;
-
-       if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band,
-                                   &tx_resp_rate)) {
-               WARN_ON_ONCE(1);
-               return;
-       }
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* Disable last tx check if we are debugging with fixed rate but
-        * update tx stats */
-       if (lq_sta->pers.dbg_fixed_rate) {
-               int index = tx_resp_rate.index;
-               enum rs_column column;
-               int attempts, success;
-
-               column = rs_get_column_from_rate(&tx_resp_rate);
-               if (WARN_ONCE(column == RS_COLUMN_INVALID,
-                             "Can't map rate 0x%x to column",
-                             tx_resp_hwrate))
-                       return;
-
-               if (info->flags & IEEE80211_TX_STAT_AMPDU) {
-                       attempts = info->status.ampdu_len;
-                       success = info->status.ampdu_ack_len;
-               } else {
-                       attempts = info->status.rates[0].count;
-                       success = !!(info->flags & IEEE80211_TX_STAT_ACK);
-               }
-
-               lq_sta->pers.tx_stats[column][index].total += attempts;
-               lq_sta->pers.tx_stats[column][index].success += success;
-
-               IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
-                              tx_resp_hwrate, success, attempts);
-               return;
-       }
-#endif
-
-       if (time_after(jiffies,
-                      (unsigned long)(lq_sta->last_tx +
-                                      (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
-               IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
-               iwl_mvm_rs_rate_init(mvm, sta, info->band, true);
-               return;
-       }
-       lq_sta->last_tx = jiffies;
-
-       /* Ignore this Tx frame response if its initial rate doesn't match
-        * that of latest Link Quality command.  There may be stragglers
-        * from a previous Link Quality command, but we're no longer interested
-        * in those; they're either from the "active" mode while we're trying
-        * 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;
-       lq_hwrate = le32_to_cpu(table->rs_table[0]);
-       if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) {
-               WARN_ON_ONCE(1);
-               return;
-       }
-
-       /* Here we actually compare this rate to the latest LQ command */
-       if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) {
-               IWL_DEBUG_RATE(mvm,
-                              "tx resp color 0x%x does not match 0x%x\n",
-                              lq_color, LQ_FLAG_COLOR_GET(table->flags));
-
-               /*
-                * 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_MVM_RS_MISSED_RATE_MAX) {
-                       lq_sta->missed_rate_counter = 0;
-                       IWL_DEBUG_RATE(mvm,
-                                      "Too many rates mismatch. Send sync LQ. rs_state %d\n",
-                                      lq_sta->rs_state);
-                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
-               }
-               /* 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;
-
-       if (!lq_sta->search_better_tbl) {
-               curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-               other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
-       } else {
-               curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
-               other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-       }
-
-       if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
-               IWL_DEBUG_RATE(mvm,
-                              "Neither active nor search matches tx rate\n");
-               tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-               rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
-               tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
-               rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
-               rs_dump_rate(mvm, &lq_rate, "ACTUAL");
-
-               /*
-                * no matching table found, let's by-pass the data collection
-                * and continue to perform rate scale to find the rate table
-                */
-               rs_stay_in_table(lq_sta, true);
-               goto done;
-       }
-
-       /*
-        * 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) {
-               rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index,
-                                   info->status.ampdu_len,
-                                   info->status.ampdu_ack_len,
-                                   reduced_txp);
-
-               /* ampdu_ack_len = 0 marks no BA was received. For TLC, treat
-                * it as a single frame loss as we don't want the success ratio
-                * to dip too quickly because a BA wasn't received.
-                * For TPC, there's no need for this optimisation since we want
-                * to recover very quickly from a bad power reduction and,
-                * therefore we'd like the success ratio to get an immediate hit
-                * when failing to get a BA, so we'd switch back to a lower or
-                * zero power reduction. When FW transmits agg with a rate
-                * different from the initial rate, it will not use reduced txp
-                * and will send BA notification twice (one empty with reduced
-                * txp equal to the value from LQ and one with reduced txp 0).
-                * We need to update counters for each txp level accordingly.
-                */
-               if (info->status.ampdu_ack_len == 0)
-                       info->status.ampdu_len = 1;
-
-               rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl, tx_resp_rate.index,
-                                   info->status.ampdu_len,
-                                   info->status.ampdu_ack_len);
-
-               /* Update success/fail counts if not searching for new mode */
-               if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
-                       lq_sta->total_success += info->status.ampdu_ack_len;
-                       lq_sta->total_failed += (info->status.ampdu_len -
-                                       info->status.ampdu_ack_len);
-               }
-       } else {
-               /* For legacy, update frame history with for each Tx retry. */
-               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) {
-                       lq_hwrate = le32_to_cpu(table->rs_table[i]);
-                       if (rs_rate_from_ucode_rate(lq_hwrate, info->band,
-                                                   &lq_rate)) {
-                               WARN_ON_ONCE(1);
-                               return;
-                       }
-
-                       /*
-                        * Only collect stats if retried rate is in the same RS
-                        * table as active/search.
-                        */
-                       if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
-                               tmp_tbl = curr_tbl;
-                       else if (rs_rate_column_match(&lq_rate,
-                                                     &other_tbl->rate))
-                               tmp_tbl = other_tbl;
-                       else
-                               continue;
-
-                       rs_collect_tpc_data(mvm, lq_sta, tmp_tbl,
-                                           tx_resp_rate.index, 1,
-                                           i < retries ? 0 : legacy_success,
-                                           reduced_txp);
-                       rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl,
-                                           tx_resp_rate.index, 1,
-                                           i < retries ? 0 : legacy_success);
-               }
-
-               /* Update success/fail counts if not searching for new mode */
-               if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
-                       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 = lq_hwrate;
-       IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
-done:
-       /* See if there's a better rate or modulation mode to try. */
-       if (sta->supp_rates[info->band])
-               rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp);
-}
-
 /*
  * mac80211 sends us Tx status
  */
@@ -1442,8 +1209,9 @@ static void rs_drv_mac80211_tx_status(void *mvm_r,
        struct iwl_op_mode *op_mode = mvm_r;
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
-       if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+       if (!mvmsta->vif)
                return;
 
        if (!ieee80211_is_data(hdr->frame_control) ||
@@ -1584,6 +1352,18 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
        tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw);
 }
 
+/* rs uses two tables, one is active and the second is for searching better
+ * configuration. This function, according to the index of the currently
+ * active table returns the search table, which is located at the
+ * index complementary to 1 according to the active table (active = 1,
+ * search = 0 or active = 0, search = 1).
+ * Since lq_info is an arary of size 2, make sure index cannot be out of bounds.
+ */
+static inline u8 rs_search_tbl(u8 active_tbl)
+{
+       return (active_tbl ^ 1) & 1;
+}
+
 static s32 rs_get_best_rate(struct iwl_mvm *mvm,
                            struct iwl_lq_sta *lq_sta,
                            struct iwl_scale_tbl_info *tbl,     /* "search" */
@@ -1794,7 +1574,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
                               struct iwl_scale_tbl_info *tbl)
 {
        rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
-       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
+       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq);
 }
 
 static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm,
@@ -1931,9 +1711,9 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
                               struct ieee80211_sta *sta,
                               enum rs_column col_id)
 {
-       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
        struct iwl_scale_tbl_info *search_tbl =
-                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+               &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
        struct rs_rate *rate = &search_tbl->rate;
        const struct rs_tx_column *column = &rs_tx_columns[col_id];
        const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column];
@@ -2341,7 +2121,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        if (!lq_sta->search_better_tbl)
                active_tbl = lq_sta->active_tbl;
        else
-               active_tbl = 1 - lq_sta->active_tbl;
+               active_tbl = rs_search_tbl(lq_sta->active_tbl);
 
        tbl = &(lq_sta->lq_info[active_tbl]);
        rate = &tbl->rate;
@@ -2565,7 +2345,7 @@ lq_update:
                /* If new "search" mode was selected, set up in uCode table */
                if (lq_sta->search_better_tbl) {
                        /* Access the "search" table, clear its history. */
-                       tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+                       tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
                        rs_rate_scale_clear_tbl_windows(mvm, tbl);
 
                        /* Use new "search" start rate */
@@ -2896,7 +2676,7 @@ void rs_update_last_rssi(struct iwl_mvm *mvm,
 static void rs_initialize_lq(struct iwl_mvm *mvm,
                             struct ieee80211_sta *sta,
                             struct iwl_lq_sta *lq_sta,
-                            enum nl80211_band band, bool update)
+                            enum nl80211_band band)
 {
        struct iwl_scale_tbl_info *tbl;
        struct rs_rate *rate;
@@ -2908,7 +2688,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        if (!lq_sta->search_better_tbl)
                active_tbl = lq_sta->active_tbl;
        else
-               active_tbl = 1 - lq_sta->active_tbl;
+               active_tbl = rs_search_tbl(lq_sta->active_tbl);
 
        tbl = &(lq_sta->lq_info[active_tbl]);
        rate = &tbl->rate;
@@ -2926,7 +2706,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        rs_set_expected_tpt_table(lq_sta, tbl);
        rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
        /* TODO restore station should remember the lq cmd */
-       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, !update);
+       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq);
 }
 
 static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta,
@@ -3175,7 +2955,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg)
  * Called after adding a new station to initialize rate scaling
  */
 static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
-                            enum nl80211_band band, bool update)
+                            enum nl80211_band band)
 {
        int i, j;
        struct ieee80211_hw *hw = mvm->hw;
@@ -3186,6 +2966,8 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
        struct ieee80211_supported_band *sband;
        unsigned long supp; /* must be unsigned long for for_each_set_bit */
 
+       lockdep_assert_held(&mvmsta->lq_sta.rs_drv.pers.lock);
+
        /* clear all non-persistent lq data */
        memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
 
@@ -3255,7 +3037,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_reset_frame_stats(mvm);
 #endif
-       rs_initialize_lq(mvm, sta, lq_sta, band, update);
+       rs_initialize_lq(mvm, sta, lq_sta, band);
 }
 
 static void rs_drv_rate_update(void *mvm_r,
@@ -3278,6 +3060,258 @@ static void rs_drv_rate_update(void *mvm_r,
        iwl_mvm_rs_rate_init(mvm, sta, sband->band, true);
 }
 
+static void __iwl_mvm_rs_tx_status(struct iwl_mvm *mvm,
+                                  struct ieee80211_sta *sta,
+                                  int tid, struct ieee80211_tx_info *info,
+                                  bool ndp)
+{
+       int legacy_success;
+       int retries;
+       int i;
+       struct iwl_lq_cmd *table;
+       u32 lq_hwrate;
+       struct rs_rate lq_rate, tx_resp_rate;
+       struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+       u32 tlc_info = (uintptr_t)info->status.status_driver_data[0];
+       u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK;
+       u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info);
+       u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1];
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv;
+
+       /* Treat uninitialized rate scaling data same as non-existing. */
+       if (!lq_sta) {
+               IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
+               return;
+       } else if (!lq_sta->pers.drv) {
+               IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+               return;
+       }
+
+       /* This packet was aggregated but doesn't carry status info */
+       if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+           !(info->flags & IEEE80211_TX_STAT_AMPDU))
+               return;
+
+       if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band,
+                                   &tx_resp_rate)) {
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+       /* Disable last tx check if we are debugging with fixed rate but
+        * update tx stats
+        */
+       if (lq_sta->pers.dbg_fixed_rate) {
+               int index = tx_resp_rate.index;
+               enum rs_column column;
+               int attempts, success;
+
+               column = rs_get_column_from_rate(&tx_resp_rate);
+               if (WARN_ONCE(column == RS_COLUMN_INVALID,
+                             "Can't map rate 0x%x to column",
+                             tx_resp_hwrate))
+                       return;
+
+               if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+                       attempts = info->status.ampdu_len;
+                       success = info->status.ampdu_ack_len;
+               } else {
+                       attempts = info->status.rates[0].count;
+                       success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+               }
+
+               lq_sta->pers.tx_stats[column][index].total += attempts;
+               lq_sta->pers.tx_stats[column][index].success += success;
+
+               IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
+                              tx_resp_hwrate, success, attempts);
+               return;
+       }
+#endif
+
+       if (time_after(jiffies,
+                      (unsigned long)(lq_sta->last_tx +
+                                      (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) {
+               IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
+               /* reach here only in case of driver RS, call directly
+                * the unlocked version
+                */
+               rs_drv_rate_init(mvm, sta, info->band);
+               return;
+       }
+       lq_sta->last_tx = jiffies;
+
+       /* Ignore this Tx frame response if its initial rate doesn't match
+        * that of latest Link Quality command.  There may be stragglers
+        * from a previous Link Quality command, but we're no longer interested
+        * in those; they're either from the "active" mode while we're trying
+        * 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;
+       lq_hwrate = le32_to_cpu(table->rs_table[0]);
+       if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) {
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       /* Here we actually compare this rate to the latest LQ command */
+       if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) {
+               IWL_DEBUG_RATE(mvm,
+                              "tx resp color 0x%x does not match 0x%x\n",
+                              lq_color, LQ_FLAG_COLOR_GET(table->flags));
+
+               /* 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_MVM_RS_MISSED_RATE_MAX) {
+                       lq_sta->missed_rate_counter = 0;
+                       IWL_DEBUG_RATE(mvm,
+                                      "Too many rates mismatch. Send sync LQ. rs_state %d\n",
+                                      lq_sta->rs_state);
+                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq);
+               }
+               /* Regardless, ignore this status info for outdated rate */
+               return;
+       }
+
+       /* Rate did match, so reset the missed_rate_counter */
+       lq_sta->missed_rate_counter = 0;
+
+       if (!lq_sta->search_better_tbl) {
+               curr_tbl = &lq_sta->lq_info[lq_sta->active_tbl];
+               other_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
+       } else {
+               curr_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
+               other_tbl = &lq_sta->lq_info[lq_sta->active_tbl];
+       }
+
+       if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) {
+               IWL_DEBUG_RATE(mvm,
+                              "Neither active nor search matches tx rate\n");
+               tmp_tbl = &lq_sta->lq_info[lq_sta->active_tbl];
+               rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE");
+               tmp_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)];
+               rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH");
+               rs_dump_rate(mvm, &lq_rate, "ACTUAL");
+
+               /* no matching table found, let's by-pass the data collection
+                * and continue to perform rate scale to find the rate table
+                */
+               rs_stay_in_table(lq_sta, true);
+               goto done;
+       }
+
+       /* 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) {
+               rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index,
+                                   info->status.ampdu_len,
+                                   info->status.ampdu_ack_len,
+                                   reduced_txp);
+
+               /* ampdu_ack_len = 0 marks no BA was received. For TLC, treat
+                * it as a single frame loss as we don't want the success ratio
+                * to dip too quickly because a BA wasn't received.
+                * For TPC, there's no need for this optimisation since we want
+                * to recover very quickly from a bad power reduction and,
+                * therefore we'd like the success ratio to get an immediate hit
+                * when failing to get a BA, so we'd switch back to a lower or
+                * zero power reduction. When FW transmits agg with a rate
+                * different from the initial rate, it will not use reduced txp
+                * and will send BA notification twice (one empty with reduced
+                * txp equal to the value from LQ and one with reduced txp 0).
+                * We need to update counters for each txp level accordingly.
+                */
+               if (info->status.ampdu_ack_len == 0)
+                       info->status.ampdu_len = 1;
+
+               rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl,
+                                   tx_resp_rate.index,
+                                   info->status.ampdu_len,
+                                   info->status.ampdu_ack_len);
+
+               /* Update success/fail counts if not searching for new mode */
+               if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
+                       lq_sta->total_success += info->status.ampdu_ack_len;
+                       lq_sta->total_failed += (info->status.ampdu_len -
+                                       info->status.ampdu_ack_len);
+               }
+       } else {
+               /* For legacy, update frame history with for each Tx retry. */
+               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) {
+                       lq_hwrate = le32_to_cpu(table->rs_table[i]);
+                       if (rs_rate_from_ucode_rate(lq_hwrate, info->band,
+                                                   &lq_rate)) {
+                               WARN_ON_ONCE(1);
+                               return;
+                       }
+
+                       /* Only collect stats if retried rate is in the same RS
+                        * table as active/search.
+                        */
+                       if (rs_rate_column_match(&lq_rate, &curr_tbl->rate))
+                               tmp_tbl = curr_tbl;
+                       else if (rs_rate_column_match(&lq_rate,
+                                                     &other_tbl->rate))
+                               tmp_tbl = other_tbl;
+                       else
+                               continue;
+
+                       rs_collect_tpc_data(mvm, lq_sta, tmp_tbl,
+                                           tx_resp_rate.index, 1,
+                                           i < retries ? 0 : legacy_success,
+                                           reduced_txp);
+                       rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl,
+                                           tx_resp_rate.index, 1,
+                                           i < retries ? 0 : legacy_success);
+               }
+
+               /* Update success/fail counts if not searching for new mode */
+               if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
+                       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 = lq_hwrate;
+       IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
+done:
+       /* See if there's a better rate or modulation mode to try. */
+       if (sta->supp_rates[info->band])
+               rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp);
+}
+
+void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                         int tid, struct ieee80211_tx_info *info, bool ndp)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+       /* If it's locked we are in middle of init flow
+        * just wait for next tx status to update the lq_sta data
+        */
+       if (!spin_trylock(&mvmsta->lq_sta.rs_drv.pers.lock))
+               return;
+
+       __iwl_mvm_rs_tx_status(mvm, sta, tid, info, ndp);
+       spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock);
+}
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
                                            struct iwl_lq_cmd *lq_cmd,
@@ -3569,7 +3603,7 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
 
                bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED;
                bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params);
-               iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false);
+               iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd);
 
                ss_params |= LQ_SS_BFER_ALLOWED;
                IWL_DEBUG_RATE(mvm,
@@ -3735,7 +3769,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
 
        if (lq_sta->pers.dbg_fixed_rate) {
                rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL);
-               iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
+               iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq);
        }
 }
 
@@ -4132,10 +4166,15 @@ static const struct rate_control_ops rs_mvm_ops_drv = {
 void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                          enum nl80211_band band, bool update)
 {
-       if (iwl_mvm_has_tlc_offload(mvm))
+       if (iwl_mvm_has_tlc_offload(mvm)) {
                rs_fw_rate_init(mvm, sta, band, update);
-       else
-               rs_drv_rate_init(mvm, sta, band, update);
+       } else {
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+               spin_lock(&mvmsta->lq_sta.rs_drv.pers.lock);
+               rs_drv_rate_init(mvm, sta, band);
+               spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock);
+       }
 }
 
 int iwl_mvm_rate_control_register(void)
@@ -4165,7 +4204,7 @@ static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                        lq->flags &= ~LQ_FLAG_USE_RTS_MSK;
        }
 
-       return iwl_mvm_send_lq_cmd(mvm, lq, false);
+       return iwl_mvm_send_lq_cmd(mvm, lq);
 }
 
 /**