Commit 9908d98a authored by Ryder Lee's avatar Ryder Lee Committed by Felix Fietkau
Browse files

mt76: mt7915: report tx rate directly from tx status



Report tx rate from tx status packets instead of receving periodic mcu
event. This improves flexibility, accuracy and AQL performance, and
simplifies code flow for better readability.

Signed-off-by: default avatarRyder Lee <ryder.lee@mediatek.com>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent ae06a88f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -244,6 +244,8 @@ struct mt76_wcid {
	struct ewma_signal rssi;
	int inactive_count;

	struct rate_info rate;

	u16 idx;
	u8 hw_key_idx;
	u8 hw_key_idx2;
+0 −47
Original line number Diff line number Diff line
@@ -377,56 +377,9 @@ static int mt7915_sta_fixed_rate_set(void *data, u64 rate)
DEFINE_DEBUGFS_ATTRIBUTE(fops_fixed_rate, NULL,
			 mt7915_sta_fixed_rate_set, "%llx\n");

static int
mt7915_sta_stats_show(struct seq_file *s, void *data)
{
	struct ieee80211_sta *sta = s->private;
	struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
	struct mt7915_sta_stats *stats = &msta->stats;
	struct rate_info *rate = &stats->prob_rate;
	static const char * const bw[] = {
		"BW20", "BW5", "BW10", "BW40",
		"BW80", "BW160", "BW_HE_RU"
	};

	if (!rate->legacy && !rate->flags)
		return 0;

	seq_puts(s, "Probing rate - ");
	if (rate->flags & RATE_INFO_FLAGS_MCS)
		seq_puts(s, "HT ");
	else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
		seq_puts(s, "VHT ");
	else if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
		seq_puts(s, "HE ");
	else
		seq_printf(s, "Bitrate %d\n", rate->legacy);

	if (rate->flags) {
		seq_printf(s, "%s NSS%d MCS%d ",
			   bw[rate->bw], rate->nss, rate->mcs);

		if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
			seq_puts(s, "SGI ");
		else if (rate->he_gi)
			seq_puts(s, "HE GI ");

		if (rate->he_dcm)
			seq_puts(s, "DCM ");
	}

	seq_printf(s, "\nPPDU PER: %ld.%1ld%%\n",
		   stats->per / 10, stats->per % 10);

	return 0;
}

DEFINE_SHOW_ATTRIBUTE(mt7915_sta_stats);

void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			    struct ieee80211_sta *sta, struct dentry *dir)
{
	debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate);
	debugfs_create_file("stats", 0400, dir, sta, &mt7915_sta_stats_fops);
}
#endif
+0 −2
Original line number Diff line number Diff line
@@ -350,7 +350,6 @@ static int mt7915_register_ext_phy(struct mt7915_dev *dev)
	mphy->chainmask = dev->chainmask & ~dev->mphy.chainmask;
	mphy->antenna_mask = BIT(hweight8(mphy->chainmask)) - 1;

	INIT_LIST_HEAD(&phy->stats_list);
	INIT_DELAYED_WORK(&mphy->mac_work, mt7915_mac_work);

	mt7915_eeprom_parse_band_config(phy);
@@ -787,7 +786,6 @@ int mt7915_register_device(struct mt7915_dev *dev)
	dev->phy.dev = dev;
	dev->phy.mt76 = &dev->mt76.phy;
	dev->mt76.phy.priv = &dev->phy;
	INIT_LIST_HEAD(&dev->phy.stats_list);
	INIT_WORK(&dev->rc_work, mt7915_mac_sta_rc_work);
	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7915_mac_work);
	INIT_LIST_HEAD(&dev->sta_rc_list);
+135 −38
Original line number Diff line number Diff line
@@ -88,15 +88,14 @@ bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask)
			 0, 5000);
}

static u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid)
static u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw)
{
	mt76_wr(dev, MT_WTBLON_TOP_WDUCR,
		FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7)));

	return MT_WTBL_LMAC_OFFS(wcid, 0);
	return MT_WTBL_LMAC_OFFS(wcid, dw);
}

