2021-07-22 20:29:08

by Ben Greear

[permalink] [raw]
Subject: [PATCH v2 01/11] mt76: add hash lookup for skb on TXS status_list.

From: Ben Greear <[email protected]>

This improves performance when sending lots of frames that
are requesting being mapped to a TXS callback.

Add comments to help next person understood the tx path
better.

Signed-off-by: Ben Greear <[email protected]>
---

v2: Fix most checkpatch warnings.
Fix bug in tx-status patch that caused tx-hang.

drivers/net/wireless/mediatek/mt76/mt76.h | 45 ++++++++---
.../net/wireless/mediatek/mt76/mt7603/mac.c | 2 +-
.../net/wireless/mediatek/mt76/mt7615/mac.c | 2 +-
.../net/wireless/mediatek/mt76/mt76x02_mac.c | 2 +-
.../net/wireless/mediatek/mt76/mt7915/mac.c | 8 +-
.../net/wireless/mediatek/mt76/mt7921/mac.c | 7 +-
drivers/net/wireless/mediatek/mt76/tx.c | 79 ++++++++++++++++---
7 files changed, 117 insertions(+), 28 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 436bf2b8e2cd..1e6b8b072aec 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -235,6 +235,14 @@ DECLARE_EWMA(signal, 10, 8);
#define MT_WCID_TX_INFO_TXPWR_ADJ GENMASK(25, 18)
#define MT_WCID_TX_INFO_SET BIT(31)

+#define MT_PACKET_ID_MASK GENMASK(6, 0)
+#define MT_PACKET_ID_NO_ACK 0
+/* Request TXS, but don't try to match with skb. */
+#define MT_PACKET_ID_NO_SKB 1
+#define MT_PACKET_ID_FIRST 2
+#define MT_PACKET_ID_HAS_RATE BIT(7)
+#define MT_PACKET_ID_MAX (GENMASK(7, 0) - 1)
+
struct mt76_wcid {
struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS];

@@ -246,6 +254,8 @@ struct mt76_wcid {

struct rate_info rate;

+ struct sk_buff *skb_status_array[MT_PACKET_ID_MAX + 1];
+
u16 idx;
u8 hw_key_idx;
u8 hw_key_idx2;
@@ -302,13 +312,8 @@ struct mt76_rx_tid {
#define MT_TX_CB_TXS_DONE BIT(1)
#define MT_TX_CB_TXS_FAILED BIT(2)

-#define MT_PACKET_ID_MASK GENMASK(6, 0)
-#define MT_PACKET_ID_NO_ACK 0
-#define MT_PACKET_ID_NO_SKB 1
-#define MT_PACKET_ID_FIRST 2
-#define MT_PACKET_ID_HAS_RATE BIT(7)
-
-#define MT_TX_STATUS_SKB_TIMEOUT HZ
+/* This is timer for when to give up when waiting for TXS callback. */
+#define MT_TX_STATUS_SKB_TIMEOUT (HZ / 8)

struct mt76_tx_cb {
unsigned long jiffies;
@@ -651,6 +656,7 @@ struct mt76_dev {
spinlock_t cc_lock;

u32 cur_cc_bss_rx;
+ unsigned long next_status_jiffies;

struct mt76_rx_status rx_ampdu_status;
u32 rx_ampdu_len;
@@ -1090,7 +1096,7 @@ struct sk_buff *mt76_tx_status_skb_get(struct mt76_dev *dev,
struct mt76_wcid *wcid, int pktid,
struct sk_buff_head *list);
void mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb,
- struct sk_buff_head *list);
+ struct sk_buff_head *list, struct mt76_wcid *wcid);
void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid, struct sk_buff *skb,
struct list_head *free_list);
static inline void
@@ -1297,13 +1303,30 @@ mt76_token_put(struct mt76_dev *dev, int token)
}

