[PATCH ath-next 5/8] wifi: ath10k: check the recovery deadline on its own timeline

Kang Yang kang.yang at oss.qualcomm.com
Tue Oct 20 01:21:57 PDT 2026



On 10/20/2026 3:36 PM, Loic Poulain wrote:
> On Fri, Sep 25, 2026 at 2:27 PM Kang Yang <kang.yang at oss.qualcomm.com> wrote:
>>
>> Recovery timeout enforcement currently advances only when
>> new recovery triggers arrive.
>>
>> In practice, the interval between triggers is not bounded.
>> Additional timeout handling, workqueue congestion, or other
>> recovery-related activities may delay subsequent triggers,
>> causing the effective recovery timeout to extend well beyond
>> the intended ATH10K_RECOVERY_DEADLINE_HZ limit.
>>
>> If recovery becomes stuck and no further trigger is generated,
>> deadline enforcement may stop progressing entirely.
>>
>> Convert recovery_check_work() into self-rearming delayed_work
>> so timeout monitoring follows its own timeline while recovery
>> is in progress, independent of future recovery triggers.
>>
>> Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1
> 
> Are you using b4 (or ./scripts/get_maintainer.pl) for this series? It
> looks like a lot of relevant To: and Cc: recipients are missing, which
> makes it harder to get proper review coverage.

I didn't use "b4 prep --auto-to-cc", will use it in next version.


> 
>>
>> Signed-off-by: Kang Yang <kang.yang at oss.qualcomm.com>
>> ---
>>   drivers/net/wireless/ath/ath10k/core.c | 20 ++++++++++++++------
>>   drivers/net/wireless/ath/ath10k/core.h |  2 +-
>>   drivers/net/wireless/ath/ath10k/mac.c  |  5 ++++-
>>   3 files changed, 19 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
>> index 29ca34a643ed..93dc7332c759 100644
>> --- a/drivers/net/wireless/ath/ath10k/core.c
>> +++ b/drivers/net/wireless/ath/ath10k/core.c
>> @@ -2522,7 +2522,8 @@ static void ath10k_core_enter_wedged(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);
>> +       struct ath10k *ar = container_of(work, struct ath10k,
>> +                                        recovery_check_work.work);
>>          unsigned long recovery_start = READ_ONCE(ar->recovery_start_jiffies);
>>          int fail_count;
>>
>> @@ -2536,8 +2537,11 @@ static void ath10k_core_recovery_check_work(struct work_struct *work)
>>          if (!atomic_read(&ar->pending_recovery))
>>                  return;
>>
>> -       if (!time_after(jiffies, recovery_start + ATH10K_RECOVERY_TIMEOUT_HZ))
>> +       if (!time_after(jiffies, recovery_start + ATH10K_RECOVERY_TIMEOUT_HZ)) {
>> +               queue_delayed_work(ar->workqueue_aux, &ar->recovery_check_work,
>> +                                  ATH10K_RECOVERY_TIMEOUT_HZ);
>>                  return;
>> +       }
>>
>>          /* Record the continuous recovery fail count when recovery is late. */
>>          fail_count = atomic_inc_return(&ar->fail_cont_count);
>> @@ -2547,7 +2551,12 @@ static void ath10k_core_recovery_check_work(struct work_struct *work)
>>                  ath10k_err(ar, "recovery timeout %ums, fail count %d, shutting down driver!",
>>                             jiffies_to_msecs(jiffies - recovery_start), fail_count);
>>                  ath10k_core_enter_wedged(ar);
>> +               return;
>>          }
>> +
>> +       /* Keep checking on our own even if no further trigger arrives. */
>> +       queue_delayed_work(ar->workqueue_aux, &ar->recovery_check_work,
>> +                          ATH10K_RECOVERY_TIMEOUT_HZ);
>>   }
>>
>>   void ath10k_core_start_recovery(struct ath10k *ar)
>> @@ -2574,9 +2583,8 @@ void ath10k_core_start_recovery(struct ath10k *ar)
>>          atomic_set(&ar->fail_cont_count, 0);
>>          WRITE_ONCE(ar->recovery_start_jiffies, jiffies);
>>          queue_work(ar->workqueue, &ar->restart_work);
>> -
>> -       /* Start monitoring after publishing the recovery timestamp. */
>> -       queue_work(ar->workqueue_aux, &ar->recovery_check_work);
>> +       queue_delayed_work(ar->workqueue_aux, &ar->recovery_check_work,
>> +                          ATH10K_RECOVERY_TIMEOUT_HZ);
>>   }
>>   EXPORT_SYMBOL(ath10k_core_start_recovery);
>>
>> @@ -3758,7 +3766,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_DELAYED_WORK(&ar->recovery_check_work, ath10k_core_recovery_check_work);
>>          INIT_WORK(&ar->set_coverage_class_work,
>>                    ath10k_core_set_coverage_class_work);
>>
>> diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
>> index 0319da3ec022..a1ded2cba103 100644
>> --- a/drivers/net/wireless/ath/ath10k/core.h
>> +++ b/drivers/net/wireless/ath/ath10k/core.h
>> @@ -1213,7 +1213,7 @@ struct ath10k {
>>
>>          struct work_struct register_work;
>>          struct work_struct restart_work;
>> -       struct work_struct recovery_check_work;
>> +       struct delayed_work recovery_check_work;
>>          struct work_struct bundle_tx_work;
>>          struct work_struct tx_complete_work;
>>
>> diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
>> index 8e604697d6c2..d10126351fce 100644
>> --- a/drivers/net/wireless/ath/ath10k/mac.c
>> +++ b/drivers/net/wireless/ath/ath10k/mac.c
>> @@ -5427,7 +5427,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);
>> +       cancel_delayed_work_sync(&ar->recovery_check_work);
>>   }
>>
>>   static int ath10k_config_ps(struct ath10k *ar)
>> @@ -8175,6 +8175,9 @@ static void ath10k_reconfig_complete(struct ieee80211_hw *hw,
>>                  atomic_set(&ar->fail_cont_count, 0);
>>                  atomic_set(&ar->pending_recovery, 0);
>>
>> +               /* Stop the self-rearming monitor after recovery succeeds. */
>> +               cancel_delayed_work(&ar->recovery_check_work);
>> +
>>                  if (ar->hw_params.hw_restart_disconnect) {
>>                          list_for_each_entry(arvif, &ar->arvifs, list) {
>>                                  if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA)
>>
>> --
>> 2.34.1
>>
>>




More information about the ath10k mailing list