mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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:
committed by
Jeff Johnson
parent
e367c92476
commit
9fe4669ae9
@@ -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);
|
||||
|
||||
@@ -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, ¶m);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user