]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mt76: mt7996: improve hardware restart reliability
authorFelix Fietkau <nbd@nbd.name>
Mon, 15 Sep 2025 07:58:57 +0000 (09:58 +0200)
committerFelix Fietkau <nbd@nbd.name>
Mon, 15 Sep 2025 11:23:01 +0000 (13:23 +0200)
Port latest version of similar changes from mt7915:
- use reconfig_complete to restart mac_work / queues
- clear wcid and vif mask to avoid leak
- fix sta poll list corruption
- reset station links
- reset interface links
- clear rro list

Link: https://patch.msgid.link/20250915075910.47558-2-nbd@nbd.name
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7996/mac.c
drivers/net/wireless/mediatek/mt76/mt7996/main.c
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h

index f6c725f52c4a5c8d1a141e52c9f0ecef5fd075df..fd57569a7da7cb6a1f6d176a4fbba9a9a8537528 100644 (file)
@@ -2355,11 +2355,59 @@ out:
        return ret;
 }
 
+static void
+mt7996_mac_reset_sta_iter(void *data, struct ieee80211_sta *sta)
+{
+       struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+       struct mt7996_dev *dev = data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(msta->link); i++) {
+               struct mt7996_sta_link *msta_link = NULL;
+
+               msta_link = rcu_replace_pointer(msta->link[i], msta_link,
+                                               lockdep_is_held(&dev->mt76.mutex));
+               if (!msta_link)
+                       continue;
+
+               mt7996_mac_sta_deinit_link(dev, msta_link);
+
+               if (msta->deflink_id == i) {
+                       msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+                       continue;
+               }
+
+               kfree_rcu(msta_link, rcu_head);
+       }
+}
+
+static void
+mt7996_mac_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv;
+       struct mt76_vif_data *mvif = mlink->mvif;
+       struct mt7996_dev *dev = data;
+       int i;
+
+       rcu_read_lock();
+       for (i = 0; i < ARRAY_SIZE(mvif->link); i++) {
+
+               mlink = mt76_dereference(mvif->link[i], &dev->mt76);
+               if (!mlink || mlink == (struct mt76_vif_link *)vif->drv_priv)
+                       continue;
+
+               rcu_assign_pointer(mvif->link[i], NULL);
+               kfree_rcu(mlink, rcu_head);
+       }
+       rcu_read_unlock();
+}
+
 static void
 mt7996_mac_full_reset(struct mt7996_dev *dev)
 {
        struct ieee80211_hw *hw = mt76_hw(dev);
        struct mt7996_phy *phy;
+       LIST_HEAD(list);
        int i;
 
        dev->recovery.hw_full_reset = true;
@@ -2376,18 +2424,42 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
                if (!mt7996_mac_restart(dev))
                        break;
        }
-       mutex_unlock(&dev->mt76.mutex);
 
        if (i == 10)
                dev_err(dev->mt76.dev, "chip full reset failed\n");
 
-       ieee80211_restart_hw(mt76_hw(dev));
-       ieee80211_wake_queues(mt76_hw(dev));
+       mt7996_for_each_phy(dev, phy)
+               phy->omac_mask = 0;
+
+       ieee80211_iterate_stations_atomic(hw, mt7996_mac_reset_sta_iter, dev);
+       ieee80211_iterate_active_interfaces_atomic(hw,
+                                                  IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER,
+                                                  mt7996_mac_reset_vif_iter, dev);
+       mt76_reset_device(&dev->mt76);
+
+       INIT_LIST_HEAD(&dev->sta_rc_list);
+       INIT_LIST_HEAD(&dev->twt_list);
+
+       spin_lock_bh(&dev->wed_rro.lock);
+       list_splice_init(&dev->wed_rro.poll_list, &list);
+       spin_unlock_bh(&dev->wed_rro.lock);
 
+       while (!list_empty(&list)) {
+               struct mt7996_wed_rro_session_id *e;
+
+               e = list_first_entry(&list, struct mt7996_wed_rro_session_id,
+                                    list);
+               list_del_init(&e->list);
+               kfree(e);
+       }
+
+       i = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+       dev->mt76.global_wcid.idx = i;
        dev->recovery.hw_full_reset = false;
-       mt7996_for_each_phy(dev, phy)
-               ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
-                                            MT7996_WATCHDOG_TIME);
+
+       mutex_unlock(&dev->mt76.mutex);
+
+       ieee80211_restart_hw(mt76_hw(dev));
 }
 
 void mt7996_mac_reset_work(struct work_struct *work)
index 6adb7f7bdd6f26501f312e9abb28cf572a8583b7..8fbe607230715d19cee9ebfd3d5556780de45e75 100644 (file)
@@ -986,13 +986,9 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
        return 0;
 }
 
-static void
-mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
-                          struct mt7996_sta_link *msta_link)
+void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
+                               struct mt7996_sta_link *msta_link)
 {
-       mt7996_mac_wtbl_update(dev, msta_link->wcid.idx,
-                              MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
-
        spin_lock_bh(&dev->mt76.sta_poll_lock);
        if (!list_empty(&msta_link->wcid.poll_list))
                list_del_init(&msta_link->wcid.poll_list);
@@ -1022,6 +1018,9 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
                if (!msta_link)
                        continue;
 
+               mt7996_mac_wtbl_update(dev, msta_link->wcid.idx,
+                                      MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
                mt7996_mac_sta_deinit_link(dev, msta_link);
                link = mt7996_vif_link(dev, vif, link_id);
                if (!link)
@@ -2206,6 +2205,19 @@ mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        return 0;
 }
 
+static void
+mt7996_reconfig_complete(struct ieee80211_hw *hw,
+                        enum ieee80211_reconfig_type reconfig_type)
+{
+       struct mt7996_dev *dev = mt7996_hw_dev(hw);
+       struct mt7996_phy *phy;
+
+       ieee80211_wake_queues(hw);
+       mt7996_for_each_phy(dev, phy)
+               ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+                                            MT7996_WATCHDOG_TIME);
+}
+
 const struct ieee80211_ops mt7996_ops = {
        .add_chanctx = mt76_add_chanctx,
        .remove_chanctx = mt76_remove_chanctx,
@@ -2264,4 +2276,5 @@ const struct ieee80211_ops mt7996_ops = {
 #endif
        .change_vif_links = mt7996_change_vif_links,
        .change_sta_links = mt7996_mac_sta_change_links,
+       .reconfig_complete = mt7996_reconfig_complete,
 };
index be26e04b7da774f02cd7c3cb38d83b5397c3b18a..fa10aa6f517a97d5d74c57b0520c2c9145ea8c25 100644 (file)
@@ -817,6 +817,8 @@ void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
                                  struct mt7996_vif_link *link,
                                  struct mt7996_sta_link *msta_link,
                                  u8 flowid);
+void mt7996_mac_sta_deinit_link(struct mt7996_dev *dev,
+                               struct mt7996_sta_link *msta_link);
 void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
                              struct ieee80211_sta *sta,
                              struct ieee80211_twt_setup *twt);