diff mbox series

[v3,1/2] mac80211: extend current rate control tx status API

Message ID 20220508151056.2431775-2-jelonek.jonas@gmail.com (mailing list archive)
State Superseded
Delegated to: Johannes Berg
Headers show
Series mac80211: extend current rate control tx status API | expand

Commit Message

Jonas Jelonek May 8, 2022, 3:10 p.m. UTC
This patch adds the new struct ieee80211_rate_status and replaces
'struct rate_info *rate' in ieee80211_tx_status with pointer and length
annotation.

The struct ieee80211_rate_status allows to:
(1)	receive tx power status feedback for transmit power control (TPC)
	per packet or packet retry
(2)	dynamic mapping of wifi chip specific multi-rate retry (mrr)
	chains with different lengths
(3)	increase the limit of annotatable rate indices to support
	IEEE802.11ac rate sets and beyond

ieee80211_tx_info, control and status buffer, and ieee80211_tx_rate
cannot be used to achieve these goals due to fixed size limitations.

Our new struct contains a struct rate_info to annotate the rate that was
used, retry count of the rate and tx power. It is intended for all
information related to RC and TPC that needs to be passed from driver to
mac80211 and its RC/TPC algorithms like Minstrel_HT. It corresponds to
one stage in an mrr. Multiple subsequent instances of this struct can be
included in struct ieee80211_tx_status via a pointer and a length variable.
Those instances can be allocated on-stack. The former reference to a single
instance of struct rate_info is replaced with our new annotation.

An extension is introduced to struct ieee80211_hw. There are two new
members called 'tx_power_levels' and 'max_txpwr_levels_idx' acting as a
tx power level table. When a wifi device is registered, the driver shall
supply all supported power levels in this list. This allows to support
several quirks like differing power steps in power level ranges or
alike. TPC can use this for algorithm and thus be designed more abstract
instead of handling all possible step widths individually.

Further mandatory changes in status.c and mt76 driver due to the
removal of 'struct rate_info *rate' are also included.
status.c already uses the information in ieee80211_tx_status->rate in
radiotap, this is now changed to use ieee80211_rate_status->rate_idx.
mt76 driver already uses struct rate_info to pass the tx rate to status
path. The new members of the ieee80211_tx_status are set to NULL and 0
because the previously passed rate is not relevant to rate control and
accurate information is passed via tx_info->status.rates.

Compile-Tested: current wireless-next tree with all flags on
Tested-on: Xiaomi 4A Gigabit (MediaTek MT7603E, MT7612E) with OpenWrt
		Linux 5.10.83

Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
---
 drivers/net/wireless/mediatek/mt76/tx.c |  5 +-
 include/net/mac80211.h                  | 33 ++++++++-
 net/mac80211/status.c                   | 91 ++++++++++++++-----------
 3 files changed, 85 insertions(+), 44 deletions(-)

Comments

