# \[PATCH\] wifi: mac80211: drain TXQ wake tasklet before freeing hardware

- Subsystems: MAC80211, THE REST
- Maintainers: Johannes Berg, Linus Torvalds
- List: linux-wireless
- 2 messages, 2 authors
- Page: https://ratatoskr.run/linux-wireless/2026/11/17850737/t

## \[PATCH\] wifi: mac80211: drain TXQ wake tasklet before freeing hardware

- From: JB Tsai \<hidden\>
- Date: 2026-11-03 09:10:08 
- Message: https://ratatoskr.run/linux-wireless/2026/11/17850737.md

```text
From: Yin-Ming Li <redacted>

Driver queue wakeups schedule wake_txqs_tasklet asynchronously. Stopping
the driver work that issued a wakeup does not wait for this tasklet, and
ieee80211_unregister_hw() does not drain it.

Drain the tasklet in ieee80211_free_hw(), before destroying the local
mutex and releasing the hardware allocation. Drivers must stop their
asynchronous producers before calling ieee80211_free_hw(); this drain
waits for a tasklet those producers have already scheduled. Using the
final free boundary also covers deferred wakeups scheduled while the
driver finishes bus teardown after ieee80211_unregister_hw() returns.

Co-developed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Yin-Ming Li <redacted>
---
 net/mac80211/main.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index f996e15e3d9e35cacddedf482a34c40c587d3e52..086b9ca3485398b4ef2ff60c3f9387dd909a8008 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1755,6 +1755,9 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
 	struct ieee80211_local *local = hw_to_local(hw);
 	enum nl80211_band band;
 
+	/* Driver callbacks have stopped; drain their deferred TXQ wakeups. */
+	tasklet_kill(&local->wake_txqs_tasklet);
+
 	mutex_destroy(&local->iflist_mtx);
 
 	idr_for_each(&local->ack_status_frames,
-- 
2.43.0
```

## Re: \[PATCH\] wifi: mac80211: drain TXQ wake tasklet before freeing hardware

- From: Johannes Berg \<johannes@sipsolutions.net\>
- Date: 2026-11-03 09:30:00 
- Message: https://ratatoskr.run/linux-wireless/2026/11/17850835.md

```text
On Tue, 2026-11-03 at 17:09 +0800, JB Tsai wrote:
> From: Yin-Ming Li <redacted>
> 
> Driver queue wakeups schedule wake_txqs_tasklet asynchronously. Stopping
> the driver work that issued a wakeup does not wait for this tasklet, and
> ieee80211_unregister_hw() does not drain it.
> 
> Drain the tasklet in ieee80211_free_hw(), before destroying the local
> mutex and releasing the hardware allocation. Drivers must stop their
> asynchronous producers before calling ieee80211_free_hw(); this drain
> waits for a tasklet those producers have already scheduled. Using the
> final free boundary also covers deferred wakeups scheduled while the
> driver finishes bus teardown after ieee80211_unregister_hw() returns.

This isn't really making sense, how would the *driver* possibly schedule
something on this tasklet after unregister_hw() and get past
tasklet_kill() too??

johannes
```