static inline int
-mt76_get_next_pkt_id(struct mt76_wcid *wcid)
+mt76_get_next_pkt_id(struct mt76_wcid *wcid, struct sk_buff *skb)
{
+ struct sk_buff *qskb;
+
wcid->packet_id = (wcid->packet_id + 1) & MT_PACKET_ID_MASK;
- if (wcid->packet_id == MT_PACKET_ID_NO_ACK ||
- wcid->packet_id == MT_PACKET_ID_NO_SKB)
+ if (wcid->packet_id < MT_PACKET_ID_FIRST)
wcid->packet_id = MT_PACKET_ID_FIRST;

+ qskb = wcid->skb_status_array[wcid->packet_id];
+ if (qskb) {
+ /* bummer, already waiting on this pid. See if it is stale. */
+ struct mt76_tx_cb *cb = mt76_tx_skb_cb(qskb);
+
+ if (!time_after(jiffies, cb->jiffies + MT_TX_STATUS_SKB_TIMEOUT)) {
+ /* ok, not stale. Increment pid anyway, will try next
+ * slot next time
+ */
+ return MT_PACKET_ID_NO_SKB;
+ }
+ }
+
+ /* cache this skb for fast lookup by packet-id */
+ wcid->skb_status_array[wcid->packet_id] = skb;
+
return wcid->packet_id;
}
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
index 3972c56136a2..2f268eb7c1e6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
@@ -1230,7 +1230,7 @@ mt7603_mac_add_txs_skb(struct mt7603_dev *dev, struct mt7603_sta *sta, int pid,
info->status.rates[0].idx = -1;
}

- mt76_tx_status_skb_done(mdev, skb, &list);
+ mt76_tx_status_skb_done(mdev, skb, &list, &sta->wcid);
}
mt76_tx_status_unlock(mdev, &list);

diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index ff3f85e4087c..381a998817d4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -1433,7 +1433,7 @@ static bool mt7615_mac_add_txs_skb(struct mt7615_dev *dev,
info->status.rates[0].idx = -1;
}

- mt76_tx_status_skb_done(mdev, skb, &list);
+ mt76_tx_status_skb_done(mdev, skb, &list, &sta->wcid);
}
mt76_tx_status_unlock(mdev, &list);

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index c32e6dc68773..fce020e64678 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -622,7 +622,7 @@ void mt76x02_send_tx_status(struct mt76x02_dev *dev,
info = *status.info;
len = status.skb->len;
ac = skb_get_queue_mapping(status.skb);
- mt76_tx_status_skb_done(mdev, status.skb, &list);
+ mt76_tx_status_skb_done(mdev, status.skb, &list, wcid);
} else if (msta) {
len = status.info->status.ampdu_len * ewma_pktlen_read(&msta->pktlen);
ac = FIELD_GET(MT_PKTID_AC, cur_pktid);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index cb6d9d2d4e06..555a2f94d069 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -1318,6 +1318,8 @@ mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid,

mt76_tx_status_lock(mdev, &list);
skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
+
+ /* TODO: Gather stats anyway, even if we are not matching on an skb. */
if (!skb)
goto out;

@@ -1417,10 +1419,14 @@ mt7915_mac_add_txs_skb(struct mt7915_dev *dev, struct mt76_wcid *wcid, int pid,
stats->tx_bw[0]++;
break;
}
+
+ /* Cache rate for packets that don't get a TXS callback for some
+ * reason.
+ */
wcid->rate = rate;

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

return !!skb;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 76985a6b3be5..a1829a6c4c64 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -732,7 +732,8 @@ mt7921_mac_write_txwi_80211(struct mt7921_dev *dev, __le32 *txwi,
txwi[7] |= cpu_to_le32(val);
}

-static void mt7921_update_txs(struct mt76_wcid *wcid, __le32 *txwi)
+static void mt7921_update_txs(struct mt76_wcid *wcid, __le32 *txwi,
+ struct sk_buff *skb)
{
struct mt7921_sta *msta = container_of(wcid, struct mt7921_sta, wcid);
u32 pid, frame_type = FIELD_GET(MT_TXD2_FRAME_TYPE, txwi[2]);
@@ -744,7 +745,7 @@ static void mt7921_update_txs(struct mt76_wcid *wcid, __le32 *txwi)
return;

msta->next_txs_ts = jiffies + msecs_to_jiffies(250);
- pid = mt76_get_next_pkt_id(wcid);
+ pid = mt76_get_next_pkt_id(wcid, skb);
txwi[5] |= cpu_to_le32(MT_TXD5_TX_STATUS_MCU |
FIELD_PREP(MT_TXD5_PID, pid));
}
@@ -839,7 +840,7 @@ void mt7921_mac_write_txwi(struct mt7921_dev *dev, __le32 *txwi,
txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
}

- mt7921_update_txs(wcid, txwi);
+ mt7921_update_txs(wcid, txwi, skb);
}

static void
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 6f302acb6e69..730f37aede3a 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -36,6 +36,7 @@ mt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(mt76_tx_check_agg_ssn);