kernel test robot May 8, 2022, 5:58 p.m. UTC | #1
Hi Jonas,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on wireless/main]
[also build test ERROR on v5.18-rc5]
[cannot apply to wireless-next/main next-20220506]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Jonas-Jelonek/mac80211-extend-current-rate-control-tx-status-API/20220508-231356
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git main
config: i386-randconfig-a005 (https://download.01.org/0day-ci/archive/20220509/202205090141.egLK4ZdI-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.2.0-20) 11.2.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/4241dcd99dd1ea39739668850806abcd249f8535
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Jonas-Jelonek/mac80211-extend-current-rate-control-tx-status-API/20220508-231356
        git checkout 4241dcd99dd1ea39739668850806abcd249f8535
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/net/wireless/ath/ath11k/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/net/wireless/ath/ath11k/dp_tx.c: In function 'ath11k_dp_tx_complete_msdu':
>> drivers/net/wireless/ath/ath11k/dp_tx.c:606:16: error: 'struct ieee80211_tx_status' has no member named 'rate'; did you mean 'rates'?
     606 |         status.rate = &rate;
         |                ^~~~
         |                rates


vim +606 drivers/net/wireless/ath/ath11k/dp_tx.c

1b8bb94c0612cf Wen Gong               2021-12-20  517  
d5c65159f28953 Kalle Valo             2019-11-23  518  static void ath11k_dp_tx_complete_msdu(struct ath11k *ar,
d5c65159f28953 Kalle Valo             2019-11-23  519  				       struct sk_buff *msdu,
d5c65159f28953 Kalle Valo             2019-11-23  520  				       struct hal_tx_status *ts)
d5c65159f28953 Kalle Valo             2019-11-23  521  {
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  522  	struct ieee80211_tx_status status = { 0 };
d5c65159f28953 Kalle Valo             2019-11-23  523  	struct ath11k_base *ab = ar->ab;
d5c65159f28953 Kalle Valo             2019-11-23  524  	struct ieee80211_tx_info *info;
d5c65159f28953 Kalle Valo             2019-11-23  525  	struct ath11k_skb_cb *skb_cb;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  526  	struct ath11k_peer *peer;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  527  	struct ath11k_sta *arsta;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  528  	struct rate_info rate;
d5c65159f28953 Kalle Valo             2019-11-23  529  
d5c65159f28953 Kalle Valo             2019-11-23  530  	if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) {
d5c65159f28953 Kalle Valo             2019-11-23  531  		/* Must not happen */
d5c65159f28953 Kalle Valo             2019-11-23  532  		return;
d5c65159f28953 Kalle Valo             2019-11-23  533  	}
d5c65159f28953 Kalle Valo             2019-11-23  534  
d5c65159f28953 Kalle Valo             2019-11-23  535  	skb_cb = ATH11K_SKB_CB(msdu);
d5c65159f28953 Kalle Valo             2019-11-23  536  
d5c65159f28953 Kalle Valo             2019-11-23  537  	dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
d5c65159f28953 Kalle Valo             2019-11-23  538  
bcef57ea400cc2 P Praneesh             2021-11-12  539  	if (unlikely(!rcu_access_pointer(ab->pdevs_active[ar->pdev_idx]))) {
d5c65159f28953 Kalle Valo             2019-11-23  540  		dev_kfree_skb_any(msdu);
bcef57ea400cc2 P Praneesh             2021-11-12  541  		return;
d5c65159f28953 Kalle Valo             2019-11-23  542  	}
d5c65159f28953 Kalle Valo             2019-11-23  543  
bcef57ea400cc2 P Praneesh             2021-11-12  544  	if (unlikely(!skb_cb->vif)) {
d5c65159f28953 Kalle Valo             2019-11-23  545  		dev_kfree_skb_any(msdu);
bcef57ea400cc2 P Praneesh             2021-11-12  546  		return;
d5c65159f28953 Kalle Valo             2019-11-23  547  	}
d5c65159f28953 Kalle Valo             2019-11-23  548  
d5c65159f28953 Kalle Valo             2019-11-23  549  	info = IEEE80211_SKB_CB(msdu);
d5c65159f28953 Kalle Valo             2019-11-23  550  	memset(&info->status, 0, sizeof(info->status));
d5c65159f28953 Kalle Valo             2019-11-23  551  
d5c65159f28953 Kalle Valo             2019-11-23  552  	/* skip tx rate update from ieee80211_status*/
d5c65159f28953 Kalle Valo             2019-11-23  553  	info->status.rates[0].idx = -1;
d5c65159f28953 Kalle Valo             2019-11-23  554  
d5c65159f28953 Kalle Valo             2019-11-23  555  	if (ts->status == HAL_WBM_TQM_REL_REASON_FRAME_ACKED &&
d5c65159f28953 Kalle Valo             2019-11-23  556  	    !(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
d5c65159f28953 Kalle Valo             2019-11-23  557  		info->flags |= IEEE80211_TX_STAT_ACK;
d5c65159f28953 Kalle Valo             2019-11-23  558  		info->status.ack_signal = ATH11K_DEFAULT_NOISE_FLOOR +
d5c65159f28953 Kalle Valo             2019-11-23  559  					  ts->ack_rssi;
ea5907db2a9ccf Avraham Stern          2022-02-02  560  		info->status.flags |= IEEE80211_TX_STATUS_ACK_SIGNAL_VALID;
d5c65159f28953 Kalle Valo             2019-11-23  561  	}
d5c65159f28953 Kalle Valo             2019-11-23  562  
d5c65159f28953 Kalle Valo             2019-11-23  563  	if (ts->status == HAL_WBM_TQM_REL_REASON_CMD_REMOVE_TX &&
d5c65159f28953 Kalle Valo             2019-11-23  564  	    (info->flags & IEEE80211_TX_CTL_NO_ACK))
d5c65159f28953 Kalle Valo             2019-11-23  565  		info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
d5c65159f28953 Kalle Valo             2019-11-23  566  
1b8bb94c0612cf Wen Gong               2021-12-20  567  	if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar)) ||
1b8bb94c0612cf Wen Gong               2021-12-20  568  	    ab->hw_params.single_pdev_only) {
d5c65159f28953 Kalle Valo             2019-11-23  569  		if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) {
d5c65159f28953 Kalle Valo             2019-11-23  570  			if (ar->last_ppdu_id == 0) {
d5c65159f28953 Kalle Valo             2019-11-23  571  				ar->last_ppdu_id = ts->ppdu_id;
d5c65159f28953 Kalle Valo             2019-11-23  572  			} else if (ar->last_ppdu_id == ts->ppdu_id ||
d5c65159f28953 Kalle Valo             2019-11-23  573  				   ar->cached_ppdu_id == ar->last_ppdu_id) {
d5c65159f28953 Kalle Valo             2019-11-23  574  				ar->cached_ppdu_id = ar->last_ppdu_id;
d5c65159f28953 Kalle Valo             2019-11-23  575  				ar->cached_stats.is_ampdu = true;
1b8bb94c0612cf Wen Gong               2021-12-20  576  				ath11k_dp_tx_update_txcompl(ar, ts);
d5c65159f28953 Kalle Valo             2019-11-23  577  				memset(&ar->cached_stats, 0,
d5c65159f28953 Kalle Valo             2019-11-23  578  				       sizeof(struct ath11k_per_peer_tx_stats));
d5c65159f28953 Kalle Valo             2019-11-23  579  			} else {
d5c65159f28953 Kalle Valo             2019-11-23  580  				ar->cached_stats.is_ampdu = false;
1b8bb94c0612cf Wen Gong               2021-12-20  581  				ath11k_dp_tx_update_txcompl(ar, ts);
d5c65159f28953 Kalle Valo             2019-11-23  582  				memset(&ar->cached_stats, 0,
d5c65159f28953 Kalle Valo             2019-11-23  583  				       sizeof(struct ath11k_per_peer_tx_stats));
d5c65159f28953 Kalle Valo             2019-11-23  584  			}
d5c65159f28953 Kalle Valo             2019-11-23  585  			ar->last_ppdu_id = ts->ppdu_id;
d5c65159f28953 Kalle Valo             2019-11-23  586  		}
d5c65159f28953 Kalle Valo             2019-11-23  587  
d5c65159f28953 Kalle Valo             2019-11-23  588  		ath11k_dp_tx_cache_peer_stats(ar, msdu, ts);
d5c65159f28953 Kalle Valo             2019-11-23  589  	}
d5c65159f28953 Kalle Valo             2019-11-23  590  
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  591  	spin_lock_bh(&ab->base_lock);
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  592  	peer = ath11k_peer_find_by_id(ab, ts->peer_id);
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  593  	if (!peer || !peer->sta) {
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  594  		ath11k_dbg(ab, ATH11K_DBG_DATA,
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  595  			   "dp_tx: failed to find the peer with peer_id %d\n",
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  596  			    ts->peer_id);
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  597  		spin_unlock_bh(&ab->base_lock);
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  598  		dev_kfree_skb_any(msdu);
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  599  		return;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  600  	}
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  601  	arsta = (struct ath11k_sta *)peer->sta->drv_priv;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  602  	status.sta = peer->sta;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  603  	status.skb = msdu;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  604  	status.info = info;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  605  	rate = arsta->last_txrate;
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16 @606  	status.rate = &rate;
d5c65159f28953 Kalle Valo             2019-11-23  607  
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  608  	spin_unlock_bh(&ab->base_lock);
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  609  
94739d45c388c5 Pradeep Kumar Chitrapu 2022-02-16  610  	ieee80211_tx_status_ext(ar->hw, &status);
d5c65159f28953 Kalle Valo             2019-11-23  611  }
d5c65159f28953 Kalle Valo             2019-11-23  612
diff mbox series

