mt7996_sta_statistics() and mt7996_ethtool_worker() read the TX rate, the TX counters and the ACK signal from msta->deflink. That is the msta_link of the first link of the station, not of its primary link. When the first link of an MLD station is removed, msta->deflink keeps the values of the removed link, and the station reports them until it is removed. Read them from the link that deflink_id selects. msta->deflink also holds the RX reorder state, the TX aggregation state and the def_wcid target of all links. These are state of the whole station, and wcid_to_sta() needs def_wcid at the start of drv_priv, so they stay there. Fixes: f520eceacd24 ("wifi: mt76: mt7996: Add mt7996_mac_sta_change_links callback") Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/main.c | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f94b5fdfca24..f44af31281b7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1920,9 +1920,16 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; - struct rate_info *txrate = &msta_link->wcid.rate; + struct mt7996_sta_link *msta_link; + struct rate_info *txrate; + rcu_read_lock(); + + msta_link = mt7996_sta_link(msta, msta->deflink_id); + if (!msta_link) + goto out; + + txrate = &msta_link->wcid.rate; if (txrate->legacy || txrate->flags) { if (txrate->legacy) { sinfo->txrate.legacy = txrate->legacy; @@ -1965,6 +1972,9 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, sinfo->rx_packets = msta_link->wcid.stats.rx_packets; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); } + +out: + rcu_read_unlock(); } static void mt7996_link_rate_ctrl_update(void *data, @@ -2264,11 +2274,15 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) { struct mt76_ethtool_worker_info *wi = wi_data; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_sta_link *msta_link; if (msta->vif->deflink.mt76.idx != wi->idx) return; + msta_link = mt7996_sta_link(msta, msta->deflink_id); + if (!msta_link) + return; + mt76_ethtool_worker(wi, &msta_link->wcid.stats, true); } -- 2.47.3