]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: ath12k: Request beacon stats from firmware
authorRamya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com>
Fri, 24 Jan 2025 18:53:29 +0000 (00:23 +0530)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Sun, 26 Jan 2025 18:41:28 +0000 (10:41 -0800)
Add support to request and dump beacon statistics from firmware

Sample output:
-------------
cat /sys/kernel/debug/ath12k/pci-0000:06:00.0/mac0/fw_stats/beacon_stats

           ath12k Beacon stats (1)
           ===================

                       VDEV ID 0
              VDEV MAC address 00:03:7f:04:37:58
              ================

      Num of beacon tx success 20
     Num of beacon tx failures 0

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Signed-off-by: Ramya Gnanasekar <ramya.gnanasekar@oss.qualcomm.com>
Reviewed-by: Aditya Kumar Singh <aditya.kumar.singh@oss.qualcomm.com>
Link: https://patch.msgid.link/20250124185330.1244585-3-ramya.gnanasekar@oss.qualcomm.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/debugfs.c
drivers/net/wireless/ath/ath12k/wmi.c
drivers/net/wireless/ath/ath12k/wmi.h

index 97939ddb3cd7c834e8316476dafe623c17bf1f34..7159343113d6a5231121af61601c99c9952e1afd 100644 (file)
@@ -1099,6 +1099,14 @@ struct ath12k_fw_stats_vdev {
        u32 beacon_rssi_history[MAX_TX_RATE_VALUES];
 };
 
+struct ath12k_fw_stats_bcn {
+       struct list_head list;
+
+       u32 vdev_id;
+       u32 tx_bcn_succ_cnt;
+       u32 tx_bcn_outage_cnt;
+};
+
 int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab);
 int ath12k_core_pre_init(struct ath12k_base *ab);
 int ath12k_core_init(struct ath12k_base *ath12k);
index 1ee9a3e00514fe50d0c4a5f3f675257851c7084b..3cb2bb9fa42404f2d4cc1eba7323d7e1a7612caa 100644 (file)
@@ -69,6 +69,16 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab)
         */
 }
 
+static void ath12k_fw_stats_bcn_free(struct list_head *head)
+{
+       struct ath12k_fw_stats_bcn *i, *tmp;
+
+       list_for_each_entry_safe(i, tmp, head, list) {
+               list_del(&i->list);
+               kfree(i);
+       }
+}
+
 static void ath12k_fw_stats_vdevs_free(struct list_head *head)
 {
        struct ath12k_fw_stats_vdev *i, *tmp;
@@ -84,6 +94,7 @@ void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
        spin_lock_bh(&ar->data_lock);
        ar->fw_stats.fw_stats_done = false;
        ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
+       ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
        spin_unlock_bh(&ar->data_lock);
 }
 
@@ -150,7 +161,7 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar,
        struct ath12k_base *ab = ar->ab;
        struct ath12k_pdev *pdev;
        bool is_end;
-       static unsigned int num_vdev;
+       static unsigned int num_vdev, num_bcn;
        size_t total_vdevs_started = 0;
        int i;
 
@@ -181,6 +192,24 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar,
                }
                return;
        }
+       if (stats->stats_id == WMI_REQUEST_BCN_STAT) {
+               if (list_empty(&stats->bcn)) {
+                       ath12k_warn(ab, "empty beacon stats");
+                       return;
+               }
+               /* Mark end until we reached the count of all started VDEVs
+                * within the PDEV
+                */
+               is_end = ((++num_bcn) == ar->num_started_vdevs);
+
+               list_splice_tail_init(&stats->bcn,
+                                     &ar->fw_stats.bcn);
+
+               if (is_end) {
+                       ar->fw_stats.fw_stats_done = true;
+                       num_bcn = 0;
+               }
+       }
 }
 
 static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
@@ -246,6 +275,78 @@ static const struct file_operations fops_vdev_stats = {
        .llseek = default_llseek,
 };
 
+static int ath12k_open_bcn_stats(struct inode *inode, struct file *file)
+{
+       struct ath12k *ar = inode->i_private;
+       struct ath12k_link_vif *arvif;
+       struct ath12k_fw_stats_req_params param;
+       struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
+       int ret;
+
+       guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+       if (ah && ah->state != ATH12K_HW_STATE_ON)
+               return -ENETDOWN;
+
+       void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC);
+       if (!buf)
+               return -ENOMEM;
+
+       param.pdev_id = ath12k_mac_get_target_pdev_id(ar);
+       param.stats_id = WMI_REQUEST_BCN_STAT;
+
+       /* loop all active VDEVs for bcn stats */
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               if (!arvif->is_up)
+                       continue;
+
+               param.vdev_id = arvif->vdev_id;
+               ret = ath12k_debugfs_fw_stats_request(ar, &param);
+               if (ret) {
+                       ath12k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
+                                buf);
+       /* since beacon stats request is looped for all active VDEVs, saved fw
+        * stats is not freed for each request until done for all active VDEVs
+        */
+       spin_lock_bh(&ar->data_lock);
+       ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
+       spin_unlock_bh(&ar->data_lock);
+
+       file->private_data = no_free_ptr(buf);
+
+       return 0;
+}
+
+static int ath12k_release_bcn_stats(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+
+       return 0;
+}
+
+static ssize_t ath12k_read_bcn_stats(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       const char *buf = file->private_data;
+       size_t len = strlen(buf);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_bcn_stats = {
+       .open = ath12k_open_bcn_stats,
+       .release = ath12k_release_bcn_stats,
+       .read = ath12k_read_bcn_stats,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static
 void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
 {
@@ -257,8 +358,11 @@ void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
         */
        debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
                            &fops_vdev_stats);
+       debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,
+                           &fops_bcn_stats);
 
        INIT_LIST_HEAD(&ar->fw_stats.vdevs);
+       INIT_LIST_HEAD(&ar->fw_stats.bcn);
        init_completion(&ar->fw_stats_complete);
 }
 