+/* Lock list, and initialize the timed-out-skb list object. */
void
mt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list)
__acquires(&dev->status_list.lock)
@@ -45,6 +46,9 @@ mt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list)
}
EXPORT_SYMBOL_GPL(mt76_tx_status_lock);

+/* Unlock list, and use last-received status for any skbs that
+ * timed out getting TXS callback (they are on the list passed in
+ */
void
mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list)
__releases(&dev->status_list.lock)
@@ -80,7 +84,7 @@ EXPORT_SYMBOL_GPL(mt76_tx_status_unlock);

static void
__mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,
- struct sk_buff_head *list)
+ struct sk_buff_head *list, struct mt76_wcid *wcid)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
@@ -89,11 +93,20 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,
flags |= cb->flags;
cb->flags = flags;

+ /* Only process skb with TXS status has been received and also
+ * the txfree (DMA_DONE) callback has happened.
+ */
if ((flags & done) != done)
return;

__skb_unlink(skb, &dev->status_list);

+ /* Make sure we clear any cached skb. */
+ if (wcid) {
+ if (!(WARN_ON_ONCE(cb->pktid >= ARRAY_SIZE(wcid->skb_status_array))))
+ wcid->skb_status_array[cb->pktid] = NULL;
+ }
+
/* Tx status can be unreliable. if it fails, mark the frame as ACKed */
if (flags & MT_TX_CB_TXS_FAILED) {
info->status.rates[0].count = 0;
@@ -106,9 +119,9 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags,

void
mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb,
- struct sk_buff_head *list)
+ struct sk_buff_head *list, struct mt76_wcid *wcid)
{
- __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_DONE, list);
+ __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_DONE, list, wcid);
}
EXPORT_SYMBOL_GPL(mt76_tx_status_skb_done);

@@ -119,6 +132,7 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
int pid;
+ int qlen;

if (!wcid)
return MT_PACKET_ID_NO_ACK;
@@ -130,15 +144,30 @@ mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid,
IEEE80211_TX_CTL_RATE_CTRL_PROBE)))
return MT_PACKET_ID_NO_SKB;

+ /* due to limited range of the pktid (7 bits), we can only
+ * have a limited number of outstanding frames. I think it is OK to
+ * check the length outside of a lock since it doesn't matter too much
+ * if we read wrong data here.
+ * The TX-status callbacks don't always return a callback for an SKB,
+ * so the status_list may contain some stale skbs. Those will be cleaned
+ * out periodically, see MT_TX_STATUS_SKB_TIMEOUT.
+ */
+
+ qlen = skb_queue_len(&dev->status_list);
+ if (qlen > 120)
+ return MT_PACKET_ID_NO_SKB;
+
spin_lock_bh(&dev->status_list.lock);

memset(cb, 0, sizeof(*cb));
- pid = mt76_get_next_pkt_id(wcid);
+ pid = mt76_get_next_pkt_id(wcid, skb);
cb->wcid = wcid->idx;
cb->pktid = pid;
cb->jiffies = jiffies;

- __skb_queue_tail(&dev->status_list, skb);
+ if (cb->pktid != MT_PACKET_ID_NO_SKB)
+ __skb_queue_tail(&dev->status_list, skb);
+
spin_unlock_bh(&dev->status_list.lock);

