[RFC PATCH] wifi: mt76: fail channel switch when TX queues do not drain

From: Pengpeng Hou

Date: Wed Jun 24 2026 - 20:39:27 EST


__mt76_set_channel() disables the TX worker, schedules pending TX queues,
sets MT76_RESET, and waits for pending TX to drain before changing the PHY
channel state and calling the driver set_channel() callback.

The wait result is ignored. If TX does not drain within the timeout, the
function still publishes the new channel state and asks the driver to
switch channels while old-channel TX may remain pending.

Return -ETIMEDOUT when the drain wait expires. Keep the survey update on
the old channel, then clear MT76_RESET and re-enable the TX worker through
the existing exit path.

This is marked RFC because the existing channel switch path treats the TX
drain wait as best-effort. Maintainer feedback is needed on whether a TX
drain timeout should fail the channel switch for all mt76 devices or
whether the current force-forward behavior is intentional.

Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 13c4e8abe..37474d64c 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1033,6 +1033,7 @@ int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
bool offchannel)
{
struct mt76_dev *dev = phy->dev;
+ long time_left;
int timeout = HZ / 5;
int ret;

@@ -1040,8 +1041,13 @@ int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
mt76_txq_schedule_pending(phy);

set_bit(MT76_RESET, &phy->state);
- wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
+ time_left = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy),
+ timeout);
mt76_update_survey(phy);
+ if (!time_left) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }

if (phy->chandef.chan->center_freq != chandef->chan->center_freq ||
phy->chandef.width != chandef->width)
@@ -1059,6 +1065,7 @@ int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,

ret = dev->drv->set_channel(phy);

+out:
clear_bit(MT76_RESET, &phy->state);
mt76_worker_enable(&dev->tx_worker);
mt76_worker_schedule(&dev->tx_worker);
--
2.50.1 (Apple Git-155)