[PATCH wireless] Revert "wifi: mt76: Disable napi when removing device"

Mikhail Gavrilov <[email protected]> Tue, 4 Aug 2026 17:00:04 +0500
Newsgroups org.kernel.vger.linux-wireless,dev.linux.lists.regressions,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
This reverts commit 13b7e6a96a005c656d38f3da51581deaf9866375.

That commit made mt76_dma_cleanup() disable every RX NAPI instance before
deleting it, to silence WARNs in __netif_napi_del_locked() and
page_pool_disable_direct_recycling() seen when unloading mt7915e with an
MT7916.

On mt7921e and mt7925e the same instances are already disabled earlier,
in mt7921e_unregister_device() and mt7925e_unregister_device(), which
only afterwards call mt792x_dma_cleanup() -> mt76_dma_cleanup().  Each
instance is therefore disabled twice, and napi_disable() is not
idempotent: on return it leaves NAPIF_STATE_SCHED and NAPIF_STATE_NPSVC
set, so the second call spins in usleep_range() forever, waiting for bits
that nobody will clear.

mt7921_pci_shutdown() and mt7925_pci_shutdown() reuse the remove path, so
this is hit on every reboot, poweroff and module unload.  It is silent:
the stuck task keeps sleeping and rescheduling, so neither the hung task
detector nor the lockup detectors fire, and the last line on the console
is "systemd-shutdown[1]: Rebooting."

  task:modprobe        state:D stack:25720 pid:7954  tgid:7954
  Call Trace:
   <TASK>
   __schedule+0x11b8/0x26d0
   schedule+0xe7/0x2f0
   schedule_hrtimeout_range_clock+0x218/0x330
   usleep_range_state+0x133/0x1b0
   napi_disable_locked+0x37d/0x5f0
   napi_disable+0x43/0x80
   mt76_dma_cleanup+0x2b4/0x860 [mt76]
   mt7921_pci_remove+0x17f/0x350 [mt7921e]
   pci_device_remove+0xb6/0x1e0
   device_release_driver_internal+0x38d/0x540
   driver_detach+0xd0/0x1b0
   bus_remove_driver+0x127/0x2d0
   pci_unregister_driver+0x2a/0x280
   __do_sys_delete_module+0x36a/0x5b0
   do_syscall_64+0x11c/0x6d0
   entry_SYSCALL_64_after_hwframe+0x76/0x7e
   </TASK>

Dropping the two driver-side loops instead was tried and rejected: with
them gone, the RX poll can reach mt76_token_release() via
PKT_TYPE_TXRX_NOTIFY and mt7921_mac_tx_free() while
mt76_connac2_tx_token_put() is running idr_destroy(&dev->token) outside
token_lock, which is a use-after-free rather than a hang [1].

Revert for now, so that reboot, poweroff and module unload work again.
The WARNs on mt7915e are a less severe problem than an unbootable
machine, and fixing them belongs in the drivers that delete the NAPI
instances, where each one can pick a point that is safe for its own
teardown order, rather than in the shared mt76_dma_cleanup().

Reported-by: Bert Karwatzki <[email protected]>
Closes: https://lore.kernel.org/all/[email protected]/
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221818
Link: https://lore.kernel.org/all/20260730050428.GA73812@sol/ [1]
Signed-off-by: Mikhail Gavrilov <[email protected]>
---

Sending this because the regression is now in its second week with four
independent reporters, and the two-part alternative (this revert plus
napi_disable() added inside mt7915_unregister_device()) needs MT7916
hardware that I do not have.  I deliberately left the mt7915 side out;
Nicolas is best placed to do it, since MT7916 is what he reported
against.
 
Verified on 7.2.0-rc5 with KASAN and lockdep enabled, MT7922 / mt7921e:
before the revert 'modprobe -r mt7921e' hangs (backtrace above, taken
with sysrq-w) and the machine never gets past "Rebooting."; after it,
module unload and reload, reboot and poweroff all work again.
 
The hang was independently bisected to the same commit by Bert Karwatzki
on MT7925 and reproduced by Devin Wittmayer on MT7927 and MT7922, and
Eric Biggers saw it on mt7925e as well.

 drivers/net/wireless/mediatek/mt76/dma.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index 322041859217..f8c2fe5f2f58 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -1189,10 +1189,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
 	mt76_for_each_q_rx(dev, i) {
 		struct mt76_queue *q = &dev->q_rx[i];
 
-		if (!mt76_queue_is_wed_rro(q)) {
-			napi_disable(&dev->napi[i]);
-			netif_napi_del(&dev->napi[i]);
-		}
+		netif_napi_del(&dev->napi[i]);
 		mt76_dma_rx_cleanup(dev, q);
 
 		page_pool_destroy(q->page_pool);
-- 
2.55.0