mt7996_mac_sta_init_link() puts a new link in msta->deflink whenever the station has no primary link. A removed link keeps its msta_link until the station is removed, and the station has no primary link after one mt7996_mac_sta_change_links() call removes all of its links and adds others. If the removed primary link was in msta->deflink, the new link then overwrites it: both link slots point to the same msta_link, and the WTBL index of the removed link is lost, so mt7996_mac_sta_remove() never returns it to wcid_mask. Use msta->deflink only if no link slot of the station points to it, and allocate the msta_link otherwise. Choose the primary link as before. Fixes: e8c819df0243 ("wifi: mt76: mt7996: Destroy active sta links in mt7996_mac_sta_remove()") Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7996/main.c | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 8f483b1de852..8e70a4061ed6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1143,6 +1143,20 @@ mt7996_sta_deflink_set(struct ieee80211_sta *sta, unsigned int link_id, mt7996_sta_init_txq_wcid(sta, idx); } +static bool +mt7996_sta_deflink_busy(struct mt7996_dev *dev, struct mt7996_sta *msta) +{ + unsigned int link_id; + + for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) { + if (mt7996_sta_link_protected(dev, msta, link_id) == + &msta->deflink) + return true; + } + + return false; +} + static void mt7996_sta_deflink_release(struct mt7996_dev *dev, struct ieee80211_sta *sta, unsigned int link_id) @@ -1210,17 +1224,19 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev, if (idx < 0) return -ENOSPC; - if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) { - msta_link = &msta->deflink; - mt7996_sta_deflink_set(sta, link_id, idx); - } else { + if (mt7996_sta_deflink_busy(dev, msta)) { msta_link = kzalloc_obj(*msta_link); if (!msta_link) { mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx); return -ENOMEM; } + } else { + msta_link = &msta->deflink; } + if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) + mt7996_sta_deflink_set(sta, link_id, idx); + INIT_LIST_HEAD(&msta_link->rc_list); INIT_LIST_HEAD(&msta_link->wcid.poll_list); msta_link->sta = msta; -- 2.47.3