Patch

diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 6b8c9dc80542..b49ef6619829 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -66,9 +66,8 @@  mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
 		wcid = rcu_dereference(dev->wcid[cb->wcid]);
 		if (wcid) {
 			status.sta = wcid_to_sta(wcid);
-
-			if (status.sta)
-				status.rate = &wcid->rate;
+			status.rates = NULL;
+			status.n_rates = 0;
 		}
 
 		hw = mt76_tx_status_get_hw(dev, skb);
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index c50221d7e82c..54455c8249c2 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1131,20 +1131,41 @@  ieee80211_info_get_tx_time_est(struct ieee80211_tx_info *info)
 	return info->tx_time_est << 2;
 }
 
+/***
+ * struct ieee80211_rate_status - mrr stage for status path
+ *
+ * This struct is used in struct ieee80211_tx_status to provide drivers a
+ * dynamic way to report about used rates and power levels per packet.
+ *
+ * @rate_idx The actual used rate.
+ * @try_count How often the rate was tried.
+ * @tx_power_idx An idx into the ieee80211_hw->tx_power_levels list of the
+ * 	corresponding wifi hardware. The idx shall point to the power level 
+ * 	that was used when sending the packet.
+ */
+struct ieee80211_rate_status {
+	struct rate_info rate_idx;
+	u8 try_count;
+	u8 tx_power_idx;
+};
+
 /**
  * struct ieee80211_tx_status - extended tx status info for rate control
  *
  * @sta: Station that the packet was transmitted for
  * @info: Basic tx status information
  * @skb: Packet skb (can be NULL if not provided by the driver)
- * @rate: The TX rate that was used when sending the packet
+ * @rates: Mrr stages that were used when sending the packet
+ * @n_rates: Number of mrr stages (count of instances for @rates)
  * @free_list: list where processed skbs are stored to be free'd by the driver
  */
 struct ieee80211_tx_status {
 	struct ieee80211_sta *sta;
 	struct ieee80211_tx_info *info;
 	struct sk_buff *skb;
-	struct rate_info *rate;
+	struct ieee80211_rate_status *rates;
+	u8 n_rates;
+
 	struct list_head *free_list;
 };
 
