[PATCH v2] wifi: mt76: usb: re-enable the rx worker when resuming fails

YuanZhe Liu 25031212351 at stu.xidian.edu.cn
Sat Oct 31 09:51:03 PDT 2026


mt76u_resume_rx() returns as soon as submitting the RX buffers fails, so
the mt76_worker_enable() at the end of the function is never reached and
the RX worker stays parked.  The mt792x USB reset path relies on
mt76u_stop_rx() and mt76u_resume_rx() being a balanced park/unpark pair:
if firmware download fails, mt7925_init_hardware() retries up to ten
times and calls mt792xu_init_reset() again, which parks the already
parked RX worker.  kthread_park() then hits its WARN_ON_ONCE() and
returns -EBUSY:

  WARNING: kernel/kthread.c:710 at kthread_park+0x17b/0x1b0
  Workqueue: events mt7925_init_work
  Call Trace:
   mt76_worker_disable     drivers/net/wireless/mediatek/mt76/util.h:93 [inline]
   mt76u_stop_rx+0x5b/0x2f0 drivers/net/wireless/mediatek/mt76/usb.c:757
   mt792xu_init_reset+0x52/0x80 drivers/net/wireless/mediatek/mt76/mt792x_usb.c:477
   mt7925_init_hardware     drivers/net/wireless/mediatek/mt76/mt7925/init.c:152 [inline]
   mt7925_init_work+0x49b/0x7e0 drivers/net/wireless/mediatek/mt76/mt7925/init.c:196

The early return is unbalanced with the URBs as well: mt76u_stop_rx()
poisons every URB of every RX queue, while mt76u_resume_rx() would only
unpoison the queues it reached.  If MT_RXQ_MAIN fails to submit, the URBs
of MT_RXQ_MCU keep urb->reject raised, the next stop/resume pair moves it
further away from zero instead of back to zero, and every later
submission on that queue fails with -EPERM.

A stalled RX endpoint makes usb_submit_urb() return -EPIPE, which is the
failure that leaves the MCU unresponsive and triggers the retry loop.
Unpoison and resubmit every RX queue, keep the first error, and always
re-enable the worker so that both pairs stay balanced.

Fixes: 0d2afe09fad5 ("mt76: mt7921: add mt7921u driver")
Cc: stable at vger.kernel.org
Co-developed-by: Bin Yu <byu at xidian.edu.cn>
Signed-off-by: Bin Yu <byu at xidian.edu.cn>
Co-developed-by: Cheng Wen <wencheng at xidian.edu.cn>
Signed-off-by: Cheng Wen <wencheng at xidian.edu.cn>
Signed-off-by: YuanZhe Liu <25031212351 at stu.xidian.edu.cn>
---
v2:
 - Unpoison and resubmit every RX queue instead of breaking out of the
   loop at the first failure, so that a failing MT_RXQ_MAIN no longer
   leaves the MT_RXQ_MCU URBs poisoned across reset retries, and the queue
   indices are reset for every queue before the worker runs again.

Yes on both counts, thank you for the review.

mt76u_stop_rx() poisons the URBs of every RX queue unconditionally, so
mt76u_resume_rx() has to unpoison that same set of URBs for urb->reject to
return to zero.  The previous version only fixed the park/unpark pair:
MT_RXQ_MAIN is queue 0 and MT_RXQ_MCU is queue 1, so a submission failure
on the main queue left all MCU URBs poisoned.  The next
mt792xu_init_reset() then raised reject to 2 and the following resume
lowered it back to 1, and usb_hcd_link_urb_to_ep() keeps rejecting every
submission on that queue with -EPERM because reject is non-zero.

The skipped queue also kept its old q->head, q->tail and q->queued, since
mt76u_submit_rx_buffers() is what clears them, while the worker had
already been re-enabled.

v2 walks all RX queues, unpoisons and resubmits each of them, keeps the
first error and still always re-enables the worker.  The error is still
returned to the caller, so the firmware retry loop behaves as before.
---
 drivers/net/wireless/mediatek/mt76/usb.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index a9af3aa6b80a..252a18dc3cb2 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -775,23 +775,23 @@ EXPORT_SYMBOL_GPL(mt76u_stop_rx);
 
 int mt76u_resume_rx(struct mt76_dev *dev)
 {
-	int i;
+	int i, err = 0;
 
 	mt76_for_each_q_rx(dev, i) {
 		struct mt76_queue *q = &dev->q_rx[i];
-		int err, j;
+		int ret, j;
 
 		for (j = 0; j < q->ndesc; j++)
 			usb_unpoison_urb(q->entry[j].urb);
 
-		err = mt76u_submit_rx_buffers(dev, i);
-		if (err < 0)
-			return err;
+		ret = mt76u_submit_rx_buffers(dev, i);
+		if (ret < 0 && !err)
+			err = ret;
 	}
 
 	mt76_worker_enable(&dev->usb.rx_worker);
 
-	return 0;
+	return err;
 }
 EXPORT_SYMBOL_GPL(mt76u_resume_rx);
 
-- 
2.34.1




More information about the Linux-mediatek mailing list