return pid;
@@ -150,6 +179,32 @@ mt76_tx_status_skb_get(struct mt76_dev *dev, struct mt76_wcid *wcid, int pktid,
struct sk_buff_head *list)
{
struct sk_buff *skb, *tmp;
+ struct sk_buff *rvskb = NULL;
+
+ /* If pktid is < first-valid-id, then it is not something we requested
+ * TXS for, so we will not find SKB. Bail out early in that case,
+ * unless we need to walk due to stale-skb-reaper timeout.
+ */
+ if (pktid < MT_PACKET_ID_FIRST) {
+ if (!time_after(jiffies, dev->next_status_jiffies))
+ return NULL;
+ }
+
+ if (wcid) {
+ if (WARN_ON_ONCE(pktid >= ARRAY_SIZE(wcid->skb_status_array))) {
+ pr_err("invalid pktid: %d status-array-size: %d\n",
+ pktid, (int)(ARRAY_SIZE(wcid->skb_status_array)));
+ pktid = 0; /* protect from potential fw bugs */
+ }
+
+ skb = wcid->skb_status_array[pktid];
+
+ /* Make sure we clear our cached skb. */
+ wcid->skb_status_array[pktid] = NULL;
+
+ if (skb && !time_after(jiffies, dev->next_status_jiffies))
+ return skb;
+ }

skb_queue_walk_safe(&dev->status_list, skb, tmp) {
struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb);
@@ -157,18 +212,22 @@ mt76_tx_status_skb_get(struct mt76_dev *dev, struct mt76_wcid *wcid, int pktid,
if (wcid && cb->wcid != wcid->idx)
continue;

- if (cb->pktid == pktid)
- return skb;
+ if (cb->pktid == pktid) {
+ /* Found our skb, but check for timeouts too */
+ rvskb = skb;
+ continue;
+ }

if (pktid >= 0 && !time_after(jiffies, cb->jiffies +
MT_TX_STATUS_SKB_TIMEOUT))
continue;

__mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_FAILED |
- MT_TX_CB_TXS_DONE, list);
+ MT_TX_CB_TXS_DONE, list, wcid);
}
+ dev->next_status_jiffies = jiffies + MT_TX_STATUS_SKB_TIMEOUT + 1;

- return NULL;
+ return rvskb;
}
EXPORT_SYMBOL_GPL(mt76_tx_status_skb_get);

@@ -238,7 +297,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
}

mt76_tx_status_lock(dev, &list);
- __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_DMA_DONE, &list);
+ __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_DMA_DONE, &list, wcid);
mt76_tx_status_unlock(dev, &list);

out:
--
2.20.1


2021-07-22 20:29:11

by Ben Greear

[permalink] [raw]
Subject: [PATCH v2 06/11] mt76: mt7915: Support parsing TXSFM 0x2.

From: Ben Greear <[email protected]>

Add code to support this format, but I don't actually
see any of these TXS format frames on my test system.

Signed-off-by: Ben Greear <[email protected]>
---
.../net/wireless/mediatek/mt76/mt7915/mac.c | 24 +++++++++++--------
.../net/wireless/mediatek/mt76/mt7915/mac.h | 3 +++
2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index 9bd2a5e795b8..073891850c0c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -1317,13 +1317,10 @@ mt7915_mac_parse_txs(struct mt7915_dev *dev, struct mt76_wcid *wcid,
{
struct ieee80211_supported_band *sband;
struct mt76_phy *mphy;
- u32 txrate, txs, txs5, txs6, txs7, mode;
+ u32 txrate, txs, mode;
bool cck = false;

txs = le32_to_cpu(txs_data[0]);
- txs5 = le32_to_cpu(txs_data[5]);
- txs6 = le32_to_cpu(txs_data[6]);
- txs7 = le32_to_cpu(txs_data[7]);

if (!(txs & MT_TXS0_ACK_ERROR_MASK))
info->flags |= IEEE80211_TX_STAT_ACK;
@@ -1334,10 +1331,6 @@ mt7915_mac_parse_txs(struct mt7915_dev *dev, struct mt76_wcid *wcid,

info->status.rates[0].idx = -1;

- stats->tx_mpdu_attempts += FIELD_GET(MT_TXS5_F1_MPDU_TX_COUNT, txs5);
- stats->tx_mpdu_fail += FIELD_GET(MT_TXS6_F1_MPDU_FAIL_COUNT, txs6);
- stats->tx_mpdu_retry += FIELD_GET(MT_TXS7_F1_MPDU_RETRY_COUNT, txs7);
-
txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);

rate->mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
@@ -1418,6 +1411,19 @@ mt7915_mac_parse_txs(struct mt7915_dev *dev, struct mt76_wcid *wcid,
stats->tx_bw[0]++;
break;
}
+
+ if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) == 2) {
+ /* TXSFM == 10 */
+ u32 txs5, txs6, txs7;
+
+ txs5 = le32_to_cpu(txs_data[5]);
+ txs6 = le32_to_cpu(txs_data[6]);
+ txs7 = le32_to_cpu(txs_data[7]);
+
+ stats->tx_mpdu_attempts += FIELD_GET(MT_TXS5_F1_MPDU_TX_COUNT, txs5);
+ stats->tx_mpdu_fail += FIELD_GET(MT_TXS6_F1_MPDU_FAIL_COUNT, txs6);
+ stats->tx_mpdu_retry += FIELD_GET(MT_TXS7_F1_MPDU_RETRY_COUNT, txs7);
+ }
}

