Commit 1b8bb94c authored by Wen Gong's avatar Wen Gong Committed by Kalle Valo
Browse files

ath11k: report tx bitrate for iw wlan station dump



HTT_T2H_MSG_TYPE_PPDU_STATS_IND is a message which include the ppdu
info, currently it is not report from firmware for ath11k, then the
tx bitrate of "iw wlan0 station dump" always show an invalid value
"tx bitrate: 6.0 MBit/s".

To address the issue, this is to parse the info of tx complete report
from firmware and indicate the tx rate to mac80211.

After that, "iw wlan0 station dump" show the correct tx bit rate such
as:
tx bitrate: 78.0 MBit/s MCS 12
tx bitrate: 144.4 MBit/s VHT-MCS 7 short GI VHT-NSS 2
tx bitrate: 286.7 MBit/s HE-MCS 11 HE-NSS 2 HE-GI 0 HE-DCM 0
tx bitrate: 1921.5 MBit/s 160MHz HE-MCS 9 HE-NSS 2 HE-GI 0 HE-DCM 0

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1

Signed-off-by: default avatarWen Gong <quic_wgong@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20211217093722.5739-1-quic_wgong@quicinc.com
parent 6ce708f5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -382,6 +382,7 @@ struct ath11k_sta {
	struct work_struct update_wk;
	struct work_struct set_4addr_wk;
	struct rate_info txrate;
	u32 peer_nss;
	struct rate_info last_txrate;
	u64 rx_duration;
	u64 tx_duration;
+1 −77
Original line number Diff line number Diff line
@@ -126,85 +126,9 @@ void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta,
}

void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar,
				       struct sk_buff *msdu,
				       struct hal_tx_status *ts)
{
	struct ath11k_base *ab = ar->ab;
	struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats;
	enum hal_tx_rate_stats_pkt_type pkt_type;
	enum hal_tx_rate_stats_sgi sgi;
	enum hal_tx_rate_stats_bw bw;
	struct ath11k_peer *peer;
	struct ath11k_sta *arsta;
	struct ieee80211_sta *sta;
	u16 rate;
	u8 rate_idx = 0;
	int ret;
	u8 mcs;

	rcu_read_lock();
	spin_lock_bh(&ab->base_lock);
	peer = ath11k_peer_find_by_id(ab, ts->peer_id);
	if (!peer || !peer->sta) {
		ath11k_warn(ab, "failed to find the peer\n");
		spin_unlock_bh(&ab->base_lock);
		rcu_read_unlock();
		return;
	}

	sta = peer->sta;
	arsta = (struct ath11k_sta *)sta->drv_priv;

	memset(&arsta->txrate, 0, sizeof(arsta->txrate));
	pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE,
			     ts->rate_stats);
	mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS,
			ts->rate_stats);
	sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI,
			ts->rate_stats);
	bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats);

	if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A ||
	    pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) {
		ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs,
							    pkt_type,
							    &rate_idx,
							    &rate);
		if (ret < 0)
			goto err_out;
		arsta->txrate.legacy = rate;
	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) {
		if (mcs > 7) {
			ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs);
			goto err_out;
		}

		arsta->txrate.mcs = mcs + 8 * (arsta->last_txrate.nss - 1);
		arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
		if (sgi)
			arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) {
		if (mcs > 9) {
			ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs);
			goto err_out;
		}

		arsta->txrate.mcs = mcs;
		arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
		if (sgi)
			arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) {
		/* TODO */
	}

	arsta->txrate.nss = arsta->last_txrate.nss;
	arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);

	ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);

err_out:
	spin_unlock_bh(&ab->base_lock);
	rcu_read_unlock();
	ath11k_dp_tx_update_txcompl(ar, ts);
}

static ssize_t ath11k_dbg_sta_dump_tx_stats(struct file *file,
+0 −2
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta,
				     struct ath11k_per_peer_tx_stats *peer_stats,
				     u8 legacy_rate_idx);
void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar,
				       struct sk_buff *msdu,
				       struct hal_tx_status *ts);

