mt76: mt76x02: fix handling MCU timeouts during hw restart
authorFelix Fietkau <nbd@nbd.name>
Sun, 16 Feb 2020 15:08:58 +0000 (16:08 +0100)
committerFelix Fietkau <nbd@nbd.name>
Tue, 12 May 2020 17:52:27 +0000 (19:52 +0200)
If a MCU timeout occurs before a hw restart completes, another hw restart
is scheduled, and the station state gets corrupted.
To speed up dealing with that, do not issue any MCU commands after the first
timeout, and defer handling timeouts until the reset has completed.
Also ignore errors in MCU commands during start/config to avoid making user
space fail on this condition. If it happens, another restart is scheduled
quickly, and that usually recovers the hardware properly.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
drivers/net/wireless/mediatek/mt76/mt76x02.h
drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c
drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c

index 8e4759bc8f59deaa079ed4ec45f0ca95e26c274f..6d60187e88ede99b5ca950ec86d0959b84032504 100644 (file)
@@ -275,6 +275,7 @@ enum {
        MT76_STATE_RUNNING,
        MT76_STATE_MCU_RUNNING,
        MT76_SCANNING,
+       MT76_RESTART,
        MT76_RESET,
        MT76_MCU_RESET,
        MT76_REMOVED,
index 0b520ae08d018a65610b36bd2411ab7296e52373..57091d41eb851bca21146f74b46c5c4af78e7e14 100644 (file)
@@ -29,6 +29,7 @@ static void mt76x0e_stop_hw(struct mt76x02_dev *dev)
 {
        cancel_delayed_work_sync(&dev->cal_work);
        cancel_delayed_work_sync(&dev->mt76.mac_work);
+       clear_bit(MT76_RESTART, &dev->mphy.state);
 
        if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY,
                       0, 1000))
@@ -83,6 +84,7 @@ static const struct ieee80211_ops mt76x0e_ops = {
        .set_coverage_class = mt76x02_set_coverage_class,
        .set_rts_threshold = mt76x02_set_rts_threshold,
        .get_antenna = mt76_get_antenna,
+       .reconfig_complete = mt76x02_reconfig_complete,
 };
 
 static int mt76x0e_register_device(struct mt76x02_dev *dev)
index 23040c193ca54de6248b1b9dab249c0edcd37c5c..94dd142cb3d7626dbcb8895d2df63c24c8de9063 100644 (file)
@@ -187,6 +187,8 @@ void mt76x02_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps);
 void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              struct ieee80211_bss_conf *info, u32 changed);
+void mt76x02_reconfig_complete(struct ieee80211_hw *hw,
+                              enum ieee80211_reconfig_type reconfig_type);
 
 struct beacon_bc_data {
        struct mt76x02_dev *dev;
index 5664749ad6c1aaebcdbbe0529fe1c0a7b8a1c3b7..8247611d9b18b517f1b0d6e15bdd7ef763772f69 100644 (file)
@@ -20,6 +20,9 @@ int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data,
        int ret;
        u8 seq;
 
+       if (mt76_is_mmio(&dev->mt76) && dev->mcu_timeout)
+               return -EIO;
+
        skb = mt76x02_mcu_msg_alloc(data, len);
        if (!skb)
                return -ENOMEM;
index 7dcc5d342e9f5f490ef26da85bc0de56456e0941..7e389dbccfeb8b3dc755a7d9722c552cf83620ed 100644 (file)
@@ -520,6 +520,7 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
        }
 
        if (restart) {
+               set_bit(MT76_RESTART, &dev->mphy.state);
                mt76x02_mcu_function_select(dev, Q_SELECT, 1);
                ieee80211_restart_hw(dev->mt76.hw);
        } else {
@@ -528,8 +529,23 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev)
        }
 }
 
+void mt76x02_reconfig_complete(struct ieee80211_hw *hw,
+                              enum ieee80211_reconfig_type reconfig_type)
+{
+       struct mt76x02_dev *dev = hw->priv;
+
+       if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
+               return;
+
+       clear_bit(MT76_RESTART, &dev->mphy.state);
+}
+EXPORT_SYMBOL_GPL(mt76x02_reconfig_complete);
+
 static void mt76x02_check_tx_hang(struct mt76x02_dev *dev)
 {
+       if (test_bit(MT76_RESTART, &dev->mphy.state))
+               return;
+
        if (mt76x02_tx_hang(dev)) {
                if (++dev->tx_hang_check >= MT_TX_HANG_TH)
                        goto restart;
index c69579e5f6479044014cc3ed7c4fd3788465564e..f27774f574382d6a5c05edeb0a002c4a21301e64 100644 (file)
@@ -256,6 +256,7 @@ void mt76x2_stop_hardware(struct mt76x02_dev *dev)
        cancel_delayed_work_sync(&dev->cal_work);
        cancel_delayed_work_sync(&dev->mt76.mac_work);
        cancel_delayed_work_sync(&dev->wdt_work);
+       clear_bit(MT76_RESTART, &dev->mphy.state);
        mt76x02_mcu_set_radio_state(dev, false);
        mt76x2_mac_stop(dev, false);
 }
index 105e5b99b3f9b56ad1035d39bf980f7d0b0fd571..a74599f7f729f77262994be7b542f5c887441d9f 100644 (file)
@@ -10,12 +10,9 @@ static int
 mt76x2_start(struct ieee80211_hw *hw)
 {
        struct mt76x02_dev *dev = hw->priv;
-       int ret;
 
        mt76x02_mac_start(dev);
-       ret = mt76x2_phy_start(dev);
-       if (ret)
-               return ret;
+       mt76x2_phy_start(dev);
 
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
                                     MT_MAC_WORK_INTERVAL);
@@ -35,11 +32,9 @@ mt76x2_stop(struct ieee80211_hw *hw)
        mt76x2_stop_hardware(dev);
 }
 
-static int
+static void
 mt76x2_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
 {
-       int ret;
-
        cancel_delayed_work_sync(&dev->cal_work);
        tasklet_disable(&dev->mt76.pre_tbtt_tasklet);
        tasklet_disable(&dev->dfs_pd.dfs_tasklet);
@@ -50,7 +45,7 @@ mt76x2_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
        mt76_set_channel(&dev->mphy);
 
        mt76x2_mac_stop(dev, true);
-       ret = mt76x2_phy_set_channel(dev, chandef);
+       mt76x2_phy_set_channel(dev, chandef);
 
        mt76x02_mac_cc_reset(dev);
        mt76x02_dfs_init_params(dev);
@@ -64,15 +59,12 @@ mt76x2_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
        tasklet_enable(&dev->mt76.pre_tbtt_tasklet);
 
        mt76_txq_schedule_all(&dev->mphy);
-
-       return ret;
 }
 
 static int
 mt76x2_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct mt76x02_dev *dev = hw->priv;
-       int ret = 0;
 
        mutex_lock(&dev->mt76.mutex);
 
@@ -101,11 +93,11 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed)
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                ieee80211_stop_queues(hw);
-               ret = mt76x2_set_channel(dev, &hw->conf.chandef);
+               mt76x2_set_channel(dev, &hw->conf.chandef);
                ieee80211_wake_queues(hw);
        }
 
-       return ret;
+       return 0;
 }
 
 static void
@@ -162,5 +154,6 @@ const struct ieee80211_ops mt76x2_ops = {
        .set_antenna = mt76x2_set_antenna,
        .get_antenna = mt76_get_antenna,
        .set_rts_threshold = mt76x02_set_rts_threshold,
+       .reconfig_complete = mt76x02_reconfig_complete,
 };