]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
wifi: ath10k: move recovery check logic into a new work
authorKang Yang <kang.yang@oss.qualcomm.com>
Tue, 14 Oct 2025 11:07:57 +0000 (19:07 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 18 Dec 2025 12:54:42 +0000 (13:54 +0100)
[ Upstream commit f35a07a4842a88801d9182b1a76d178bfa616978 ]

Currently, ath10k has a recovery check logic. It will wait for the
last recovery to finish by wait_for_completion_timeout();

But in SDIO scenarios, the recovery function may be invoked from
interrupt context, where long blocking waits are undesirable and can
lead to system instability.

Additionally, Linux’s ordered workqueue processes one task at a time.
If a previous recovery is still queued or executing, new triggers are
ignored. This prevents accurate tracking of consecutive failures and
delays transition to the WEDGED state.

To address this, move the recovery check logic into a different
workqueue.

Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1
Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189

Fixes: c256a94d1b1b ("wifi: ath10k: shutdown driver when hardware is unreliable")
Signed-off-by: Kang Yang <kang.yang@oss.qualcomm.com>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Link: https://patch.msgid.link/20251014110757.155-1-kang.yang@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/mac.c

index d13acb9e7000951b98d865b019038c5c3088be17..f9b51d98d20bb3e289b7b0caa8d9acf6023cf216 100644 (file)
@@ -3,7 +3,6 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
@@ -2486,8 +2485,9 @@ static int ath10k_init_hw_params(struct ath10k *ar)
        return 0;
 }
 
-static bool ath10k_core_needs_recovery(struct ath10k *ar)
+static void ath10k_core_recovery_check_work(struct work_struct *work)
 {
+       struct ath10k *ar = container_of(work, struct ath10k, recovery_check_work);
        long time_left;
 
        /* Sometimes the recovery will fail and then the next all recovery fail,
@@ -2497,7 +2497,7 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
                ath10k_err(ar, "consecutive fail %d times, will shutdown driver!",
                           atomic_read(&ar->fail_cont_count));
                ar->state = ATH10K_STATE_WEDGED;
-               return false;
+               return;
        }
 
        ath10k_dbg(ar, ATH10K_DBG_BOOT, "total recovery count: %d", ++ar->recovery_count);
@@ -2511,27 +2511,24 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
                                                        ATH10K_RECOVERY_TIMEOUT_HZ);
                if (time_left) {
                        ath10k_warn(ar, "previous recovery succeeded, skip this!\n");
-                       return false;
+                       return;
                }
 
                /* Record the continuous recovery fail count when recovery failed. */
                atomic_inc(&ar->fail_cont_count);
 
                /* Avoid having multiple recoveries at the same time. */
-               return false;
+               return;
        }
 
        atomic_inc(&ar->pending_recovery);
-
-       return true;
+       queue_work(ar->workqueue, &ar->restart_work);
 }
 
 void ath10k_core_start_recovery(struct ath10k *ar)
 {
-       if (!ath10k_core_needs_recovery(ar))
-               return;
-
-       queue_work(ar->workqueue, &ar->restart_work);
+       /* Use workqueue_aux to avoid blocking recovery tracking */
+       queue_work(ar->workqueue_aux, &ar->recovery_check_work);
 }
 EXPORT_SYMBOL(ath10k_core_start_recovery);
 
@@ -3727,6 +3724,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 
        INIT_WORK(&ar->register_work, ath10k_core_register_work);
        INIT_WORK(&ar->restart_work, ath10k_core_restart);
+       INIT_WORK(&ar->recovery_check_work, ath10k_core_recovery_check_work);
        INIT_WORK(&ar->set_coverage_class_work,
                  ath10k_core_set_coverage_class_work);
 
index 85e16c945b5c20ecfc62728d79086f0fcb46eac5..4026cc433b8511200e1aed41c56f48a34175f711 100644 (file)
@@ -3,7 +3,6 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
@@ -1208,6 +1207,7 @@ struct ath10k {
 
        struct work_struct register_work;
        struct work_struct restart_work;
+       struct work_struct recovery_check_work;
        struct work_struct bundle_tx_work;
        struct work_struct tx_complete_work;
 
index 935923c290e1f08bb07f79000c8d7e0bccd14419..97e0a75237583aff2d5c7bb4152634f09289a372 100644 (file)
@@ -3,7 +3,6 @@
  * Copyright (c) 2005-2011 Atheros Communications Inc.
  * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
  * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
@@ -5426,6 +5425,7 @@ static void ath10k_stop(struct ieee80211_hw *hw, bool suspend)
        cancel_work_sync(&ar->set_coverage_class_work);
        cancel_delayed_work_sync(&ar->scan.timeout);
        cancel_work_sync(&ar->restart_work);
+       cancel_work_sync(&ar->recovery_check_work);
 }
 
 static int ath10k_config_ps(struct ath10k *ar)