/* TODO: use txfree airtime info to avoid runtime accessing in the long run */
static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
{
	static const u8 ac_to_tid[] = {
@@ -107,6 +106,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
	};
	struct ieee80211_sta *sta;
	struct mt7915_sta *msta;
	struct rate_info *rate;
	u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
	LIST_HEAD(sta_poll_list);
	int i;
@@ -119,8 +119,9 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)

	while (true) {
		bool clear = false;
		u32 addr;
		u32 addr, val;
		u16 idx;
		u8 bw;

		spin_lock_bh(&dev->sta_poll_lock);
		if (list_empty(&sta_poll_list)) {
@@ -133,7 +134,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
		spin_unlock_bh(&dev->sta_poll_lock);

		idx = msta->wcid.idx;
		addr = mt7915_mac_wtbl_lmac_addr(dev, idx) + 20 * 4;
		addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 20);

		for (i = 0; i < IEEE80211_NUM_ACS; i++) {
			u32 tx_last = msta->airtime_ac[i];
@@ -174,6 +175,43 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
			ieee80211_sta_register_airtime(sta, tid, tx_cur,
						       rx_cur);
		}

		/*
		 * We don't support reading GI info from txs packets.
		 * For accurate tx status reporting and AQL improvement,
		 * we need to make sure that flags match so polling GI
		 * from per-sta counters directly.
		 */
		rate = &msta->wcid.rate;
		addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 7);
		val = mt76_rr(dev, addr);

		switch (rate->bw) {
		case RATE_INFO_BW_160:
			bw = IEEE80211_STA_RX_BW_160;
			break;
		case RATE_INFO_BW_80:
			bw = IEEE80211_STA_RX_BW_80;
			break;
		case RATE_INFO_BW_40:
			bw = IEEE80211_STA_RX_BW_40;
			break;
		default:
			bw = IEEE80211_STA_RX_BW_20;
			break;
		}

		if (rate->flags & RATE_INFO_FLAGS_HE_MCS) {
			u8 offs = 24 + 2 * bw;

			rate->he_gi = (val & (0x3 << offs)) >> offs;
		} else if (rate->flags &
			   (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) {
			if (val & BIT(12 + bw))
				rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
			else
				rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
		}
	}

	rcu_read_unlock();
@@ -1057,6 +1095,17 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
	if (!wcid)
		wcid = &dev->mt76.global_wcid;

	if (sta) {
		struct mt7915_sta *msta;

		msta = (struct mt7915_sta *)sta->drv_priv;

		if (time_after(jiffies, msta->stats.jiffies + HZ / 4)) {
			info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
			msta->stats.jiffies = jiffies;
		}
	}

	pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);

	mt7915_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, pid, key,
@@ -1222,8 +1271,6 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)

			msta = container_of(wcid, struct mt7915_sta, wcid);
			spin_lock_bh(&dev->sta_poll_lock);
			if (list_empty(&msta->stats_list))
				list_add_tail(&msta->stats_list, &phy->stats_list);
			if (list_empty(&msta->poll_list))
				list_add_tail(&msta->poll_list, &dev->sta_poll_list);
			spin_unlock_bh(&dev->sta_poll_lock);
@@ -1257,18 +1304,25 @@ static bool
mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid,
		       __le32 *txs_data)
{
	struct ieee80211_supported_band *sband;
	struct mt76_dev *mdev = &dev->mt76;
	struct mt76_phy *mphy;
	struct ieee80211_tx_info *info;
	struct sk_buff_head list;
	struct rate_info rate = {};
	struct sk_buff *skb;
	bool cck = false;
	u32 txrate, txs;

	mt76_tx_status_lock(mdev, &list);
	skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
	if (!skb)
		goto out;

	txs = le32_to_cpu(txs_data[0]);

	info = IEEE80211_SKB_CB(skb);
	if (!(txs_data[0] & cpu_to_le32(MT_TXS0_ACK_ERROR_MASK)))
	if (!(txs & MT_TXS0_ACK_ERROR_MASK))
		info->flags |= IEEE80211_TX_STAT_ACK;

	info->status.ampdu_len = 1;
@@ -1276,9 +1330,81 @@ mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid,
					IEEE80211_TX_STAT_ACK);

	info->status.rates[0].idx = -1;
	mt76_tx_status_skb_done(mdev, skb, &list);

	if (!wcid->sta)
		goto out;

	txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);

	rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
	rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;

	switch (FIELD_GET(MT_TX_RATE_MODE, txrate)) {
	case MT_PHY_TYPE_CCK:
		cck = true;
		fallthrough;
	case MT_PHY_TYPE_OFDM:
		mphy = &dev->mphy;
		if (wcid->ext_phy && dev->mt76.phy2)
			mphy = dev->mt76.phy2;

		if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
			sband = &mphy->sband_5g.sband;
		else
			sband = &mphy->sband_2g.sband;

		rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck);
		rate.legacy = sband->bitrates[rate.mcs].bitrate;
		break;
	case MT_PHY_TYPE_HT:
	case MT_PHY_TYPE_HT_GF:
		rate.mcs += (rate.nss - 1) * 8;
		if (rate.mcs > 31)
			goto out;

		rate.flags = RATE_INFO_FLAGS_MCS;
		if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
			rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
		break;
	case MT_PHY_TYPE_VHT:
		if (rate.mcs > 9)
			goto out;

		rate.flags = RATE_INFO_FLAGS_VHT_MCS;
		break;
	case MT_PHY_TYPE_HE_SU:
	case MT_PHY_TYPE_HE_EXT_SU:
	case MT_PHY_TYPE_HE_TB:
	case MT_PHY_TYPE_HE_MU:
		if (rate.mcs > 11)
			goto out;

		rate.he_gi = wcid->rate.he_gi;
		rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate);
		rate.flags = RATE_INFO_FLAGS_HE_MCS;
		break;
	default:
		goto out;
	}

	switch (FIELD_GET(MT_TXS0_BW, txs)) {
	case IEEE80211_STA_RX_BW_160:
		rate.bw = RATE_INFO_BW_160;
		break;
	case IEEE80211_STA_RX_BW_80:
		rate.bw = RATE_INFO_BW_80;
		break;
	case IEEE80211_STA_RX_BW_40:
		rate.bw = RATE_INFO_BW_40;
		break;
	default:
		rate.bw = RATE_INFO_BW_20;
		break;
	}
	wcid->rate = rate;

