]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: ath12k: avoid burning CPU while waiting for firmware stats
authorBaochen Qiang <quic_bqiang@quicinc.com>
Thu, 12 Jun 2025 01:31:50 +0000 (09:31 +0800)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Tue, 17 Jun 2025 23:28:35 +0000 (16:28 -0700)
ath12k_mac_get_fw_stats() is busy polling fw_stats_done flag while waiting
firmware finishing sending all events. This is not good as CPU is
monopolized and kept burning during the wait.

Change to the completion mechanism to fix it.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284.1-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3
Tested-on: QCN9274 hw2.0 WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1

Fixes: e367c924768b ("wifi: ath12k: Request vdev stats from firmware")
Reported-by: Grégoire Stein <gregoire.s93@live.fr>
Closes: https://lore.kernel.org/ath12k/AS8P190MB120575BBB25FCE697CD7D4988763A@AS8P190MB1205.EURP190.PROD.OUTLOOK.COM/
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Tested-by: Grégoire Stein <gregoire.s93@live.fr>
Link: https://patch.msgid.link/20250612-ath12k-fw-fixes-v1-2-12f594f3b857@quicinc.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.c
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/wmi.c

index ebc0560d40e3419130e4caf01c9b91bd9affb3bd..fbc62209086fe5fde007193755f6116bfa72ab77 100644 (file)
@@ -1216,6 +1216,7 @@ void ath12k_fw_stats_init(struct ath12k *ar)
        INIT_LIST_HEAD(&ar->fw_stats.pdevs);
        INIT_LIST_HEAD(&ar->fw_stats.bcn);
        init_completion(&ar->fw_stats_complete);
+       init_completion(&ar->fw_stats_done);
 }
 
 void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
@@ -1228,7 +1229,6 @@ void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
 void ath12k_fw_stats_reset(struct ath12k *ar)
 {
        spin_lock_bh(&ar->data_lock);
-       ar->fw_stats.fw_stats_done = false;
        ath12k_fw_stats_free(&ar->fw_stats);
        spin_unlock_bh(&ar->data_lock);
 }
index efbd6e8ce275eaaa8c7bba7db7ae4674f9c2259a..2b2e0c16b64e6105cf4fc21ff974c59b90552977 100644 (file)
@@ -632,7 +632,6 @@ struct ath12k_fw_stats {
        struct list_head pdevs;
        struct list_head vdevs;
        struct list_head bcn;
-       bool fw_stats_done;
 };
 
 struct ath12k_dbg_htt_stats {
@@ -812,6 +811,7 @@ struct ath12k {
        bool regdom_set_by_user;
 
        struct completion fw_stats_complete;
+       struct completion fw_stats_done;
 
        struct completion mlo_setup_done;
        u32 mlo_setup_status;
index 581b6c9c84ea171660cb49559aeb11578f2694b8..59ec422992d304a5bb3f76cd7c3f0ac0b773da3f 100644 (file)
@@ -4360,7 +4360,7 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
 {
        struct ath12k_base *ab = ar->ab;
        struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
-       unsigned long timeout, time_left;
+       unsigned long time_left;
        int ret;
 
        guard(mutex)(&ah->hw_mutex);
@@ -4368,19 +4368,13 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
        if (ah->state != ATH12K_HW_STATE_ON)
                return -ENETDOWN;
 
-       /* FW stats can get split when exceeding the stats data buffer limit.
-        * In that case, since there is no end marking for the back-to-back
-        * received 'update stats' event, we keep a 3 seconds timeout in case,
-        * fw_stats_done is not marked yet
-        */
-       timeout = jiffies + msecs_to_jiffies(3 * 1000);
        ath12k_fw_stats_reset(ar);
 
        reinit_completion(&ar->fw_stats_complete);
+       reinit_completion(&ar->fw_stats_done);
 
        ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id,
                                                param->vdev_id, param->pdev_id);
-
        if (ret) {
                ath12k_warn(ab, "failed to request fw stats: %d\n", ret);
                return ret;
@@ -4391,7 +4385,6 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
                   param->pdev_id, param->vdev_id, param->stats_id);
 
        time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
-
        if (!time_left) {
                ath12k_warn(ab, "time out while waiting for get fw stats\n");
                return -ETIMEDOUT;
@@ -4400,19 +4393,15 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
        /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back
         * when stats data buffer limit is reached. fw_stats_complete
         * is completed once host receives first event from firmware, but
-        * still there could be more events following. Below loop is to wait
+        * still there could be more events following. Below is to wait
         * until firmware completes sending all the events.
         */
-       for (;;) {
-               if (time_after(jiffies, timeout))
-                       break;
-               spin_lock_bh(&ar->data_lock);
-               if (ar->fw_stats.fw_stats_done) {
-                       spin_unlock_bh(&ar->data_lock);
-                       break;
-               }
-               spin_unlock_bh(&ar->data_lock);
+       time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
+       if (!time_left) {
+               ath12k_warn(ab, "time out while waiting for fw stats done\n");
+               return -ETIMEDOUT;
        }
+
        return 0;
 }
 
index 6dbd272db1d580ca343f3e23e95e9e7fb7b9be6a..e8323581a5afc80173df37229f8355d94cb7627a 100644 (file)
@@ -8192,11 +8192,12 @@ static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
                                      &ar->fw_stats.vdevs);
 
                if (is_end) {
-                       ar->fw_stats.fw_stats_done = true;
+                       complete(&ar->fw_stats_done);
                        num_vdev = 0;
                }
                return;
        }
+
        if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
                if (list_empty(&stats->bcn)) {
                        ath12k_warn(ab, "empty beacon stats");
@@ -8211,7 +8212,7 @@ static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
                                      &ar->fw_stats.bcn);
 
                if (is_end) {
-                       ar->fw_stats.fw_stats_done = true;
+                       complete(&ar->fw_stats_done);
                        num_bcn = 0;
                }
        }
@@ -8249,7 +8250,7 @@ static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *sk
        /* Handle WMI_REQUEST_PDEV_STAT status update */
        if (stats.stats_id == WMI_REQUEST_PDEV_STAT) {
                list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs);
-               ar->fw_stats.fw_stats_done = true;
+               complete(&ar->fw_stats_done);
                goto complete;
        }