static void
@@ -1469,8 +1475,6 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
u8 pid;

txs = le32_to_cpu(txs_data[0]);
- if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1)
- return;

txs = le32_to_cpu(txs_data[2]);
wcidx = FIELD_GET(MT_TXS2_WCID, txs);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
index ccce994dc07a..986a65badd1b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
@@ -347,6 +347,9 @@ struct mt7915_tx_free {

#define MT_TXS4_TIMESTAMP GENMASK(31, 0)

+/* The F0 variables are for TXSFM 0x0 and 0x1. The F1 variables
+ * are for TXSFM 0x2 aka PPDU format.
+ */
#define MT_TXS5_F0_FINAL_MPDU BIT(31)
#define MT_TXS5_F0_QOS BIT(30)
#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25)
--
2.20.1

2021-07-22 20:30:20

by Ben Greear

[permalink] [raw]
Subject: [PATCH v2 10/11] mt76: mt7915: Report tx-retries

From: Ben Greear <[email protected]>

mac80211 stack will only report tx-status for skb claiming to be ampdu heads,
so lie a bit in mt7915 and set the flag so that mac80211 will record status
for each skb.

mt7915 appears to report retry status on an individual per-skb manner,
so that method above seems to work.

Re-constitute the txinfo status rate info so that the rix and flags
is also at least close to correct. No direct way to report HE
rates that way, so mac80211 might could use some tweaking in
the ieee80211_tx_status_ext to take both info and status->rate
into account.

Signed-off-by: Ben Greear <[email protected]>
---
.../net/wireless/mediatek/mt76/mt7915/init.c | 1 +
.../net/wireless/mediatek/mt76/mt7915/mac.c | 82 +++++++++++++++++--
.../net/wireless/mediatek/mt76/mt7915/mac.h | 4 +-
drivers/net/wireless/mediatek/mt76/tx.c | 6 +-
4 files changed, 86 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index a0d282771d77..9dc7a67dd76f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -217,6 +217,7 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
struct wiphy *wiphy = hw->wiphy;

hw->queues = 4;
+ hw->max_report_rates = 1;
hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF;
hw->netdev_features = NETIF_F_RXCSUM;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index d5747d61404a..09c91f887ae9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -1257,17 +1257,22 @@ mt7915_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t)

