1: e73df665b330 ! 1: ea4eae78bab2 wifi: ath12k: convert scan timeout to wiphy delayed work @@ Metadata ## Commit message ## wifi: ath12k: convert scan timeout to wiphy delayed work  - ath12k_mac_op_stop() is called with the wiphy mutex held and calls - ath12k_mac_stop(), which cancels ar->scan.timeout. The timeout worker - also takes the wiphy mutex before aborting the scan, so synchronously - cancelling it from the stop path can deadlock if the worker has started - and is waiting for the same mutex. + ath12k_mac_op_stop() runs with the wiphy mutex held and calls + ath12k_mac_stop(), which synchronously cancels ar->scan.timeout. + The timeout worker takes the same mutex before aborting the scan. If + the worker has started and is waiting for the mutex, stop waits for the + worker while the worker waits for the mutex, resulting in a deadlock.  - Do not drop the wiphy mutex inside the mac80211 stop callback. Convert - ar->scan.timeout to wiphy_delayed_work instead, so the timeout callback - runs in wiphy work context with the wiphy mutex held. This matches the - locking model used by the scan vdev cleanup work and lets stop/cancel - paths use wiphy_delayed_work_cancel() while they already hold the wiphy - mutex. + Convert ar->scan.timeout to a wiphy_delayed_work. Its callback then runs + as wiphy work with the mutex held, allowing stop and other cancellation + paths to use wiphy_delayed_work_cancel() without waiting for the work + callback. The same conversion is applied to ath12k_core_halt(), which + also runs with the wiphy mutex held.  - The old scan-finish path could cancel the delayed work directly from WMI - event context. With wiphy_delayed_work that cancellation must happen from - wiphy context, so keep it in scan.vdev_clean_wk. Mark scans whose cleanup - work has been queued so a timeout work item that was already queued before - cleanup runs does not abort a scan that is already finishing. + WMI event handlers call __ath12k_mac_scan_finish() while holding only + data_lock, so they cannot cancel the wiphy delayed work directly. Set + finish_queued and rely on the existing vdev_clean_wk to cancel the + timeout from wiphy context. Check finish_queued in ath12k_scan_abort() + under data_lock before changing a running scan to aborting, so a timeout + racing with completion does not abort a scan whose completion has already + queued cleanup.  - Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") + Fixes: b8c67509b91ec ("wifi: ath12k: switch to using wiphy_lock() and remove ar->conf_mutex") Suggested-by: Johannes Berg  Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao  @@ drivers/net/wireless/ath/ath12k/mac.c: void __ath12k_mac_scan_finish(struct ath1 complete_all(&ar->scan.completed); wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk); break; +@@ drivers/net/wireless/ath/ath12k/mac.c: static void ath12k_scan_abort(struct ath12k *ar) + ar->scan.state); + break; + case ATH12K_SCAN_RUNNING: ++ if (ar->scan.finish_queued) ++ break; + ar->scan.state = ATH12K_SCAN_ABORTING; + spin_unlock_bh(&ar->data_lock); +   @@ drivers/net/wireless/ath/ath12k/mac.c: static void ath12k_scan_abort(struct ath12k *ar) spin_unlock_bh(&ar->data_lock); } @@ drivers/net/wireless/ath/ath12k/mac.c: static void ath12k_scan_abort(struct ath1  +  + dwork = container_of(work, struct wiphy_delayed_work, work);  + ar = container_of(dwork, struct ath12k, scan.timeout); -+ -+ spin_lock_bh(&ar->data_lock); -+ if (ar->scan.finish_queued) { -+ spin_unlock_bh(&ar->data_lock); -+ return; -+ } -+ spin_unlock_bh(&ar->data_lock); ++ lockdep_assert_wiphy(wiphy);   - wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); ath12k_scan_abort(ar);