diff options
| author | Felix Fietkau <nbd@nbd.name> | 2026-10-06 22:59:19 +0200 |
|---|---|---|
| committer | Felix Fietkau <nbd@nbd.name> | 2026-10-07 12:50:43 +0200 |
| commit | a53699a3d482a980efb016469b99ffac172280af (patch) | |
| tree | 4471a63b295f3ea05a8bf462e9dbd6ffbe987ff7 /drivers/net | |
| parent | a9792846fbc62af2290552ab66b5a855415acaa8 (diff) | |
| download | linux-a53699a3d482a980efb016469b99ffac172280af.tar.gz linux-a53699a3d482a980efb016469b99ffac172280af.zip | |
wifi: mt76: mt7996: clear all link wcid entries in sta_pre_rcu_remove
mt76_sta_pre_rcu_remove() clears the wcid table entry of msta->deflink
only. mac80211 calls it before the synchronize_net() that ends the RCU
readers of the station, and frees the station right after
mt7996_mac_sta_remove(). For an MLD station, the wcid table entries of
the other links stay set until mt7996_mac_sta_remove(), after that
grace period. An RX or TX status reader that looks up one of these
entries in that window can follow msta_link->sta or def_wcid into the
freed station.
Clear the wcid table entries of all links of the station in
.sta_pre_rcu_remove.
Fixes: dd82a9e02c05 ("wifi: mt76: mt7996: Rely on mt7996_sta_link in sta_add/sta_remove callbacks")
Link: https://patch.msgid.link/20261006205936.669125-10-nbd@nbd.name
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'drivers/net')
| -rw-r--r-- | drivers/net/wireless/mediatek/mt76/mt7996/main.c | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index f44af31281b7..3c27cc02d527 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -1513,6 +1513,30 @@ mt7996_set_active_links(struct ieee80211_vif *vif) ieee80211_set_active_links_async(vif, active_links); } +static void +mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + unsigned int link_id; + + mutex_lock(&dev->mt76.mutex); + spin_lock_bh(&dev->mt76.status_lock); + + for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) { + struct mt7996_sta_link *msta_link; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (msta_link) + rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx], + NULL); + } + + spin_unlock_bh(&dev->mt76.status_lock); + mutex_unlock(&dev->mt76.mutex); +} + static int mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, @@ -2618,7 +2642,7 @@ const struct ieee80211_ops mt7996_ops = { .vif_cfg_changed = mt7996_vif_cfg_changed, .link_info_changed = mt7996_link_info_changed, .sta_state = mt7996_sta_state, - .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .sta_pre_rcu_remove = mt7996_sta_pre_rcu_remove, .link_sta_rc_update = mt7996_link_sta_rc_update, .set_key = mt7996_set_key, .ampdu_action = mt7996_ampdu_action, |