#else /* CONFIG_ATH11K_DEBUGFS */
@@ -34,7 +33,6 @@ ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta,
}

static inline void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar,
						     struct sk_buff *msdu,
						     struct hal_tx_status *ts)
{
}
+5 −23
Original line number Diff line number Diff line
@@ -1360,25 +1360,6 @@ int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len,
	return 0;
}

static inline u32 ath11k_he_gi_to_nl80211_he_gi(u8 sgi)
{
	u32 ret = 0;

	switch (sgi) {
	case RX_MSDU_START_SGI_0_8_US:
		ret = NL80211_RATE_INFO_HE_GI_0_8;
		break;
	case RX_MSDU_START_SGI_1_6_US:
		ret = NL80211_RATE_INFO_HE_GI_1_6;
		break;
	case RX_MSDU_START_SGI_3_2_US:
		ret = NL80211_RATE_INFO_HE_GI_3_2;
		break;
	}

	return ret;
}

static void
ath11k_update_per_peer_tx_stats(struct ath11k *ar,
				struct htt_ppdu_stats *ppdu_stats, u8 user)
@@ -1497,14 +1478,15 @@ ath11k_update_per_peer_tx_stats(struct ath11k *ar,
		arsta->txrate.mcs = mcs;
		arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
		arsta->txrate.he_dcm = dcm;
		arsta->txrate.he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi);
		arsta->txrate.he_ru_alloc = ath11k_he_ru_tones_to_nl80211_he_ru_alloc(
						(user_rate->ru_end -
		arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi);
		arsta->txrate.he_ru_alloc = ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc
						((user_rate->ru_end -
						 user_rate->ru_start) + 1);
		break;
	}

	arsta->txrate.nss = nss;

	arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);
	arsta->tx_duration += tx_duration;
	memcpy(&arsta->last_txrate, &arsta->txrate, sizeof(struct rate_info));
@@ -2384,7 +2366,7 @@ static void ath11k_dp_rx_h_rate(struct ath11k *ar, struct hal_rx_desc *rx_desc,
		}
		rx_status->encoding = RX_ENC_HE;
		rx_status->nss = nss;
		rx_status->he_gi = ath11k_he_gi_to_nl80211_he_gi(sgi);
		rx_status->he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi);
		rx_status->bw = ath11k_mac_bw_to_mac80211_bw(bw);
		break;
	}
+103 −3
Original line number Diff line number Diff line
@@ -415,6 +415,105 @@ static void ath11k_dp_tx_cache_peer_stats(struct ath11k *ar,
	}
}