index 8ffb215f7aba933ff9ebedef04bf79e33fb9fd17..c9a448492bf12d870d7451a89f6e29c31aff436f 100644 (file)
@@ -6933,6 +6933,45 @@ ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar,
        }
 }
 
+static void
+ath12k_wmi_fw_bcn_stats_dump(struct ath12k *ar,
+                            struct ath12k_fw_stats *fw_stats,
+                            char *buf, u32 *length)
+{
+       const struct ath12k_fw_stats_bcn *bcn;
+       u32 buf_len = ATH12K_FW_STATS_BUF_SIZE;
+       struct ath12k_link_vif *arvif;
+       u32 len = *length;
+       size_t num_bcn;
+
+       num_bcn = list_count_nodes(&fw_stats->bcn);
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+                        "ath12k Beacon stats", num_bcn);
+       len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                        "===================");
+
+       list_for_each_entry(bcn, &fw_stats->bcn, list) {
+               arvif = ath12k_mac_get_arvif(ar, bcn->vdev_id);
+               if (!arvif)
+                       continue;
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "VDEV ID", bcn->vdev_id);
+               len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+                                "VDEV MAC address", arvif->ahvif->vif->addr);
+               len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+                                "================");
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Num of beacon tx success", bcn->tx_bcn_succ_cnt);
+               len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+                                "Num of beacon tx failures", bcn->tx_bcn_outage_cnt);
+
+               len += scnprintf(buf + len, buf_len - len, "\n");
+               *length = len;
+       }
+}
+
 void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
                              struct ath12k_fw_stats *fw_stats,
                              u32 stats_id, char *buf)
@@ -6946,6 +6985,9 @@ void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
        case WMI_REQUEST_VDEV_STAT:
                ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len);
                break;
+       case WMI_REQUEST_BCN_STAT:
+               ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len);
+               break;
        default:
                break;
        }
@@ -6997,6 +7039,15 @@ ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src,
                        le32_to_cpu(src->beacon_rssi_history[i]);
 }
 
+static void
+ath12k_wmi_pull_bcn_stats(const struct ath12k_wmi_bcn_stats_params *src,
+                         struct ath12k_fw_stats_bcn *dst)
+{
+       dst->vdev_id = le32_to_cpu(src->vdev_id);
+       dst->tx_bcn_succ_cnt = le32_to_cpu(src->tx_bcn_succ_cnt);
+       dst->tx_bcn_outage_cnt = le32_to_cpu(src->tx_bcn_outage_cnt);
+}
+
 static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
                                              struct wmi_tlv_fw_stats_parse *parse,
                                              const void *ptr,
@@ -7013,6 +7064,7 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
        const void *data = ptr;
 
        INIT_LIST_HEAD(&stats.vdevs);
+       INIT_LIST_HEAD(&stats.bcn);
 
        if (!ev) {
                ath12k_warn(ab, "failed to fetch update stats ev");
@@ -7067,6 +7119,25 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
                stats.stats_id = WMI_REQUEST_VDEV_STAT;
                list_add_tail(&dst->list, &stats.vdevs);
        }
+       for (i = 0; i < le32_to_cpu(ev->num_bcn_stats); i++) {
+               const struct ath12k_wmi_bcn_stats_params *src;
+               struct ath12k_fw_stats_bcn *dst;
+
+               src = data;
+               if (len < sizeof(*src)) {
+                       ret = -EPROTO;
+                       goto exit;
+               }
+
+               data += sizeof(*src);
+               len -= sizeof(*src);
+               dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+               if (!dst)
+                       continue;
+               ath12k_wmi_pull_bcn_stats(src, dst);
+               stats.stats_id = WMI_REQUEST_BCN_STAT;
+               list_add_tail(&dst->list, &stats.bcn);
+       }
 
        complete(&ar->fw_stats_complete);
        ath12k_debugfs_fw_stats_process(ar, &stats);
index 13644dec50f92654e0520fed85f2f284d921b351..326e079c46c552f3bdce6aa393e6e7eb1eccfa8d 100644 (file)
@@ -5666,6 +5666,7 @@ struct wmi_stats_event {
 
 enum wmi_stats_id {
        WMI_REQUEST_VDEV_STAT   = BIT(3),
+       WMI_REQUEST_BCN_STAT    = BIT(11),
 };
 
 struct wmi_request_stats_cmd {
@@ -5696,6 +5697,12 @@ struct wmi_vdev_stats_params {
        __le32 beacon_rssi_history[MAX_TX_RATE_VALUES];
 } __packed;
 
+struct ath12k_wmi_bcn_stats_params {
+       __le32 vdev_id;
+       __le32 tx_bcn_succ_cnt;
+       __le32 tx_bcn_outage_cnt;
+} __packed;
+
 struct ath12k_fw_stats_req_params {
        u32 stats_id;
        u32 vdev_id;