During device shutdown or removal, a deadlock can occur between the PCIe remove path and the driver's asynchronous reset work. The unregistration path calls napi_disable() before cancelling the reset work. If the reset work runs concurrently, it may re-enable NAPI and schedule it. Because the device is being unregistered, this can lead to NAPI state corruption where NAPI is marked as scheduled but never polled, causing subsequent napi_disable() calls to hang forever. Fix this by: 1. Moving cancel_work_sync(&dev->reset_work) to the very start of mt7925e_unregister_device(), ensuring it is stopped before NAPI is disabled. 2. Setting the MT76_REMOVED flag early in the PCI remove path to prevent new reset work from being queued. 3. Checking MT76_REMOVED in mt7925_mac_reset_work() and aborting the reset early if the device is being removed. Co-developed-by: Fei Shao Signed-off-by: JB Tsai --- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 7 +++++++ drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c index 0641a7131d7c..1c6d43b162c0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -1319,6 +1319,9 @@ void mt7925_mac_reset_work(struct work_struct *work) cancel_work_sync(&pm->wake_work); for (i = 0; i < 10; i++) { + if (test_bit(MT76_REMOVED, &dev->mt76.phy.state)) + goto out; + mutex_lock(&dev->mt76.mutex); ret = mt792x_dev_reset(dev); mutex_unlock(&dev->mt76.mutex); @@ -1338,8 +1341,12 @@ void mt7925_mac_reset_work(struct work_struct *work) ieee80211_scan_completed(dev->mphy.hw, &info); } +out: dev->hw_full_reset = false; pm->suspended = false; + if (test_bit(MT76_REMOVED, &dev->mt76.phy.state)) + return; + ieee80211_wake_queues(hw); ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index ea64303283ed..978a3f08e080 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -43,13 +43,13 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev) if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) wiphy_rfkill_stop_polling(hw->wiphy); + cancel_work_sync(&dev->reset_work); cancel_work_sync(&dev->init_work); mt76_unregister_device(&dev->mt76); mt76_for_each_q_rx(&dev->mt76, i) napi_disable(&dev->mt76.napi[i]); cancel_delayed_work_sync(&pm->ps_work); cancel_work_sync(&pm->wake_work); - cancel_work_sync(&dev->reset_work); mt7925_tx_token_put(dev); __mt792x_mcu_drv_pmctrl(dev); @@ -498,8 +498,8 @@ static void mt7925_pci_remove(struct pci_dev *pdev) struct mt76_dev *mdev = pci_get_drvdata(pdev); struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); - mt7925e_unregister_device(dev); set_bit(MT76_REMOVED, &mdev->phy.state); + mt7925e_unregister_device(dev); devm_free_irq(&pdev->dev, pdev->irq, dev); mt76_free_device(&dev->mt76); pci_free_irq_vectors(pdev); -- 2.45.2