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 --- 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