static void
mt7915_txwi_free(struct mt7915_dev *dev, struct mt76_txwi_cache *t,
- struct ieee80211_sta *sta, struct list_head *free_list)
+ struct ieee80211_sta *sta, struct list_head *free_list,
+ u32 tx_cnt, u32 tx_status, u32 ampdu)
{
struct mt76_dev *mdev = &dev->mt76;
struct mt76_wcid *wcid;
__le32 *txwi;
u16 wcid_idx;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_tx_rate *rate;

mt7915_txp_skb_unmap(mdev, t);
if (!t->skb)
goto out;

+ rcu_read_lock(); /* protect wcid access */
+
txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
if (sta) {
wcid = (struct mt76_wcid *)sta->drv_priv;
@@ -1277,6 +1282,64 @@ mt7915_txwi_free(struct mt7915_dev *dev, struct mt76_txwi_cache *t,
mt7915_tx_check_aggr(sta, txwi);
} else {
wcid_idx = FIELD_GET(MT_TXD1_WLAN_IDX, le32_to_cpu(txwi[1]));
+ wcid = rcu_dereference(mdev->wcid[wcid_idx]);
+ }
+
+ info = IEEE80211_SKB_CB(t->skb);
+
+ /* Cannot clear all of info->status, we need the driver private
+ * status intact.
+ */
+ info->status.is_valid_ack_signal = 0;
+
+ rate = &info->status.rates[0];
+ rate->idx = -1; /* will over-write below if we found wcid */
+ info->status.rates[1].idx = -1; /* terminate rate list */
+
+ /* force TX_STAT_AMPDU to be set, or mac80211 will ignore status */
+ if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU;
+ info->status.ampdu_len = 1;
+ }
+
+ /* update info status based on cached wcid rate info since
+ * txfree path doesn't give us a lot of info.
+ */
+ if (wcid) {
+ if (wcid->rate.flags & RATE_INFO_FLAGS_MCS) {
+ rate->flags |= IEEE80211_TX_RC_MCS;
+ rate->idx = wcid->rate.mcs + wcid->rate.nss * 8;
+ } else if (wcid->rate.flags & RATE_INFO_FLAGS_VHT_MCS) {
+ rate->flags |= IEEE80211_TX_RC_VHT_MCS;
+ rate->idx = (wcid->rate.nss << 4) | wcid->rate.mcs;
+ } else if (wcid->rate.flags & RATE_INFO_FLAGS_HE_MCS) {
+ rate->idx = (wcid->rate.nss << 4) | wcid->rate.mcs;
+ } else {
+ rate->idx = wcid->rate.mcs;
+ }
+
+ switch (wcid->rate.bw) {
+ case RATE_INFO_BW_160:
+ rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_80:
+ rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+ break;
+ case RATE_INFO_BW_40:
+ rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+
+ /* Apply the values that this txfree path reports */
+ rate->count = tx_cnt;
+ if (tx_status == 0) {
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ info->status.ampdu_ack_len = 1;
+ } else {
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
}

__mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
@@ -1296,7 +1359,8 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
struct ieee80211_sta *sta = NULL;
LIST_HEAD(free_list);
struct sk_buff *tmp;
- u8 i, count;
+ u8 i;
+ u16 count;
bool wake = false;

/* clean DMA queues and unmap buffers first */
@@ -1312,9 +1376,12 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
* to the time ack is received or dropped by hw (air + hw queue time).
* Should avoid accessing WTBL to get Tx airtime, and use it instead.
*/
+ /* free->ctrl is high u16 of first DW in the txfree struct */
count = FIELD_GET(MT_TX_FREE_MSDU_CNT, le16_to_cpu(free->ctrl));
for (i = 0; i < count; i++) {
- u32 msdu, info = le32_to_cpu(free->info[i]);
+ u32 msdu, tx_cnt, tx_status;
+ u32 info = le32_to_cpu(free->info[i]); /* DW3+ */
+ u32 ampdu;

/*
* 1'b1: new wcid pair.
@@ -1345,7 +1412,12 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, struct sk_buff *skb)
if (!txwi)
continue;

- mt7915_txwi_free(dev, txwi, sta, &free_list);
+ tx_cnt = FIELD_GET(MT_TX_FREE_TXCNT, info);
+ /* 0 = success, 1 dropped-by-hw, 2 dropped-by-cpu */
+ tx_status = FIELD_GET(MT_TX_FREE_STATUS, info);
+ ampdu = FIELD_GET(MT_TX_FREE_HEAD_OF_PAGE, info);
+
+ mt7915_txwi_free(dev, txwi, sta, &free_list, tx_cnt, tx_status, ampdu);
}

mt7915_mac_sta_poll(dev);
@@ -1873,7 +1945,7 @@ void mt7915_tx_token_put(struct mt7915_dev *dev)

spin_lock_bh(&dev->mt76.token_lock);
idr_for_each_entry(&dev->mt76.token, txwi, id) {
- mt7915_txwi_free(dev, txwi, NULL, NULL);
+ mt7915_txwi_free(dev, txwi, NULL, NULL, 0, 1, 0);
dev->mt76.token_count--;
}
spin_unlock_bh(&dev->mt76.token_lock);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
index 741d51b0fa06..3f8fbed144df 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
@@ -299,7 +299,7 @@ struct mt7915_tx_free {
__le16 ctrl;
u8 txd_cnt;
u8 rsv[3];
- __le32 info[];
+ __le32 info[]; /* DW3+ */
} __packed __aligned(4);

#define MT_TX_FREE_MSDU_CNT GENMASK(9, 0)
@@ -311,6 +311,8 @@ struct mt7915_tx_free {
/* when configured for txcount mode. See MT_PLE_HOST_RPT0_TX_LATENCY. */
#define MT_TX_FREE_TXCNT GENMASK(12, 0)
#define MT_TX_FREE_STATUS GENMASK(14, 13)
+/* 0: not MPDU, 1: MSDU is head pkt of TXD page (MPDU) */
+#define MT_TX_FREE_HEAD_OF_PAGE BIT(15)
#define MT_TX_FREE_MSDU_ID GENMASK(30, 16)
#define MT_TX_FREE_PAIR BIT(31)

diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 730f37aede3a..7ab7ea63fa52 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -263,6 +263,7 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *
struct ieee80211_tx_status status = {
.skb = skb,
.free_list = free_list,
+ .info = IEEE80211_SKB_CB(skb),
};
struct mt76_wcid *wcid = NULL;
struct ieee80211_hw *hw;
@@ -270,8 +271,11 @@ void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *

rcu_read_lock();

- if (wcid_idx < ARRAY_SIZE(dev->wcid))
+ if (wcid_idx < ARRAY_SIZE(dev->wcid)) {
wcid = rcu_dereference(dev->wcid[wcid_idx]);
+ if (wcid)
+ status.rate = &wcid->rate;
+ }

mt76_tx_check_non_aql(dev, wcid, skb);

--
2.20.1

2021-07-22 20:30:21

by Ben Greear

[permalink] [raw]
Subject: [PATCH v2 11/11] mt76: mt7915: add a missing HT flag for GI parsing

From: Ryder Lee <[email protected]>

The previous commit missed a HT flag.

Fixes: 14b220c07188 ("mt76: mt7915: report tx rate directly from tx status")
Signed-off-by: Ryder Lee <[email protected]>
---
drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index 09c91f887ae9..565875813565 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -215,7 +215,8 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)

rate->he_gi = (val & (0x3 << offs)) >> offs;
msta->wcid.rate_he_gi = rate->he_gi; /* cache for later */
- } else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) {
+ } 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;
msta->wcid.rate_short_gi = 1;
@@ -224,7 +225,6 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
msta->wcid.rate_short_gi = 0;
}
}
- /* TODO: Deal with HT_MCS */
}

