wifi: ath12k: Request beacon stats from firmware

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>
This commit is contained in:
Ramya Gnanasekar
2025-01-25 00:23:29 +05:30
committed by Jeff Johnson
parent e367c92476
commit 9fe4669ae9
4 changed files with 191 additions and 1 deletions

View 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);

View 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);
}

View 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);

View 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;