[PATCH 7.0 0116/1146] wifi: mt76: fix deadlock in remain-on-channel
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Date: 2026-05-20 16:30:42
Also in:
stable
Subsystem:
mediatek mt76 wireless lan driver, the rest · Maintainers:
Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Linus Torvalds
7.0-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chad Monroe <redacted>
[ Upstream commit 6939b97ddad3cf3dfbb3b5a0a12ef79cb886747e ]
mt76_remain_on_channel() and mt76_roc_complete() call mt76_set_channel()
while already holding dev->mutex. Since mt76_set_channel() also acquires
dev->mutex, this results in a deadlock.
Use __mt76_set_channel() instead of mt76_set_channel().
Add cancel_delayed_work_sync() for mac_work before acquiring the mutex
in mt76_remain_on_channel() to prevent a secondary deadlock with the
mac_work workqueue.
Fixes: a8f424c1287c ("wifi: mt76: add multi-radio remain_on_channel functions")
Signed-off-by: Chad Monroe <redacted>
Link: https://patch.msgid.link/ace737e7b621af7c2adb33b0188011a5c1de2166.1765204256.git.chad@monroe.io
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/net/wireless/mediatek/mt76/channel.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
index 2b705bdb7993c..d9f8529db7ed4 100644
--- a/drivers/net/wireless/mediatek/mt76/channel.c
+++ b/drivers/net/wireless/mediatek/mt76/channel.c@@ -326,7 +326,7 @@ void mt76_roc_complete(struct mt76_phy *phy) mlink->mvif->roc_phy = NULL; if (phy->main_chandef.chan && !test_bit(MT76_MCU_RESET, &dev->phy.state)) - mt76_set_channel(phy, &phy->main_chandef, false); + __mt76_set_channel(phy, &phy->main_chandef, false); mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link); phy->roc_vif = NULL; phy->roc_link = NULL;
@@ -370,6 +370,8 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!phy) return -EINVAL; + cancel_delayed_work_sync(&phy->mac_work); + mutex_lock(&dev->mutex); if (phy->roc_vif || dev->scan.phy == phy ||
@@ -388,7 +390,14 @@ int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, phy->roc_vif = vif; phy->roc_link = mlink; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); - mt76_set_channel(phy, &chandef, true); + ret = __mt76_set_channel(phy, &chandef, true); + if (ret) { + mlink->mvif->roc_phy = NULL; + phy->roc_vif = NULL; + phy->roc_link = NULL; + mt76_put_vif_phy_link(phy, vif, mlink); + goto out; + } ieee80211_ready_on_channel(hw); ieee80211_queue_delayed_work(phy->hw, &phy->roc_work, msecs_to_jiffies(duration));
--
2.53.0