rcu_read_unlock();
--
2.20.1

2021-07-22 20:31:27

by Ben Greear

[permalink] [raw]
Subject: [PATCH v2 09/11] mt76: mt7915: txfree status to show txcount instead of latency.

From: Ben Greear <[email protected]>

Latency is not obviously that useful, but txcount can let us deduce
retries, which may be more interesting.

Signed-off-by: Ben Greear <[email protected]>
---
drivers/net/wireless/mediatek/mt76/mt7915/init.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7915/mac.h | 8 +++++---
drivers/net/wireless/mediatek/mt76/mt7915/regs.h | 4 ++++
3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index f174cf219724..a0d282771d77 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -304,6 +304,9 @@ static void mt7915_mac_init(struct mt7915_dev *dev)
/* enable hardware de-agg */
mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN);

+ /* disable Tx latency report to enable Tx count in txfree path */
+ mt76_clear(dev, MT_PLE_HOST_RPT0, MT_PLE_HOST_RPT0_TX_LATENCY);
+
for (i = 0; i < MT7915_WTBL_SIZE; i++)
mt7915_mac_wtbl_update(dev, i,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
index 986a65badd1b..741d51b0fa06 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
@@ -304,13 +304,15 @@ struct mt7915_tx_free {

#define MT_TX_FREE_MSDU_CNT GENMASK(9, 0)
#define MT_TX_FREE_WLAN_ID GENMASK(23, 14)
+/* when configured for txfree latency mode. See MT_PLE_HOST_RPT0_TX_LATENCY
+ * Not enabled by default now.
+ */
#define MT_TX_FREE_LATENCY GENMASK(12, 0)
-/* 0: success, others: dropped */
+/* when configured for txcount mode. See MT_PLE_HOST_RPT0_TX_LATENCY. */
+#define MT_TX_FREE_TXCNT GENMASK(12, 0)
#define MT_TX_FREE_STATUS GENMASK(14, 13)
#define MT_TX_FREE_MSDU_ID GENMASK(30, 16)
#define MT_TX_FREE_PAIR BIT(31)
-/* will support this field in further revision */
-#define MT_TX_FREE_RATE GENMASK(13, 0)

#define MT_TXS0_FIXED_RATE BIT(31)
#define MT_TXS0_BW GENMASK(30, 29)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
index 62cc32a098fc..ac4d233b8cf2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
@@ -22,6 +22,10 @@
#define MT_PLE_BASE 0x8000
#define MT_PLE(ofs) (MT_PLE_BASE + (ofs))

+/* Modify whether txfree struct returns latency or txcount. */
+#define MT_PLE_HOST_RPT0 MT_PLE(0x030)
+#define MT_PLE_HOST_RPT0_TX_LATENCY BIT(3)
+
#define MT_PLE_FL_Q0_CTRL MT_PLE(0x1b0)
#define MT_PLE_FL_Q1_CTRL MT_PLE(0x1b4)
#define MT_PLE_FL_Q2_CTRL MT_PLE(0x1b8)
--
2.20.1