[PATCH wireless] wifi: ath12k: avoid flushing scan work under the wiphy lock
From: Runyu Xiao
Date: Thu Jun 11 2026 - 23:33:08 EST
ath12k_mac_op_stop() is entered with the wiphy mutex already held. It
then takes ah->hw_mutex and ath12k_mac_stop() synchronously cancels the
scan.timeout delayed work. The timeout worker grabs the same wiphy lock
before it aborts the scan, so stop can deadlock against the pending
worker.
This issue was found by our static analysis tool and then manually
reviewed against the current tree.
The grounded PoC kept the ath12k_mac_op_stop() -> ath12k_mac_stop() ->
cancel_delayed_work_sync(&ar->scan.timeout) path and the
ath12k_scan_timeout_work() -> wiphy_lock() edge. Lockdep reported:
WARNING: possible circular locking dependency detected
ath12k_scan_timeout_work+0x25/0x42 [vuln_msv]
__cancel_work_timer
*** DEADLOCK ***
Drain scan.timeout before re-entering the stop path under the wiphy lock
and leave the rest of ath12k_mac_stop() unchanged.
Fixes: 2830bc9e784f ("wifi: ath12k: implement remain on channel for P2P mode")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
Notes:
- Validated with a grounded Lockdep PoC that preserves the
ath12k_mac_op_stop() -> ath12k_mac_stop() ->
cancel_delayed_work_sync(&ar->scan.timeout) path and the
ath12k_scan_timeout_work() -> wiphy_lock() edge.
- checkpatch.pl --strict: clean.
- Not tested on ath12k hardware.
drivers/net/wireless/ath/ath12k/mac.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index b97469dca046..cc9e8331513d 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -5263,8 +5263,6 @@ static void ath12k_mac_op_cancel_hw_scan(struct ieee80211_hw *hw,
ar = arvif->ar;
ath12k_scan_abort(ar);
-
- cancel_delayed_work_sync(&ar->scan.timeout);
}
}
@@ -9164,6 +9162,15 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw)
lockdep_assert_wiphy(hw->wiphy);
+ /*
+ * scan.timeout grabs the wiphy lock before aborting the scan, so
+ * drain it before re-entering the stop path under the wiphy lock.
+ */
+ wiphy_unlock(hw->wiphy);
+ for_each_ar(ah, ar, i)
+ cancel_delayed_work_sync(&ar->scan.timeout);
+ wiphy_lock(hw->wiphy);
+
ath12k_drain_tx(ah);
guard(mutex)(&ah->hw_mutex);
--
2.34.1