]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mt76: mt7925: host-only unwind published links on add failure
authorSean Wang <sean.wang@mediatek.com>
Fri, 6 Mar 2026 23:22:38 +0000 (17:22 -0600)
committerFelix Fietkau <nbd@nbd.name>
Tue, 24 Mar 2026 15:49:32 +0000 (15:49 +0000)
Release host link resources when mt7925_mac_sta_add_links() fails after
partial success.

msta->link[] and dev->mt76.wcid[] may already be published, so unwind
the host state to avoid leaving stale links behind.

Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Link: https://patch.msgid.link/20260306232238.2039675-20-sean.wang@kernel.org
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7925/main.c

index 95c631b57894d38c17e493480aedbec747a6a7a1..73d3722739d0a8f28ecda7987f750a5e1ca048f2 100644 (file)
@@ -989,11 +989,51 @@ out_wcid:
        return ret;
 }
 
+/*
+ * Host-only unwind for sta_add_links() failures.
+ *
+ * If add_links fail due to MCU/firmware timeouts; calling the full remove
+ * path would send more firmware commands and may hang again. So only rollback
+ * host-published state here (msta->link/valid_links, dev->mt76.wcid[idx]) and
+ * free mlink objects (RCU-safe). Firmware state is left for reset/recovery.
+ */
+static void
+mt7925_mac_sta_unwind_links_host(struct mt792x_dev *dev,
+                                struct ieee80211_sta *sta,
+                                unsigned long links)
+{
+       struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       unsigned int link_id;
+
+       for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+               struct mt792x_link_sta *mlink;
+               u16 idx;
+
+               mlink = rcu_replace_pointer(msta->link[link_id], NULL,
+                                           lockdep_is_held(&dev->mt76.mutex));
+               if (!mlink)
+                       continue;
+
+               msta->valid_links &= ~BIT(link_id);
+               if (msta->deflink_id == link_id)
+                       msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+
+               idx = mlink->wcid.idx;
+               rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+               mt76_wcid_cleanup(&dev->mt76, &mlink->wcid);
+               mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
+
+               if (mlink != &msta->deflink)
+                       kfree_rcu(mlink, rcu_head);
+       }
+}
+
 static int
 mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
                         struct ieee80211_sta *sta, unsigned long new_links)
 {
        struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+       unsigned long added_links = 0;
        unsigned int link_id;
        int err = 0;
 
@@ -1030,8 +1070,13 @@ mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
 
                rcu_assign_pointer(msta->link[link_id], mlink);
                msta->valid_links |= BIT(link_id);
+
+               added_links |= BIT(link_id);
        }
 
+       if (err && added_links)
+               mt7925_mac_sta_unwind_links_host(dev, sta, added_links);
+
        return err;
 }