@@ -2601,6 +2622,12 @@  enum ieee80211_hw_flags {
  *	refilling deficit of each TXQ.
  *
  * @max_mtu: the max mtu could be set.
+ *
+ * @tx_power_levels: a list of power levels supported by the wifi hardware.
+ * 	The power levels can be specified either as integer or fractions.
+ * 	The power level at idx 0 shall be the maximum positive power level.
+ *
+ * @max_txpwr_levels_idx: the maximum valid idx of 'tx_power_levels' list.
  */
 struct ieee80211_hw {
 	struct ieee80211_conf conf;
@@ -2639,6 +2666,8 @@  struct ieee80211_hw {
 	u8 tx_sk_pacing_shift;
 	u8 weight_multiplier;
 	u32 max_mtu;
+	const s8 *tx_power_levels;
+	u8 max_txpwr_levels_idx;
 };
 
 static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index f6f63a0b1b72..fc6f88ab648b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -246,15 +246,19 @@  static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn)
 static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info,
 				     struct ieee80211_tx_status *status)
 {
+	struct ieee80211_rate_status *status_rate = NULL;
 	int len = sizeof(struct ieee80211_radiotap_header);
 
+	if (status && status->n_rates)
+		status_rate = &status->rates[status->n_rates - 1];
+
 	/* IEEE80211_RADIOTAP_RATE rate */
-	if (status && status->rate && !(status->rate->flags &
-					(RATE_INFO_FLAGS_MCS |
-					 RATE_INFO_FLAGS_DMG |
-					 RATE_INFO_FLAGS_EDMG |
-					 RATE_INFO_FLAGS_VHT_MCS |
-					 RATE_INFO_FLAGS_HE_MCS)))
+	if (status_rate && !(status_rate->rate_idx.flags &
+						(RATE_INFO_FLAGS_MCS |
+						 RATE_INFO_FLAGS_DMG |
+						 RATE_INFO_FLAGS_EDMG |
+						 RATE_INFO_FLAGS_VHT_MCS |
+						 RATE_INFO_FLAGS_HE_MCS)))
 		len += 2;
 	else if (info->status.rates[0].idx >= 0 &&
 		 !(info->status.rates[0].flags &
@@ -269,12 +273,12 @@  static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info,
 
 	/* IEEE80211_RADIOTAP_MCS
 	 * IEEE80211_RADIOTAP_VHT */
-	if (status && status->rate) {
-		if (status->rate->flags & RATE_INFO_FLAGS_MCS)
+	if (status_rate) {
+		if (status_rate->rate_idx.flags & RATE_INFO_FLAGS_MCS)
 			len += 3;
-		else if (status->rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+		else if (status_rate->rate_idx.flags & RATE_INFO_FLAGS_VHT_MCS)
 			len = ALIGN(len, 2) + 12;
-		else if (status->rate->flags & RATE_INFO_FLAGS_HE_MCS)
+		else if (status_rate->rate_idx.flags & RATE_INFO_FLAGS_HE_MCS)
 			len = ALIGN(len, 2) + 12;
 	} else if (info->status.rates[0].idx >= 0) {
 		if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
@@ -296,10 +300,14 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_radiotap_header *rthdr;
+	struct ieee80211_rate_status *status_rate = NULL;
 	unsigned char *pos;
 	u16 legacy_rate = 0;
 	u16 txflags;
 
+	if (status && status->n_rates)
+		status_rate = &status->rates[status->n_rates - 1];
+
 	rthdr = skb_push(skb, rtap_len);
 
 	memset(rthdr, 0, rtap_len);
@@ -317,13 +325,14 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 
 	/* IEEE80211_RADIOTAP_RATE */
 
-	if (status && status->rate) {
-		if (!(status->rate->flags & (RATE_INFO_FLAGS_MCS |
-					     RATE_INFO_FLAGS_DMG |
-					     RATE_INFO_FLAGS_EDMG |
-					     RATE_INFO_FLAGS_VHT_MCS |
-					     RATE_INFO_FLAGS_HE_MCS)))
-			legacy_rate = status->rate->legacy;
+	if (status_rate) {
+		if (!(status_rate->rate_idx.flags &
+						(RATE_INFO_FLAGS_MCS |
+						 RATE_INFO_FLAGS_DMG |
+						 RATE_INFO_FLAGS_EDMG |
+						 RATE_INFO_FLAGS_VHT_MCS |
+						 RATE_INFO_FLAGS_HE_MCS)))
+			legacy_rate = status_rate->rate_idx.legacy;
 	} else if (info->status.rates[0].idx >= 0 &&
 		 !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
 						  IEEE80211_TX_RC_VHT_MCS)))
@@ -356,20 +365,21 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 	*pos = retry_count;
 	pos++;
 
-	if (status && status->rate &&
-	    (status->rate->flags & RATE_INFO_FLAGS_MCS)) {
+	if (status_rate && (status_rate->rate_idx.flags & RATE_INFO_FLAGS_MCS))
+	{
 		rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_MCS));
 		pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
 			 IEEE80211_RADIOTAP_MCS_HAVE_GI |
 			 IEEE80211_RADIOTAP_MCS_HAVE_BW;
-		if (status->rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+		if (status_rate->rate_idx.flags & RATE_INFO_FLAGS_SHORT_GI)
 			pos[1] |= IEEE80211_RADIOTAP_MCS_SGI;
-		if (status->rate->bw == RATE_INFO_BW_40)
+		if (status_rate->rate_idx.bw == RATE_INFO_BW_40)
 			pos[1] |= IEEE80211_RADIOTAP_MCS_BW_40;
-		pos[2] = status->rate->mcs;
+		pos[2] = status_rate->rate_idx.mcs;
 		pos += 3;
-	} else if (status && status->rate &&
-		   (status->rate->flags & RATE_INFO_FLAGS_VHT_MCS)) {
+	} else if (status_rate && (status_rate->rate_idx.flags &
+					RATE_INFO_FLAGS_VHT_MCS))
+	{
 		u16 known = local->hw.radiotap_vht_details &
 			(IEEE80211_RADIOTAP_VHT_KNOWN_GI |
 			 IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
@@ -384,12 +394,12 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 		pos += 2;
 
 		/* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
-		if (status->rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+		if (status_rate->rate_idx.flags & RATE_INFO_FLAGS_SHORT_GI)
 			*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
 		pos++;
 
 		/* u8 bandwidth */
-		switch (status->rate->bw) {
+		switch (status_rate->rate_idx.bw) {
 		case RATE_INFO_BW_160:
 			*pos = 11;
 			break;
@@ -406,7 +416,8 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 		pos++;
 
 		/* u8 mcs_nss[4] */
-		*pos = (status->rate->mcs << 4) | status->rate->nss;
+		*pos = (status_rate->rate_idx.mcs << 4) |
+				status_rate->rate_idx.nss;
 		pos += 4;
 
 		/* u8 coding */
@@ -415,8 +426,9 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 		pos++;
 		/* u16 partial_aid */
 		pos += 2;
-	} else if (status && status->rate &&
-		   (status->rate->flags & RATE_INFO_FLAGS_HE_MCS)) {
+	} else if (status_rate && (status_rate->rate_idx.flags &
+					RATE_INFO_FLAGS_HE_MCS))
+	{
 		struct ieee80211_radiotap_he *he;
 
 		rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_HE));
@@ -434,7 +446,7 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 
 #define HE_PREP(f, val) le16_encode_bits(val, IEEE80211_RADIOTAP_HE_##f)
 
-		he->data6 |= HE_PREP(DATA6_NSTS, status->rate->nss);
+		he->data6 |= HE_PREP(DATA6_NSTS, status_rate->rate_idx.nss);
 
 #define CHECK_GI(s) \
 	BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_GI_##s != \
@@ -444,12 +456,12 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 		CHECK_GI(1_6);
 		CHECK_GI(3_2);
 
-		he->data3 |= HE_PREP(DATA3_DATA_MCS, status->rate->mcs);
-		he->data3 |= HE_PREP(DATA3_DATA_DCM, status->rate->he_dcm);
+		he->data3 |= HE_PREP(DATA3_DATA_MCS, status_rate->rate_idx.mcs);
+		he->data3 |= HE_PREP(DATA3_DATA_DCM, status_rate->rate_idx.he_dcm);
 
-		he->data5 |= HE_PREP(DATA5_GI, status->rate->he_gi);
+		he->data5 |= HE_PREP(DATA5_GI, status_rate->rate_idx.he_gi);
 
-		switch (status->rate->bw) {
+		switch (status_rate->rate_idx.bw) {
 		case RATE_INFO_BW_20:
 			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
 					     IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
@@ -480,16 +492,16 @@  ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 			CHECK_RU_ALLOC(2x996);
 
 			he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
-					     status->rate->he_ru_alloc + 4);
+					     status_rate->rate_idx.he_ru_alloc + 4);
 			break;
 		default:
-			WARN_ONCE(1, "Invalid SU BW %d\n", status->rate->bw);
+			WARN_ONCE(1, "Invalid SU BW %d\n", status_rate->rate_idx.bw);
 		}
 
 		pos += sizeof(struct ieee80211_radiotap_he);
 	}
 
-	if ((status && status->rate) || info->status.rates[0].idx < 0)
+	if (status_rate || info->status.rates[0].idx < 0)
 		return;
 
 	/* IEEE80211_RADIOTAP_MCS
@@ -1108,8 +1120,9 @@  void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
 	if (pubsta) {
 		sta = container_of(pubsta, struct sta_info, sta);
 
-		if (status->rate)
-			sta->tx_stats.last_rate_info = *status->rate;
+		if (status->rates)
+			sta->tx_stats.last_rate_info =
+				status->rates[status->n_rates - 1].rate_idx;
 	}
 
 	if (skb && (tx_time_est =