[PATCH] wifi: mt76: mt792x: Fix a potential deadlock in high-load situations
Leon Yen
leon.yen at mediatek.com
Thu Dec 11 04:43:44 PST 2025
A deadlock may occur between two works, ps_work and mac_work, if their work
functions run simultaneously as they attempt to cancel each other by
calling cancel_delayed_work_sync().
mt792x_mac_work() -> ... -> cancel_delayed_work_sync(&pm->ps_work);
mt792x_pm_power_save_work() -> cancel_delayed_work_sync(&mphy->mac_work);
In high-load situations, they are queued but may not have chance to be
executed until the CPUs are released. Once the CPUs are available, there
is a high possibility that the ps_work function and mac_work function will
be executed simultaneously, resulting in a deadlock.
This patch ensures that the ps_work function and mac_work function can run
exclusively by adding two flags to indicate their running status.The work
function will reschedule itself if the opposite is currently running.
Signed-off-by: Leon Yen <leon.yen at mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
drivers/net/wireless/mediatek/mt76/mt76_connac.h | 1 +
drivers/net/wireless/mediatek/mt76/mt792x_mac.c | 13 +++++++++++++
3 files changed, 15 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index d05e83ea1cac..0414a4898d80 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -892,6 +892,7 @@ struct mt76_phy {
#endif
struct delayed_work mac_work;
+ atomic_t mac_work_running;
u8 mac_work_count;
struct {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
index 813d61bffc2c..eefa0147f883 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
@@ -107,6 +107,7 @@ struct mt76_connac_pm {
struct mutex mutex;
struct delayed_work ps_work;
+ atomic_t ps_work_running;
unsigned long last_activity;
unsigned long idle_timeout;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
index 71dec93094eb..22345031e262 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
@@ -15,6 +15,10 @@ void mt792x_mac_work(struct work_struct *work)
mac_work.work);
phy = mphy->priv;
+ if (atomic_read(&phy->dev->pm.ps_work_running))
+ goto out;
+ atomic_set(&mphy->mac_work_running, 1);
+
mt792x_mutex_acquire(phy->dev);
mt76_update_survey(mphy);
@@ -27,8 +31,10 @@ void mt792x_mac_work(struct work_struct *work)
mt792x_mutex_release(phy->dev);
mt76_tx_status_check(mphy->dev, false);
+out:
ieee80211_queue_delayed_work(phy->mt76->hw, &mphy->mac_work,
MT792x_WATCHDOG_TIME);
+ atomic_set(&mphy->mac_work_running, 0);
}
EXPORT_SYMBOL_GPL(mt792x_mac_work);
@@ -356,6 +362,11 @@ void mt792x_pm_power_save_work(struct work_struct *work)
mphy = dev->phy.mt76;
delta = dev->pm.idle_timeout;
+
+ if (atomic_read(&mphy->mac_work_running))
+ goto out;
+ atomic_set(&dev->pm.ps_work_running, 1);
+
if (test_bit(MT76_HW_SCANNING, &mphy->state) ||
test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) ||
dev->fw_assert)
@@ -376,9 +387,11 @@ void mt792x_pm_power_save_work(struct work_struct *work)
if (!mt792x_mcu_fw_pmctrl(dev)) {
cancel_delayed_work_sync(&mphy->mac_work);
+ atomic_set(&dev->pm.ps_work_running, 0);
return;
}
out:
queue_delayed_work(dev->mt76.wq, &dev->pm.ps_work, delta);
+ atomic_set(&dev->pm.ps_work_running, 0);
}
EXPORT_SYMBOL_GPL(mt792x_pm_power_save_work);
--
2.45.2
More information about the Linux-mediatek
mailing list