mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
wifi: mt76: mt7925: Fix unregister deadlock
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 <[email protected]> Signed-off-by: JB Tsai <[email protected]> Tested-by: Rafael Passos <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Felix Fietkau <[email protected]>
This commit is contained in:
@@ -1525,6 +1525,9 @@ void mt7925_mac_reset_work(struct work_struct *work)
|
|||||||
cancel_work_sync(&pm->wake_work);
|
cancel_work_sync(&pm->wake_work);
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
|
if (test_bit(MT76_REMOVED, &dev->mphy.state))
|
||||||
|
goto out;
|
||||||
|
|
||||||
mutex_lock(&dev->mt76.mutex);
|
mutex_lock(&dev->mt76.mutex);
|
||||||
ret = mt792x_dev_reset(dev);
|
ret = mt792x_dev_reset(dev);
|
||||||
mutex_unlock(&dev->mt76.mutex);
|
mutex_unlock(&dev->mt76.mutex);
|
||||||
@@ -1547,8 +1550,12 @@ void mt7925_mac_reset_work(struct work_struct *work)
|
|||||||
ieee80211_scan_completed(dev->mphy.hw, &info);
|
ieee80211_scan_completed(dev->mphy.hw, &info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
dev->hw_full_reset = false;
|
dev->hw_full_reset = false;
|
||||||
pm->suspended = false;
|
pm->suspended = false;
|
||||||
|
if (test_bit(MT76_REMOVED, &dev->mphy.state))
|
||||||
|
return;
|
||||||
|
|
||||||
ieee80211_wake_queues(hw);
|
ieee80211_wake_queues(hw);
|
||||||
ieee80211_iterate_active_interfaces(hw,
|
ieee80211_iterate_active_interfaces(hw,
|
||||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||||
|
|||||||
+2
-2
@@ -47,13 +47,13 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
|
|||||||
if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
|
if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
|
||||||
wiphy_rfkill_stop_polling(hw->wiphy);
|
wiphy_rfkill_stop_polling(hw->wiphy);
|
||||||
|
|
||||||
|
cancel_work_sync(&dev->reset_work);
|
||||||
cancel_work_sync(&dev->init_work);
|
cancel_work_sync(&dev->init_work);
|
||||||
mt76_unregister_device(&dev->mt76);
|
mt76_unregister_device(&dev->mt76);
|
||||||
mt76_for_each_q_rx(&dev->mt76, i)
|
mt76_for_each_q_rx(&dev->mt76, i)
|
||||||
napi_disable(&dev->mt76.napi[i]);
|
napi_disable(&dev->mt76.napi[i]);
|
||||||
cancel_delayed_work_sync(&pm->ps_work);
|
cancel_delayed_work_sync(&pm->ps_work);
|
||||||
cancel_work_sync(&pm->wake_work);
|
cancel_work_sync(&pm->wake_work);
|
||||||
cancel_work_sync(&dev->reset_work);
|
|
||||||
|
|
||||||
mt7925_tx_token_put(dev);
|
mt7925_tx_token_put(dev);
|
||||||
__mt792x_mcu_drv_pmctrl(dev);
|
__mt792x_mcu_drv_pmctrl(dev);
|
||||||
@@ -721,8 +721,8 @@ static void mt7925_pci_remove(struct pci_dev *pdev)
|
|||||||
struct mt76_dev *mdev = pci_get_drvdata(pdev);
|
struct mt76_dev *mdev = pci_get_drvdata(pdev);
|
||||||
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
|
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
|
||||||
|
|
||||||
mt7925e_unregister_device(dev);
|
|
||||||
set_bit(MT76_REMOVED, &mdev->phy.state);
|
set_bit(MT76_REMOVED, &mdev->phy.state);
|
||||||
|
mt7925e_unregister_device(dev);
|
||||||
devm_free_irq(&pdev->dev, pdev->irq, dev);
|
devm_free_irq(&pdev->dev, pdev->irq, dev);
|
||||||
mt76_free_device(&dev->mt76);
|
mt76_free_device(&dev->mt76);
|
||||||
pci_free_irq_vectors(pdev);
|
pci_free_irq_vectors(pdev);
|
||||||
|
|||||||
Reference in New Issue
Block a user