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

Pengpeng Hou pengpeng at iscas.ac.cn
Wed Jun 24 17:38:12 PDT 2026


__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 at iscas.ac.cn>
---
 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)




More information about the linux-arm-kernel mailing list