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];
|
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_qmi_firmware_ready(struct ath12k_base *ab);
|
||||||
int ath12k_core_pre_init(struct ath12k_base *ab);
|
int ath12k_core_pre_init(struct ath12k_base *ab);
|
||||||
int ath12k_core_init(struct ath12k_base *ath12k);
|
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)
|
static void ath12k_fw_stats_vdevs_free(struct list_head *head)
|
||||||
{
|
{
|
||||||
struct ath12k_fw_stats_vdev *i, *tmp;
|
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);
|
spin_lock_bh(&ar->data_lock);
|
||||||
ar->fw_stats.fw_stats_done = false;
|
ar->fw_stats.fw_stats_done = false;
|
||||||
ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
|
ath12k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
|
||||||
|
ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
|
||||||
spin_unlock_bh(&ar->data_lock);
|
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_base *ab = ar->ab;
|
||||||
struct ath12k_pdev *pdev;
|
struct ath12k_pdev *pdev;
|
||||||
bool is_end;
|
bool is_end;
|
||||||
static unsigned int num_vdev;
|
static unsigned int num_vdev, num_bcn;
|
||||||
size_t total_vdevs_started = 0;
|
size_t total_vdevs_started = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -181,6 +192,24 @@ ath12k_debugfs_fw_stats_process(struct ath12k *ar,
|
|||||||
}
|
}
|
||||||
return;
|
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)
|
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,
|
.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
|
static
|
||||||
void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
|
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,
|
debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,
|
||||||
&fops_vdev_stats);
|
&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.vdevs);
|
||||||
|
INIT_LIST_HEAD(&ar->fw_stats.bcn);
|
||||||
init_completion(&ar->fw_stats_complete);
|
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,
|
void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
|
||||||
struct ath12k_fw_stats *fw_stats,
|
struct ath12k_fw_stats *fw_stats,
|
||||||
u32 stats_id, char *buf)
|
u32 stats_id, char *buf)
|
||||||
@@ -6946,6 +6985,9 @@ void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
|
|||||||
case WMI_REQUEST_VDEV_STAT:
|
case WMI_REQUEST_VDEV_STAT:
|
||||||
ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len);
|
ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len);
|
||||||
break;
|
break;
|
||||||
|
case WMI_REQUEST_BCN_STAT:
|
||||||
|
ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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]);
|
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,
|
static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
|
||||||
struct wmi_tlv_fw_stats_parse *parse,
|
struct wmi_tlv_fw_stats_parse *parse,
|
||||||
const void *ptr,
|
const void *ptr,
|
||||||
@@ -7013,6 +7064,7 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab,
|
|||||||
const void *data = ptr;
|
const void *data = ptr;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&stats.vdevs);
|
INIT_LIST_HEAD(&stats.vdevs);
|
||||||
|
INIT_LIST_HEAD(&stats.bcn);
|
||||||
|
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
ath12k_warn(ab, "failed to fetch update stats 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;
|
stats.stats_id = WMI_REQUEST_VDEV_STAT;
|
||||||
list_add_tail(&dst->list, &stats.vdevs);
|
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);
|
complete(&ar->fw_stats_complete);
|
||||||
ath12k_debugfs_fw_stats_process(ar, &stats);
|
ath12k_debugfs_fw_stats_process(ar, &stats);
|
||||||
|
|||||||
@@ -5666,6 +5666,7 @@ struct wmi_stats_event {
|
|||||||
|
|
||||||
enum wmi_stats_id {
|
enum wmi_stats_id {
|
||||||
WMI_REQUEST_VDEV_STAT = BIT(3),
|
WMI_REQUEST_VDEV_STAT = BIT(3),
|
||||||
|
WMI_REQUEST_BCN_STAT = BIT(11),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wmi_request_stats_cmd {
|
struct wmi_request_stats_cmd {
|
||||||
@@ -5696,6 +5697,12 @@ struct wmi_vdev_stats_params {
|
|||||||
__le32 beacon_rssi_history[MAX_TX_RATE_VALUES];
|
__le32 beacon_rssi_history[MAX_TX_RATE_VALUES];
|
||||||
} __packed;
|
} __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 {
|
struct ath12k_fw_stats_req_params {
|
||||||
u32 stats_id;
|
u32 stats_id;
|
||||||
u32 vdev_id;
|
u32 vdev_id;
|
||||||
|
|||||||
Reference in New Issue
Block a user