]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mt76: flush pending TX before channel switch
authorFelix Fietkau <nbd@nbd.name>
Mon, 9 Mar 2026 06:07:27 +0000 (06:07 +0000)
committerFelix Fietkau <nbd@nbd.name>
Tue, 24 Mar 2026 15:49:31 +0000 (15:49 +0000)
mt76_tx() queues frames on wcid->tx_pending for async processing by
tx_worker. In __mt76_set_channel(), the worker gets disabled before it
may have run, and the subsequent wait only checks DMA ring queues, not
the software pending list. This means frames like nullfunc PS frames
from mt76_offchannel_notify() may never be transmitted on the correct
channel.

Fix this by running mt76_txq_schedule_pending() synchronously after
disabling the tx_worker but before setting MT76_RESET, which would
otherwise cause mt76_txq_schedule_pending_wcid() to bail out.

Link: https://patch.msgid.link/20260309060730.87840-8-nbd@nbd.name
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mac80211.c
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/tx.c

index 63a42fe16f73dca15a86d22a670e59610ffe7464..2ddbfdcae939b45ddccdbb3ea8d588fa0147c572 100644 (file)
@@ -1031,9 +1031,10 @@ int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef,
        int timeout = HZ / 5;
        int ret;
 
-       set_bit(MT76_RESET, &phy->state);
-
        mt76_worker_disable(&dev->tx_worker);
+       mt76_txq_schedule_pending(phy);
+
+       set_bit(MT76_RESET, &phy->state);
        wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
        mt76_update_survey(phy);
 
index c9084c0ae522766cd14b608b2dca714cdcc91a59..210aafc1e21edc497e552483adb065c1cc4f02e5 100644 (file)
@@ -1522,6 +1522,7 @@ void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta,
 void mt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb);
 void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid);
 void mt76_txq_schedule_all(struct mt76_phy *phy);
+void mt76_txq_schedule_pending(struct mt76_phy *phy);
 void mt76_tx_worker_run(struct mt76_dev *dev);
 void mt76_tx_worker(struct mt76_worker *w);
 void mt76_release_buffered_frames(struct ieee80211_hw *hw,
index 0753acf2eccb854e64b362efbed119f8c03b4c6f..ab62591b7a2604be58a31637518f402f86a88f94 100644 (file)
@@ -660,7 +660,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,
        return ret;
 }
 
-static void mt76_txq_schedule_pending(struct mt76_phy *phy)
+void mt76_txq_schedule_pending(struct mt76_phy *phy)
 {
        LIST_HEAD(tx_list);
        int ret = 0;