out:
	mt76_tx_status_skb_done(mdev, skb, &list);
	mt76_tx_status_unlock(mdev, &list);

	return !!skb;
@@ -1769,30 +1895,6 @@ mt7915_mac_update_stats(struct mt7915_phy *phy)
	}
}

static void
mt7915_mac_sta_stats_work(struct mt7915_phy *phy)
{
	struct mt7915_dev *dev = phy->dev;
	struct mt7915_sta *msta;
	LIST_HEAD(list);

	spin_lock_bh(&dev->sta_poll_lock);
	list_splice_init(&phy->stats_list, &list);

	while (!list_empty(&list)) {
		msta = list_first_entry(&list, struct mt7915_sta, stats_list);
		list_del_init(&msta->stats_list);
		spin_unlock_bh(&dev->sta_poll_lock);

		/* use MT_TX_FREE_RATE to report Tx rate for further devices */
		mt7915_mcu_get_tx_rate(dev, RATE_CTRL_RU_INFO, msta->wcid.idx);

		spin_lock_bh(&dev->sta_poll_lock);
	}

	spin_unlock_bh(&dev->sta_poll_lock);
}

void mt7915_mac_sta_rc_work(struct work_struct *work)
{
	struct mt7915_dev *dev = container_of(work, struct mt7915_dev, rc_work);
@@ -1849,11 +1951,6 @@ void mt7915_mac_work(struct work_struct *work)
		mt7915_mac_update_stats(phy);
	}

	if (++phy->sta_work_count == 10) {
		phy->sta_work_count = 0;
		mt7915_mac_sta_stats_work(phy);
	}

	mutex_unlock(&mphy->dev->mutex);

	mt76_tx_status_check(mphy->dev, NULL, false);
+11 −15
Original line number Diff line number Diff line
@@ -231,7 +231,6 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
	idx = MT7915_WTBL_RESERVED - mvif->idx;

	INIT_LIST_HEAD(&mvif->sta.rc_list);
	INIT_LIST_HEAD(&mvif->sta.stats_list);
	INIT_LIST_HEAD(&mvif->sta.poll_list);
	mvif->sta.wcid.idx = idx;
	mvif->sta.wcid.ext_phy = mvif->band_idx;
@@ -618,7 +617,6 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
		return -ENOSPC;

	INIT_LIST_HEAD(&msta->rc_list);
	INIT_LIST_HEAD(&msta->stats_list);
	INIT_LIST_HEAD(&msta->poll_list);
	msta->vif = mvif;
	msta->wcid.sta = 1;
@@ -652,8 +650,6 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
	spin_lock_bh(&dev->sta_poll_lock);
	if (!list_empty(&msta->poll_list))
		list_del_init(&msta->poll_list);
	if (!list_empty(&msta->stats_list))
		list_del_init(&msta->stats_list);
	if (!list_empty(&msta->rc_list))
		list_del_init(&msta->rc_list);
	spin_unlock_bh(&dev->sta_poll_lock);
@@ -926,7 +922,7 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
{
	struct mt7915_phy *phy = mt7915_hw_phy(hw);
	struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
	struct mt7915_sta_stats *stats = &msta->stats;
	struct rate_info *txrate = &msta->wcid.rate;
	struct rate_info rxrate = {};

	if (!mt7915_mcu_get_rx_rate(phy, vif, sta, &rxrate)) {
@@ -934,20 +930,20 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
	}

	if (!stats->tx_rate.legacy && !stats->tx_rate.flags)
	if (!txrate->legacy && !txrate->flags)
		return;

	if (stats->tx_rate.legacy) {
		sinfo->txrate.legacy = stats->tx_rate.legacy;
	if (txrate->legacy) {
		sinfo->txrate.legacy = txrate->legacy;
	} else {
		sinfo->txrate.mcs = stats->tx_rate.mcs;
		sinfo->txrate.nss = stats->tx_rate.nss;
		sinfo->txrate.bw = stats->tx_rate.bw;
		sinfo->txrate.he_gi = stats->tx_rate.he_gi;
		sinfo->txrate.he_dcm = stats->tx_rate.he_dcm;
		sinfo->txrate.he_ru_alloc = stats->tx_rate.he_ru_alloc;
		sinfo->txrate.mcs = txrate->mcs;
		sinfo->txrate.nss = txrate->nss;
		sinfo->txrate.bw = txrate->bw;
		sinfo->txrate.he_gi = txrate->he_gi;
		sinfo->txrate.he_dcm = txrate->he_dcm;
		sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc;
	}
	sinfo->txrate.flags = stats->tx_rate.flags;
	sinfo->txrate.flags = txrate->flags;
	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
}

Loading