From 5ecfdb1a6e0a310caed741bd6e161f10789ba00c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 16 Feb 2020 16:08:58 +0100 Subject: [PATCH] mt76: mt76x02: fix handling MCU timeouts during hw restart 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 --- mt76.h | 1 + mt76x0/pci.c | 2 ++ mt76x02.h | 2 ++ mt76x02_mcu.c | 3 +++ mt76x02_mmio.c | 16 ++++++++++++++++ mt76x2/pci_init.c | 1 + mt76x2/pci_main.c | 19 ++++++------------- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/mt76.h b/mt76.h index 9f7bcbeb..f820c8d2 100644 --- a/mt76.h +++ b/mt76.h @@ -266,6 +266,7 @@ enum { MT76_STATE_RUNNING, MT76_STATE_MCU_RUNNING, MT76_SCANNING, + MT76_RESTART, MT76_RESET, MT76_REMOVED, MT76_READING_STATS, diff --git a/mt76x0/pci.c b/mt76x0/pci.c index 922fcbfd..7ee6e1ac 100644 --- a/mt76x0/pci.c +++ b/mt76x0/pci.c @@ -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->mt76.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) diff --git a/mt76x02.h b/mt76x02.h index 0ca0bbfe..d79b27ff 100644 --- a/mt76x02.h +++ b/mt76x02.h @@ -182,6 +182,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; diff --git a/mt76x02_mcu.c b/mt76x02_mcu.c index 6274b6a2..848ed906 100644 --- a/mt76x02_mcu.c +++ b/mt76x02_mcu.c @@ -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; diff --git a/mt76x02_mmio.c b/mt76x02_mmio.c index c7324bb8..659ea48b 100644 --- a/mt76x02_mmio.c +++ b/mt76x02_mmio.c @@ -511,6 +511,7 @@ static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) } if (restart) { + set_bit(MT76_RESTART, &dev->mt76.state); mt76x02_mcu_function_select(dev, Q_SELECT, 1); ieee80211_restart_hw(dev->mt76.hw); } else { @@ -519,8 +520,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->mt76.state); +} +EXPORT_SYMBOL_GPL(mt76x02_reconfig_complete); + static void mt76x02_check_tx_hang(struct mt76x02_dev *dev) { + if (test_bit(MT76_RESTART, &dev->mt76.state)) + return; + if (mt76x02_tx_hang(dev)) { if (++dev->tx_hang_check >= MT_TX_HANG_TH) goto restart; diff --git a/mt76x2/pci_init.c b/mt76x2/pci_init.c index 33fcec91..ae08c19d 100644 --- a/mt76x2/pci_init.c +++ b/mt76x2/pci_init.c @@ -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->mt76.state); mt76x02_mcu_set_radio_state(dev, false); mt76x2_mac_stop(dev, false); } diff --git a/mt76x2/pci_main.c b/mt76x2/pci_main.c index f2beb717..446047bd 100644 --- a/mt76x2/pci_main.c +++ b/mt76x2/pci_main.c @@ -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->mt76); 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->mt76); - - 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, };