[PATCH v2] wifi: mt76: mt7925: keep MLD membership consistent during link add
Andrei Rusu de Castro
arc at empyreal.works
Sun Oct 4 08:49:02 PDT 2026
mt7925_mac_link_sta_add() sends an ASSOC update for the primary WCID
before sending the update for a new secondary WCID. The new link is
published in msta->link[] and msta->valid_links only after both commands
succeed.
Selecting the secondary entry from the subject of each command gives
the primary STA_REC_MLD one link and the secondary STA_REC_MLD two
links. Enumerating published links alone still omits the pending link
from the primary command. Firmware-bound command captures reproduce
this n=1/n=2 sequence during a secondary addition.
Build each MLD TLV from the station's published links and an explicit
pending link. Pass the initialized pending link through both add-time
station updates, including the update whose subject is the primary.
Keep the primary first, bound entries by the firmware array, and skip
links without station and BSS state. Other update callers have no
pending link.
This leaves msta->link[] publication after successful link setup and
preserves the existing add-failure cleanup. The pending pointer is used
synchronously to populate the command, not stored in shared state.
Fixes: ff643b81bc38 ("wifi: mt76: mt7925: pass mlink and mconf to sta_mld_tlv()")
Link: https://lore.kernel.org/linux-wireless/066b30cc-a9e6-4aeb-964d-71551e8ea3ef@posteo.de/
Signed-off-by: Andrei Rusu de Castro <arc at empyreal.works>
---
Changes since v1:
- Carry an explicit initialized pending link through both add-time updates,
not just the update whose subject is the pending secondary.
- Preserve msta->link[] publication after successful setup.
- Describe consistency between per-WCID commands without assuming a
shared firmware record overwritten by the last command.
V1: https://lore.kernel.org/all/20260902-mt7925-0cbea623@empyreal.works/
Tested on MT7925 PCIe, Linux 7.3-rc5, firmware 20260813113118, ASUS
GT-BE98 advertising three links with a 5+6 GHz active pair. The in-tree
MLD path stalled after 157 seconds, v1 after 289 and 150 seconds. V2
passed a 48-sample, 813-second scan/traffic run; full-band scans returned
to 6.3-7.1 seconds from about 27 seconds. A second clean boot did not
stall through 812 seconds and 760 seconds of bilateral 20 Mbit/s traffic.
One gateway ping timed out while same-sample IP/HTTPS passed, so that
second run did not pass the strict zero-failure gate.
The local deployment variant, with separate retained safety guards,
passed the same gate on two PCIe Z13 machines (813 and 810 seconds).
Both then passed ordinary default boots. The scheduled-scan withdrawal
and independent WM2 reset correction were retained throughout testing.
USB hardware and the reporter's FritzBox setup remain untested here.
W=1/-Werror builds of changed objects pass on both rc5 and the mt76 base
below. Source-extracted fixtures under ASan/UBSan cover pending-link
encoding and host cleanup at eight failed BSS/STA command positions;
they do not model firmware rollback. Direct partial-link switches and
reset aggregation warnings also fail on the old full-revert baseline
and are not claimed fixed by this patch.
.../net/wireless/mediatek/mt76/mt7925/mac.c | 2 +-
.../net/wireless/mediatek/mt76/mt7925/main.c | 16 ++---
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 59 +++++++++++++++----
.../wireless/mediatek/mt76/mt7925/mt7925.h | 3 +-
4 files changed, 60 insertions(+), 20 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 101f571b027f..cbc18dbcbad9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1502,7 +1502,7 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,
true, NULL);
mt7925_mcu_sta_update(dev, NULL, vif,
&mvif->sta.deflink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true);
}
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index c882952f5df1..f536fffffa08 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1006,7 +1006,7 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
link_sta == mlink->pri_link) {
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
if (ret)
goto out_pm;
} else if (ieee80211_vif_is_mld(vif) &&
@@ -1028,19 +1028,19 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
pri_mlink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, mlink);
if (ret)
goto out_pm;
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, mlink);
if (ret)
goto out_pm;
} else {
ret = mt7925_mcu_sta_update(dev, link_sta, vif,
mlink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
if (ret)
goto out_pm;
}
@@ -1248,7 +1248,7 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac));
mt7925_mcu_sta_update(dev, link_sta, vif, mlink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, NULL);
mt792x_mutex_release(dev);
}
@@ -1308,7 +1308,7 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
mt76_connac_pm_wake(&dev->mphy, &dev->pm);
mt7925_mcu_sta_update(dev, link_sta, vif, mlink, false,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
mt7925_mac_wtbl_update(dev, mlink->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -1979,7 +1979,7 @@ mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
err = mt7925_mcu_sta_update(dev, NULL, vif,
&mvif->sta.deflink, true,
- MT76_STA_INFO_STATE_NONE);
+ MT76_STA_INFO_STATE_NONE, NULL);
out:
mt792x_mutex_release(dev);
@@ -2123,7 +2123,7 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC) {
mt7925_mcu_sta_update(dev, NULL, vif,
&mvif->sta.deflink, true,
- MT76_STA_INFO_STATE_ASSOC);
+ MT76_STA_INFO_STATE_ASSOC, NULL);
mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc);
if (ieee80211_vif_is_mld(vif))
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 2afd3f5e3266..634062730e65 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -2065,14 +2065,18 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct mt792x_bss_conf *mconf,
- struct mt792x_link_sta *mlink)
+ struct mt792x_link_sta *mlink,
+ struct mt792x_link_sta *pending)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
struct mt792x_dev *dev = mvif->phy->dev;
+ unsigned long valid = msta->valid_links;
struct mt792x_bss_conf *mconf_pri;
struct sta_rec_mld *mld;
+ unsigned int link_id;
struct tlv *tlv;
+ u8 max_links;
u8 cnt = 0;
/* Primary link always uses driver's deflink WCID. */
@@ -2101,11 +2105,44 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
mld->link[cnt].wlan_id = cpu_to_le16(msta->deflink.wcid.idx);
mld->link[cnt++].bss_idx = mconf_pri->mt76.idx;
- /* Optionally encode the currently-updated secondary link. */
- if (mlink && mlink != &msta->deflink && mconf) {
- mld->secondary_id = cpu_to_le16(mlink->wcid.idx);
- mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx);
- mld->link[cnt++].bss_idx = mconf->mt76.idx;
+ /* Describe the same station links in each per-WCID STA_REC_MLD,
+ * rather than selecting the secondary from the current command.
+ */
+ max_links = ARRAY_SIZE(mld->link);
+
+ /* Adding a secondary link updates both primary and secondary STA
+ * records before publishing the new link in msta->link[]. Include it
+ * in both commands without moving that publication before success.
+ */
+ if (pending && pending != &msta->deflink)
+ valid |= BIT(pending->wcid.link_id);
+
+ for_each_set_bit(link_id, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_link_sta *mlink_sec;
+ struct mt792x_bss_conf *mconf_sec;
+
+ if (cnt == max_links)
+ break;
+
+ if (link_id == msta->deflink_id)
+ continue;
+
+ mlink_sec = mt792x_sta_to_link(msta, link_id);
+ if (!mlink_sec && pending && link_id == pending->wcid.link_id)
+ mlink_sec = pending;
+ if (!mlink_sec || mlink_sec == &msta->deflink)
+ continue;
+
+ mconf_sec = rcu_dereference_protected(mvif->link_conf[link_id],
+ lockdep_is_held(&dev->mt76.mutex));
+ if (!mconf_sec)
+ continue;
+
+ if (cnt == 1)
+ mld->secondary_id = cpu_to_le16(mlink_sec->wcid.idx);
+
+ mld->link[cnt].wlan_id = cpu_to_le16(mlink_sec->wcid.idx);
+ mld->link[cnt++].bss_idx = mconf_sec->mt76.idx;
}
mld->link_num = cnt;
@@ -2124,7 +2161,8 @@ mt7925_mcu_sta_remove_tlv(struct sk_buff *skb)
static int
mt7925_mcu_sta_cmd(struct mt76_phy *phy,
- struct mt76_sta_cmd_info *info)
+ struct mt76_sta_cmd_info *info,
+ struct mt792x_link_sta *pending)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv;
struct mt76_dev *dev = phy->dev;
@@ -2165,7 +2203,7 @@ mt7925_mcu_sta_cmd(struct mt76_phy *phy,
if (info->state != MT76_STA_INFO_STATE_NONE) {
mt7925_mcu_sta_mld_tlv(skb, info->vif,
info->link_sta->sta,
- mconf, mlink);
+ mconf, mlink, pending);
mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta);
}
@@ -2190,7 +2228,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct mt792x_link_sta *mlink,
bool enable,
- enum mt76_sta_info_state state)
+ enum mt76_sta_info_state state,
+ struct mt792x_link_sta *pending)
{
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
int rssi = -ewma_rssi_read(&mvif->bss_conf.rssi);
@@ -2208,7 +2247,7 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
info.wcid = &mlink->wcid;
info.newly = state != MT76_STA_INFO_STATE_ASSOC;
- return mt7925_mcu_sta_cmd(&dev->mphy, &info);
+ return mt7925_mcu_sta_cmd(&dev->mphy, &info, pending);
}
int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 33782d9ba9ed..49fa94e5cfd9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -286,7 +286,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct ieee80211_vif *vif,
struct mt792x_link_sta *mlink,
bool enable,
- enum mt76_sta_info_state state);
+ enum mt76_sta_info_state state,
+ struct mt792x_link_sta *pending);
int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag);
int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf);
int mt7925_mcu_set_eeprom(struct mt792x_dev *dev);
base-commit: 0dbc9c9fa9b92767c2d556504f38b544cb57a96a
--
2.54.0
More information about the Linux-mediatek
mailing list