void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts)
{
	struct ath11k_base *ab = ar->ab;
	struct ath11k_per_peer_tx_stats *peer_stats = &ar->cached_stats;
	enum hal_tx_rate_stats_pkt_type pkt_type;
	enum hal_tx_rate_stats_sgi sgi;
	enum hal_tx_rate_stats_bw bw;
	struct ath11k_peer *peer;
	struct ath11k_sta *arsta;
	struct ieee80211_sta *sta;
	u16 rate, ru_tones;
	u8 mcs, rate_idx, ofdma;
	int ret;

	spin_lock_bh(&ab->base_lock);
	peer = ath11k_peer_find_by_id(ab, ts->peer_id);
	if (!peer || !peer->sta) {
		ath11k_dbg(ab, ATH11K_DBG_DP_TX,
			   "failed to find the peer by id %u\n", ts->peer_id);
		goto err_out;
	}

	sta = peer->sta;
	arsta = (struct ath11k_sta *)sta->drv_priv;

	memset(&arsta->txrate, 0, sizeof(arsta->txrate));
	pkt_type = FIELD_GET(HAL_TX_RATE_STATS_INFO0_PKT_TYPE,
			     ts->rate_stats);
	mcs = FIELD_GET(HAL_TX_RATE_STATS_INFO0_MCS,
			ts->rate_stats);
	sgi = FIELD_GET(HAL_TX_RATE_STATS_INFO0_SGI,
			ts->rate_stats);
	bw = FIELD_GET(HAL_TX_RATE_STATS_INFO0_BW, ts->rate_stats);
	ru_tones = FIELD_GET(HAL_TX_RATE_STATS_INFO0_TONES_IN_RU, ts->rate_stats);
	ofdma = FIELD_GET(HAL_TX_RATE_STATS_INFO0_OFDMA_TX, ts->rate_stats);

	/* This is to prefer choose the real NSS value arsta->last_txrate.nss,
	 * if it is invalid, then choose the NSS value while assoc.
	 */
	if (arsta->last_txrate.nss)
		arsta->txrate.nss = arsta->last_txrate.nss;
	else
		arsta->txrate.nss = arsta->peer_nss;

	if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11A ||
	    pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11B) {
		ret = ath11k_mac_hw_ratecode_to_legacy_rate(mcs,
							    pkt_type,
							    &rate_idx,
							    &rate);
		if (ret < 0)
			goto err_out;
		arsta->txrate.legacy = rate;
	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) {
		if (mcs > 7) {
			ath11k_warn(ab, "Invalid HT mcs index %d\n", mcs);
			goto err_out;
		}

		if (arsta->txrate.nss != 0)
			arsta->txrate.mcs = mcs + 8 * (arsta->txrate.nss - 1);
		arsta->txrate.flags = RATE_INFO_FLAGS_MCS;
		if (sgi)
			arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AC) {
		if (mcs > 9) {
			ath11k_warn(ab, "Invalid VHT mcs index %d\n", mcs);
			goto err_out;
		}

		arsta->txrate.mcs = mcs;
		arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
		if (sgi)
			arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
	} else if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) {
		if (mcs > 11) {
			ath11k_warn(ab, "Invalid HE mcs index %d\n", mcs);
			goto err_out;
		}

		arsta->txrate.mcs = mcs;
		arsta->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
		arsta->txrate.he_gi = ath11k_mac_he_gi_to_nl80211_he_gi(sgi);
	}

	arsta->txrate.bw = ath11k_mac_bw_to_mac80211_bw(bw);
	if (ofdma && pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) {
		arsta->txrate.bw = RATE_INFO_BW_HE_RU;
		arsta->txrate.he_ru_alloc =
			ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones);
	}

	if (ath11k_debugfs_is_extd_tx_stats_enabled(ar))
		ath11k_debugfs_sta_add_tx_stats(arsta, peer_stats, rate_idx);

err_out:
	spin_unlock_bh(&ab->base_lock);
}

static void ath11k_dp_tx_complete_msdu(struct ath11k *ar,
				       struct sk_buff *msdu,
				       struct hal_tx_status *ts)
@@ -460,7 +559,8 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar,
	    (info->flags & IEEE80211_TX_CTL_NO_ACK))
		info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;

	if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar))) {
	if (unlikely(ath11k_debugfs_is_extd_tx_stats_enabled(ar)) ||
	    ab->hw_params.single_pdev_only) {
		if (ts->flags & HAL_TX_STATUS_FLAGS_FIRST_MSDU) {
			if (ar->last_ppdu_id == 0) {
				ar->last_ppdu_id = ts->ppdu_id;
@@ -468,12 +568,12 @@ static void ath11k_dp_tx_complete_msdu(struct ath11k *ar,
				   ar->cached_ppdu_id == ar->last_ppdu_id) {
				ar->cached_ppdu_id = ar->last_ppdu_id;
				ar->cached_stats.is_ampdu = true;
				ath11k_debugfs_sta_update_txcompl(ar, msdu, ts);
				ath11k_dp_tx_update_txcompl(ar, ts);
				memset(&ar->cached_stats, 0,
				       sizeof(struct ath11k_per_peer_tx_stats));
			} else {
				ar->cached_stats.is_ampdu = false;
				ath11k_debugfs_sta_update_txcompl(ar, msdu, ts);
				ath11k_dp_tx_update_txcompl(ar, ts);
				memset(&ar->cached_stats, 0,
				       sizeof(struct ath11k_per_peer_tx_stats));
			}
Loading