[PATCH wireless] wifi: mt76: usb: keep the tx worker parked through a reset or teardown
Devin Wittmayer
lucid_duck at justthetip.ca
Wed Oct 21 16:39:35 PDT 2026
The mt792x USB reset and disconnect paths park the tx worker and then
stop tx. If frames are still queued when the wait runs out, stopping tx
parks the worker a second time, which warns. It then unparks the worker,
which runs again during the chip reset or the teardown:
WARNING: kernel/kthread.c:710 at kthread_park+0x8c/0xc0, CPU#2: kworker/u16:1/100
Workqueue: mt76 mt7925_mac_reset_work [mt7925_common]
Call Trace:
mt7925u_mac_reset.part.0+0x73/0x1e0 [mt7925u]
mt7925u_mac_reset+0x3c/0x50 [mt7925u]
mt7925_mac_reset_work+0xb5/0x1f0 [mt7925_common]
Remember that a worker is disabled, and leave the worker to the caller
that parked it.
Fixes: 0d2afe09fad5 ("mt76: mt7921: add mt7921u driver")
Fixes: 7910bd565d8b ("wifi: mt76: mt792x: quiesce USB paths on disconnect")
Signed-off-by: Devin Wittmayer <lucid_duck at justthetip.ca>
---
Making the disable call skip an already parked worker is not enough on
its own: stopping tx would still unpark the worker at its end.
Viktor Pashaiev's mt792x USB hardening patch avoided the same warning on
disconnect by not parking the worker there:
https://lore.kernel.org/linux-wireless/20260927180415.1498019-1-w.paszajew@gmail.com/
This keeps the park and fixes the stop path, which also covers the reset.
drivers/net/wireless/mediatek/mt76/usb.c | 10 ++++++++--
drivers/net/wireless/mediatek/mt76/util.h | 9 ++++++++-
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index be48a88a3ad0..47106ed6bb94 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -1068,6 +1068,7 @@ void mt76u_stop_tx(struct mt76_dev *dev)
ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy),
HZ / 5);
if (!ret) {
+ bool parked = mt76_worker_disabled(&dev->tx_worker);
struct mt76_queue_entry entry;
struct mt76_queue *q;
int i, j;
@@ -1083,7 +1084,11 @@ void mt76u_stop_tx(struct mt76_dev *dev)
usb_kill_urb(q->entry[j].urb);
}
- mt76_worker_disable(&dev->tx_worker);
+ /* The mt792x disconnect and reset paths park the worker
+ * before they get here. Leave it parked for them.
+ */
+ if (!parked)
+ mt76_worker_disable(&dev->tx_worker);
/* On device removal we maight queue skb's, but mt76u_tx_kick()
* will fail to submit urb, cleanup those skb's manually.
@@ -1100,7 +1105,8 @@ void mt76u_stop_tx(struct mt76_dev *dev)
}
}
- mt76_worker_enable(&dev->tx_worker);
+ if (!parked)
+ mt76_worker_enable(&dev->tx_worker);
}
cancel_work_sync(&dev->usb.stat_work);
diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h
index 617966e8de76..71f3aa42bb82 100644
--- a/drivers/net/wireless/mediatek/mt76/util.h
+++ b/drivers/net/wireless/mediatek/mt76/util.h
@@ -21,6 +21,7 @@ struct mt76_worker
enum {
MT76_WORKER_SCHEDULED,
MT76_WORKER_RUNNING,
+ MT76_WORKER_DISABLED,
};
#define MT76_INCR(_var, _size) \
@@ -91,7 +92,7 @@ static inline void mt76_worker_disable(struct mt76_worker *w)
return;
kthread_park(w->task);
- WRITE_ONCE(w->state, 0);
+ WRITE_ONCE(w->state, BIT(MT76_WORKER_DISABLED));
}
static inline void mt76_worker_enable(struct mt76_worker *w)
@@ -99,10 +100,16 @@ static inline void mt76_worker_enable(struct mt76_worker *w)
if (!w->task)
return;
+ clear_bit(MT76_WORKER_DISABLED, &w->state);
kthread_unpark(w->task);
mt76_worker_schedule(w);
}
+static inline bool mt76_worker_disabled(struct mt76_worker *w)
+{
+ return test_bit(MT76_WORKER_DISABLED, &w->state);
+}
+
static inline void mt76_worker_teardown(struct mt76_worker *w)
{
if (!w->task)
--
2.56.0
More information about the Linux-mediatek
mailing list