Message ID | 20240529180920.1472003-1-quic_rgnanase@quicinc.com |
---|---|
State | New |
Headers | show |
Series | wifi: ath12k: Add WMI control path stats infra | expand |
On 5/29/2024 11:39 PM, Ramya Gnanasekar wrote: > From: Rajat Soni <quic_rajson@quicinc.com> > > Currently, firmware stats is requested by host through HTT interface. > Since HTT interface is already overloaded for data path stats, > leveraging control path to request other stats through WMI interface. > > Add debugfs to request the stats and dump the stats forwarded by firmware. > > ath12k > `-- pci-0000:06:00.0 > |-- mac0 > `-- wmi_ctrl_stats > > This patch also adds support to request PDEV Tx stats, parse and dump > the data sent from firmware. > > Usage: > echo <stats id> <action> > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats > > Sample: > echo 1 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats > cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats > WMI_CTRL_PATH_PDEV_TX_STATS: > fw_tx_mgmt_subtype = 0:0, 1:2, 2:0, 3:0, 4:0, 5:37, 6:0, 7:0, 8:908, 9:0, 10:0, 11:18, 12:2, 13:3, 14:0, 15:0, > fw_rx_mgmt_subtype = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, > scan_fail_dfs_violation_time_ms = 0 > nol_chk_fail_last_chan_freq = 0 > nol_chk_fail_time_stamp_ms = 0 > tot_peer_create_cnt = 7 > tot_peer_del_cnt = 7 > tot_peer_del_resp_cnt = 7 > vdev_pause_fail_rt_to_sched_algo_fifo_full_cnt = 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: Rajat Soni <quic_rajson@quicinc.com> > Co-developed-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com> > Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com> > --- > Depends-On: [PATCH v2 3/5] wifi: ath12k: Fix Pdev id in HTT stats request for WCN7850 > Link: https://lore.kernel.org/linux-wireless/20240510050806.514126-1-quic_rgnanase@quicinc.com > --- > drivers/net/wireless/ath/ath12k/core.h | 7 + > drivers/net/wireless/ath/ath12k/debugfs.c | 146 ++++++++++ > drivers/net/wireless/ath/ath12k/wmi.c | 320 ++++++++++++++++++++++ > drivers/net/wireless/ath/ath12k/wmi.h | 86 ++++++ > 4 files changed, 559 insertions(+) > > diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h > index 8acf6424f981..18f9b8e0eb85 100644 > --- a/drivers/net/wireless/ath/ath12k/core.h > +++ b/drivers/net/wireless/ath/ath12k/core.h > @@ -484,6 +484,13 @@ struct ath12k_dbg_htt_stats { > struct ath12k_debug { > struct dentry *debugfs_pdev; > struct ath12k_dbg_htt_stats htt_stats; > + struct ath12k_wmi_ctrl_path_stats_list wmi_ctrl_path_stats; > + enum wmi_tlv_tag wmi_ctrl_path_stats_tagid; > + struct completion wmi_ctrl_path_stats_rcvd; > + u8 wmi_ctrl_path_stats_reqid; > + /* To protect wmi_list manipulation */ > + spinlock_t wmi_ctrl_path_stats_lock; > + bool wmi_ctrl_path_stats_more_enabled; > }; > > struct ath12k_per_peer_tx_stats { > diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c > index 30a80f04d824..5cd5b69324d2 100644 > --- a/drivers/net/wireless/ath/ath12k/debugfs.c > +++ b/drivers/net/wireless/ath/ath12k/debugfs.c > @@ -68,6 +68,148 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) > */ > } > > +static ssize_t ath12k_write_wmi_ctrl_path_stats(struct file *file, > + const char __user *ubuf, > + size_t count, loff_t *ppos) > +{ > + struct ath12k *ar = file->private_data; > + struct wmi_ctrl_path_stats_arg arg = {}; > + u8 buf[128] = {0}; > + int ret; > + > + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); > + if (ret < 0) > + return ret; > + > + buf[ret] = '\0'; > + > + ret = sscanf(buf, "%u %u", &arg.stats_id, &arg.action); > + if (ret != 2) > + return -EINVAL; > + > + if (!arg.action || arg.action > WMI_REQUEST_CTRL_PATH_STAT_RESET) > + return -EINVAL; > + > + ret = ath12k_wmi_send_wmi_ctrl_stats_cmd(ar, &arg); > + return ret ? ret : count; > +} > + > +static int wmi_ctrl_path_pdev_stat(struct ath12k *ar, char __user *ubuf, > + size_t count, loff_t *ppos) > +{ > + char fw_tx_mgmt_subtype[WMI_MAX_STRING_LEN] = {0}; > + char fw_rx_mgmt_subtype[WMI_MAX_STRING_LEN] = {0}; > + struct wmi_ctrl_path_pdev_stats *stats, *tmp; > + u16 index_tx, index_rx; > + const int size = 2048; > + u8 i; > + int len = 0; > + > + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); > + if (!buf) > + return -ENOMEM; > + > + LIST_HEAD(wmi_stats_list); > + > + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + list_splice_tail_init(&ar->debug.wmi_ctrl_path_stats.pdev_stats, &wmi_stats_list); > + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + > + list_for_each_entry_safe(stats, tmp, &wmi_stats_list, list) { > + if (!stats) > + break; > + > + index_tx = 0; > + index_rx = 0; > + > + for (i = 0; i < IEEE80211_MGMT_FRAME_SUBTYPE_MAX; i++) { > + index_tx += scnprintf(&fw_tx_mgmt_subtype[index_tx], > + WMI_MAX_STRING_LEN - index_tx, > + " %u:%u,", i, > + stats->tx_mgmt_subtype[i]); > + index_rx += scnprintf(&fw_rx_mgmt_subtype[index_rx], > + WMI_MAX_STRING_LEN - index_rx, > + " %u:%u,", i, > + stats->rx_mgmt_subtype[i]); > + } > + > + len += scnprintf(buf + len, size - len, > + "WMI_CTRL_PATH_PDEV_TX_STATS:\n"); > + len += scnprintf(buf + len, size - len, > + "fw_tx_mgmt_subtype = %s\n", > + fw_tx_mgmt_subtype); > + len += scnprintf(buf + len, size - len, > + "fw_rx_mgmt_subtype = %s\n", > + fw_rx_mgmt_subtype); > + len += scnprintf(buf + len, size - len, > + "scan_fail_dfs_violation_time_ms = %u\n", > + stats->scan_fail_dfs_viol_time_ms); > + len += scnprintf(buf + len, size - len, > + "nol_chk_fail_last_chan_freq = %u\n", > + stats->nol_chk_fail_last_chan_freq); > + len += scnprintf(buf + len, size - len, > + "nol_chk_fail_time_stamp_ms = %u\n", > + stats->nol_chk_fail_time_stamp_ms); > + len += scnprintf(buf + len, size - len, > + "tot_peer_create_cnt = %u\n", > + stats->tot_peer_create_cnt); > + len += scnprintf(buf + len, size - len, > + "tot_peer_del_cnt = %u\n", > + stats->tot_peer_del_cnt); > + len += scnprintf(buf + len, size - len, > + "tot_peer_del_resp_cnt = %u\n", > + stats->tot_peer_del_resp_cnt); > + len += scnprintf(buf + len, size - len, > + "vdev_pause_fail_rt_to_sched_algo_fifo_full_cnt = %u\n", > + stats->sched_algo_fifo_full_cnt); > + list_del(&stats->list); > + kfree(stats); > + } > + > + return simple_read_from_buffer(ubuf, count, ppos, buf, len); > +} > + > +static ssize_t ath12k_read_wmi_ctrl_path_stats(struct file *file, > + char __user *ubuf, > + size_t count, loff_t *ppos) > +{ > + struct ath12k *ar = file->private_data; > + int ret; > + enum wmi_tlv_tag tagid; > + > + tagid = ar->debug.wmi_ctrl_path_stats_tagid; > + > + switch (tagid) { > + case WMI_TAG_CTRL_PATH_PDEV_STATS: > + ret = wmi_ctrl_path_pdev_stat(ar, ubuf, count, ppos); > + break; > + default: > + /* Unsupported tag */ > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +static const struct file_operations ath12k_fops_wmi_ctrl_stats = { > + .write = ath12k_write_wmi_ctrl_path_stats, > + .open = simple_open, > + .read = ath12k_read_wmi_ctrl_path_stats, > +}; > + > +static void ath12k_debugfs_wmi_ctrl_stats_register(struct ath12k *ar) > +{ > + debugfs_create_file("wmi_ctrl_stats", 0600, > + ar->debug.debugfs_pdev, > + ar, > + &ath12k_fops_wmi_ctrl_stats); > + INIT_LIST_HEAD(&ar->debug.wmi_ctrl_path_stats.pdev_stats); > + spin_lock_init(&ar->debug.wmi_ctrl_path_stats_lock); > + init_completion(&ar->debug.wmi_ctrl_path_stats_rcvd); > + ar->debug.wmi_ctrl_path_stats_more_enabled = false; > +} > + > void ath12k_debugfs_register(struct ath12k *ar) > { > struct ath12k_base *ab = ar->ab; > @@ -90,4 +232,8 @@ void ath12k_debugfs_register(struct ath12k *ar) > } > > ath12k_debugfs_htt_stats_init(ar); > + > + if (test_bit(WMI_TLV_SERVICE_CTRL_PATH_STATS_REQUEST, > + ar->ab->wmi_ab.svc_map)) > + ath12k_debugfs_wmi_ctrl_stats_register(ar); > } > diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c > index 99106b088311..2d636baa1d53 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.c > +++ b/drivers/net/wireless/ath/ath12k/wmi.c > @@ -16,6 +16,7 @@ > #include <linux/of.h> > #include "core.h" > #include "debug.h" > +#include "debugfs.h" > #include "mac.h" > #include "hw.h" > #include "peer.h" > @@ -7029,6 +7030,232 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, > kfree(tb); > } > > +#ifdef CONFIG_ATH12K_DEBUGFS > +static void > +ath12k_wmi_ctrl_path_pdev_stats_list_free(struct list_head *head) > +{ > + struct wmi_ctrl_path_pdev_stats *stats_list, *tmp; > + > + list_for_each_entry_safe(stats_list, tmp, head, list) { > + list_del(&stats_list->list); > + kfree(stats_list); > + } > +} > + > +static void > +ath12k_wmi_ctrl_path_stats_list_free(struct ath12k_wmi_ctrl_path_stats_list *param) > +{ > + ath12k_wmi_ctrl_path_pdev_stats_list_free(¶m->pdev_stats); > +} > + > +static int wmi_pull_ctrl_path_pdev_tx_stats_tlv(struct ath12k_base *ab, u16 len, > + const void *ptr, void *data) > +{ > + struct ath12k_wmi_ctrl_path_stats_list *stats_buff = data; > + const struct wmi_ctrl_path_pdev_stats_params *stats = ptr; > + struct ath12k_wmi_ctrl_path_stats_list *stats_list; > + struct wmi_ctrl_path_pdev_stats *pdev_stats = > + kzalloc(sizeof(*pdev_stats), GFP_ATOMIC); > + struct ath12k *ar; > + u32 pdev_id; > + int i; > + > + if (!pdev_stats) > + return -ENOMEM; > + > + for (i = 0; i < IEEE80211_MGMT_FRAME_SUBTYPE_MAX; i++) { > + pdev_stats->tx_mgmt_subtype[i] = > + __le32_to_cpu(stats->tx_mgmt_subtype[i]); > + pdev_stats->rx_mgmt_subtype[i] = > + __le32_to_cpu(stats->rx_mgmt_subtype[i]); > + } > + pdev_stats->scan_fail_dfs_viol_time_ms = > + __le32_to_cpu(stats->scan_fail_dfs_viol_time_ms); > + pdev_stats->nol_chk_fail_last_chan_freq = > + __le32_to_cpu(stats->nol_chk_fail_last_chan_freq); > + pdev_stats->nol_chk_fail_time_stamp_ms = > + __le32_to_cpu(stats->nol_chk_fail_time_stamp_ms); > + pdev_stats->tot_peer_create_cnt = > + __le32_to_cpu(stats->tot_peer_create_cnt); > + pdev_stats->tot_peer_del_cnt = > + __le32_to_cpu(stats->tot_peer_del_cnt); > + pdev_stats->tot_peer_del_resp_cnt = > + __le32_to_cpu(stats->tot_peer_del_resp_cnt); > + pdev_stats->sched_algo_fifo_full_cnt = > + __le32_to_cpu(stats->sched_algo_fifo_full_cnt); > + > + list_add_tail(&pdev_stats->list, &stats_buff->pdev_stats); > + pdev_id = le32_to_cpu(stats->pdev_id); > + > + rcu_read_lock(); > + ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id + 1); > + if (!ar) { > + rcu_read_unlock(); > + ath12k_warn(ab, "Failed to get ar for wmi ctrl stats\n"); > + kfree(pdev_stats); > + return -EINVAL; > + } > + > + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + stats_list = &ar->debug.wmi_ctrl_path_stats; > + ath12k_wmi_ctrl_path_pdev_stats_list_free(&stats_list->pdev_stats); > + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + ar->debug.wmi_ctrl_path_stats_tagid = WMI_TAG_CTRL_PATH_PDEV_STATS; > + stats_buff->ar = ar; > + rcu_read_unlock(); > + return 0; > +} > + > +static int ath12k_wmi_ctrl_stats_subtlv_parser(struct ath12k_base *ab, > + u16 tag, u16 len, > + const void *ptr, void *data) > +{ > + int ret; > + > + switch (tag) { > + case WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM: > + break; > + case WMI_TAG_CTRL_PATH_PDEV_STATS: > + ret = wmi_pull_ctrl_path_pdev_tx_stats_tlv(ab, len, ptr, data); > + break; > + /* Add case for newly wmi ctrl path added stats here */ > + default: > + ath12k_warn(ab, > + "Received invalid tag for wmi ctrl path stats in subtlvs, tag : 0x%x\n", > + tag); > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +static int ath12k_wmi_ctrl_stats_event_parser(struct ath12k_base *ab, > + u16 tag, u16 len, > + const void *ptr, void *data) > +{ > + int ret; > + > + ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi ctrl path stats tag 0x%x of len %d rcvd\n", > + tag, len); > + > + switch (tag) { > + case WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM: > + /* Fixed param is already processed*/ > + ret = 0; > + break; > + case WMI_TAG_ARRAY_STRUCT: > + /* len 0 is expected for array of struct when there > + * is no content of that type to pack inside that tlv > + */ > + if (len == 0) > + return 0; > + > + ret = ath12k_wmi_tlv_iter(ab, ptr, len, > + ath12k_wmi_ctrl_stats_subtlv_parser, > + data); > + break; > + default: > + ath12k_warn(ab, "Received invalid tag for wmi ctrl path stats\n"); > + ret = -EINVAL; > + break; > + } > + > + return ret; > +} > + > +static void ath12k_wmi_ctrl_path_stats_event(struct ath12k_base *ab, struct sk_buff *skb) > +{ > + struct wmi_ctrl_path_stats_event *fixed_param; > + struct ath12k_wmi_ctrl_path_stats_list param = {0}; > + struct ath12k_wmi_ctrl_path_stats_list *stats; > + const struct wmi_tlv *tlv; > + struct list_head *src, *dst; > + struct ath12k *ar; > + void *ptr = skb->data; > + u16 tlv_tag, tag_id; > + u32 more; > + int ret; > + > + if (!skb->data) { > + ath12k_warn(ab, "No data present in wmi ctrl stats event\n"); > + return; > + } > + > + if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) { > + ath12k_warn(ab, "wmi ctrl stats event size invalid\n"); > + return; > + } > + > + param.ar = NULL; > + > + tlv = ptr; > + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); > + ptr += sizeof(*tlv); > + > + if (tlv_tag != WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM) { > + ath12k_warn(ab, "wmi ctrl stats without fixed param tlv at start\n"); > + return; > + } > + > + INIT_LIST_HEAD(¶m.pdev_stats); > + > + fixed_param = ptr; > + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, > + ath12k_wmi_ctrl_stats_event_parser, > + ¶m); > + if (ret) { > + ath12k_warn(ab, "failed to parse wmi_ctrl_path_stats tlv: %d\n", ret); > + goto free; > + } > + > + ar = param.ar; > + if (!ar) > + return; > + > + tag_id = ar->debug.wmi_ctrl_path_stats_tagid; > + stats = &ar->debug.wmi_ctrl_path_stats; > + more = __le32_to_cpu(fixed_param->more); > + > + switch (tag_id) { > + case WMI_TAG_CTRL_PATH_PDEV_STATS: > + src = ¶m.pdev_stats; > + dst = &stats->pdev_stats; > + break; > + default: > + goto free; > + } > + > + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + if (!more) { > + if (!ar->debug.wmi_ctrl_path_stats_more_enabled) > + ath12k_wmi_ctrl_path_stats_list_free(stats); > + else > + ar->debug.wmi_ctrl_path_stats_more_enabled = false; > + > + list_splice_tail_init(src, dst); > + complete(&ar->debug.wmi_ctrl_path_stats_rcvd); > + } else { > + if (!ar->debug.wmi_ctrl_path_stats_more_enabled) { > + ath12k_wmi_ctrl_path_stats_list_free(stats); > + ar->debug.wmi_ctrl_path_stats_more_enabled = true; > + } > + list_splice_tail_init(src, dst); > + } > + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + return; > +free: > + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > + ath12k_wmi_ctrl_path_stats_list_free(¶m); > + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); > +} > +#else > +static void ath12k_wmi_ctrl_path_stats_event(struct ath12k_base *ab, > + struct sk_buff *skb) > +{ > +} > +#endif /* CONFIG_ATH12K_DEBUGFS */ > + > static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) > { > struct wmi_cmd_hdr *cmd_hdr; > @@ -7149,6 +7376,10 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) > case WMI_DIAG_EVENTID: > ath12k_wmi_diag_event(ab, skb); > break; > + case WMI_CTRL_PATH_STATS_EVENTID: > + ath12k_wmi_ctrl_path_stats_event(ab, skb); > + break; > + > /* TODO: Add remaining events */ > default: > ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); > @@ -7284,6 +7515,95 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) > return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); > } > > +#ifdef CONFIG_ATH12K_DEBUGFS > +int > +ath12k_wmi_send_wmi_ctrl_stats_cmd(struct ath12k *ar, > + struct wmi_ctrl_path_stats_arg *arg) > +{ > + struct wmi_ctrl_path_stats_cmd *cmd; > + struct ath12k_wmi_pdev *wmi = ar->wmi; > + struct ath12k_base *ab = wmi->wmi_ab->ab; > + struct ath12k_debug *debug = &ar->debug; > + __le32 pdev_id; > + struct wmi_tlv *tlv; > + struct sk_buff *skb; > + int len, ret; > + void *ptr; > + u32 stats_id; > + > + if (ab->hw_params->single_pdev_only) > + pdev_id = cpu_to_le32(ath12k_mac_get_target_pdev_id(ar)); > + else > + pdev_id = cpu_to_le32(ar->pdev->pdev_id); > + stats_id = (1 << arg->stats_id); > + > + len = sizeof(*cmd) + > + TLV_HDR_SIZE + sizeof(u32) + > + TLV_HDR_SIZE + TLV_HDR_SIZE; > + > + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); > + if (!skb) > + return -ENOMEM; > + > + cmd = (void *)skb->data; > + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CTRL_PATH_STATS_CMD_FIXED_PARAM, > + sizeof(*cmd)); > + cmd->stats_id = cpu_to_le32(stats_id); > + cmd->req_id = cpu_to_le32(arg->req_id); > + cmd->action = cpu_to_le32(arg->action); > + > + ptr = skb->data + sizeof(*cmd); > + > + /* The below TLV arrays optionally follow this fixed param TLV structure > + * 1. ARRAY_UINT32 pdev_ids[] > + * If this array is present and non-zero length, stats should only > + * be provided from the pdevs identified in the array. > + * 2. ARRAY_UNIT32 vdev_ids[] > + * If this array is present and non-zero length, stats should only > + * be provided from the vdevs identified in the array. > + * 3. ath12k_wmi_mac_addr_params peer_macaddr[]; > + * If this array is present and non-zero length, stats should only > + * be provided from the peers with the MAC addresses specified > + * in the array > + */ > + > + tlv = ptr; > + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); > + ptr += TLV_HDR_SIZE; > + memcpy(ptr, &pdev_id, sizeof(u32)); > + ptr += sizeof(u32); > + > + tlv = ptr; > + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); > + ptr += TLV_HDR_SIZE; > + > + tlv = ptr; > + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_FIXED_STRUCT, 0); > + ptr += TLV_HDR_SIZE; > + > + if (arg->action == WMI_REQUEST_CTRL_PATH_STAT_GET) > + reinit_completion(&ar->debug.wmi_ctrl_path_stats_rcvd); > + > + ret = ath12k_wmi_cmd_send(wmi, skb, > + WMI_REQUEST_CTRL_PATH_STATS_CMDID); > + if (ret) { > + dev_kfree_skb(skb); > + ath12k_warn(ab, "Failed to send WMI_REQUEST_CTRL_PATH_STATS_CMDID: %d", > + ret); > + } else { > + if (arg->action == WMI_REQUEST_CTRL_PATH_STAT_GET) { > + if (!wait_for_completion_timeout(&debug->wmi_ctrl_path_stats_rcvd, > + WMI_CTRL_STATS_READY_TIMEOUT)) { > + ath12k_warn(ab, "wmi ctrl path stats timed out\n"); > + ret = -ETIMEDOUT; > + } > + } > + } > + > + return ret; > +} > +#endif > + > int ath12k_wmi_connect(struct ath12k_base *ab) > { > u32 i; > diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h > index c2b86e187a03..6ecd6c326912 100644 > --- a/drivers/net/wireless/ath/ath12k/wmi.h > +++ b/drivers/net/wireless/ath/ath12k/wmi.h > @@ -514,6 +514,8 @@ enum wmi_tlv_cmd_id { > WMI_REQUEST_RCPI_CMDID, > WMI_REQUEST_PEER_STATS_INFO_CMDID, > WMI_REQUEST_RADIO_CHAN_STATS_CMDID, > + WMI_REQUEST_WLM_STATS_CMDID, > + WMI_REQUEST_CTRL_PATH_STATS_CMDID, > WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), > WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, > WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, > @@ -776,6 +778,8 @@ enum wmi_tlv_event_id { > WMI_UPDATE_RCPI_EVENTID, > WMI_PEER_STATS_INFO_EVENTID, > WMI_RADIO_CHAN_STATS_EVENTID, > + WMI_WLM_STATS_EVENTID, > + WMI_CTRL_PATH_STATS_EVENTID, > WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), > WMI_NLO_SCAN_COMPLETE_EVENTID, > WMI_APFIND_EVENTID, > @@ -1925,6 +1929,9 @@ enum wmi_tlv_tag { > WMI_TAG_SERVICE_READY_EXT2_EVENT = 0x334, > WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344, > WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F, > + WMI_TAG_CTRL_PATH_STATS_CMD_FIXED_PARAM = 0x388, > + WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM, > + WMI_TAG_CTRL_PATH_PDEV_STATS, > WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, > WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, > WMI_TAG_EHT_RATE_SET = 0x3C4, > @@ -2154,6 +2161,7 @@ enum wmi_tlv_service { > WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213, > WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219, > WMI_TLV_SERVICE_EXT2_MSG = 220, > + WMI_TLV_SERVICE_CTRL_PATH_STATS_REQUEST = 250, > WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253, > > WMI_MAX_EXT_SERVICE = 256, > @@ -4902,6 +4910,82 @@ struct wmi_twt_disable_event { > __le32 status; > } __packed; > > +#define WMI_CTRL_STATS_READY_TIMEOUT (1 * HZ) > + > +enum wmi_ctrl_path_stats_id { > + /* bit 0 is currently unused / reserved */ > + WMI_REQ_CTRL_PATH_PDEV_TX_STAT = 1, > +}; > + > +enum wmi_ctrl_path_stats_action { > + WMI_REQUEST_CTRL_PATH_STAT_GET = 1, > + WMI_REQUEST_CTRL_PATH_STAT_RESET = 2, > + WMI_REQUEST_CTRL_PATH_STAT_START = 3, > + WMI_REQUEST_CTRL_PATH_STAT_STOP = 4, > +}; > + > +struct wmi_ctrl_path_stats_cmd { > + __le32 tlv_header; > + __le32 stats_id; > + __le32 req_id; > + /* get/reset/start/stop based on stats id is defined as > + * a part of wmi_ctrl_path_stats_action > + */ > + __le32 action; > +} __packed; > + > +struct wmi_ctrl_path_stats_arg { > + u32 stats_id; > + u32 req_id; > + u32 action; > +}; > + > +struct wmi_ctrl_path_stats_event { > + __le32 req_id; > + /* more flag > + * 1 - More events sent after this event. > + * 0 - no more events after this event. > + */ > + __le32 more; > +}; > + > +/* WMI arrays of length WMI_MGMT_FRAME_SUBTYPE_MAX use the > + * IEEE802.11 standard's enumeration of mgmt frame subtypes: > + */ > +#define IEEE80211_MGMT_FRAME_SUBTYPE_MAX 16 > +#define WMI_MAX_STRING_LEN 256 > + > +struct wmi_ctrl_path_pdev_stats_params { > + __le32 pdev_id; > + __le32 tx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; > + __le32 rx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; > + __le32 scan_fail_dfs_viol_time_ms; > + __le32 nol_chk_fail_last_chan_freq; > + __le32 nol_chk_fail_time_stamp_ms; > + __le32 tot_peer_create_cnt; > + __le32 tot_peer_del_cnt; > + __le32 tot_peer_del_resp_cnt; > + __le32 sched_algo_fifo_full_cnt; > +} __packed; > + > +struct ath12k_wmi_ctrl_path_stats_list { > + struct list_head pdev_stats; > + struct ath12k *ar; > +}; > + > +struct wmi_ctrl_path_pdev_stats { > + struct list_head list; > + u32 tx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; > + u32 rx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; > + u32 scan_fail_dfs_viol_time_ms; > + u32 nol_chk_fail_last_chan_freq; > + u32 nol_chk_fail_time_stamp_ms; > + u32 tot_peer_create_cnt; > + u32 tot_peer_del_cnt; > + u32 tot_peer_del_resp_cnt; > + u32 sched_algo_fifo_full_cnt; > +}; > + > void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, > struct ath12k_wmi_resource_config_arg *config); > void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, > @@ -5027,6 +5111,8 @@ int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, > const u8 *buf, size_t buf_len); > int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table); > int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table); > +int ath12k_wmi_send_wmi_ctrl_stats_cmd(struct ath12k *ar, > + struct wmi_ctrl_path_stats_arg *arg); > > static inline u32 > ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) Please drop this patch. I'll send a re-based version
Ramya Gnanasekar <quic_rgnanase@quicinc.com> writes: > On 5/29/2024 11:39 PM, Ramya Gnanasekar wrote: >> From: Rajat Soni <quic_rajson@quicinc.com> >> >> Currently, firmware stats is requested by host through HTT interface. >> Since HTT interface is already overloaded for data path stats, >> leveraging control path to request other stats through WMI interface. >> >> Add debugfs to request the stats and dump the stats forwarded by firmware. >> >> ath12k >> `-- pci-0000:06:00.0 >> |-- mac0 >> `-- wmi_ctrl_stats >> >> This patch also adds support to request PDEV Tx stats, parse and dump >> the data sent from firmware. >> >> Usage: >> echo <stats id> <action> > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats >> >> Sample: >> echo 1 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats >> cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats >> WMI_CTRL_PATH_PDEV_TX_STATS: >> fw_tx_mgmt_subtype = 0:0, 1:2, 2:0, 3:0, 4:0, 5:37, 6:0, 7:0, 8:908, 9:0, 10:0, 11:18, 12:2, 13:3, 14:0, 15:0, >> fw_rx_mgmt_subtype = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, >> scan_fail_dfs_violation_time_ms = 0 >> nol_chk_fail_last_chan_freq = 0 >> nol_chk_fail_time_stamp_ms = 0 >> tot_peer_create_cnt = 7 >> tot_peer_del_cnt = 7 >> tot_peer_del_resp_cnt = 7 >> vdev_pause_fail_rt_to_sched_algo_fifo_full_cnt = 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: Rajat Soni <quic_rajson@quicinc.com> >> Co-developed-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com> >> Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com> >> --- >> Depends-On: [PATCH v2 3/5] wifi: ath12k: Fix Pdev id in HTT stats request for WCN7850 >> Link: https://lore.kernel.org/linux-wireless/20240510050806.514126-1-quic_rgnanase@quicinc.com [removing over 600 lines unnecessary quotes] > Please drop this patch. I'll send a re-based version Dropped, thanks for letting us know. But please edit your quotes, including the full patch in the reply makes the use of patchwork practically unusable: https://patchwork.kernel.org/project/linux-wireless/patch/20240529180920.1472003-1-quic_rgnanase@quicinc.com/
On 6/27/2024 9:31 AM, Kalle Valo wrote: > Ramya Gnanasekar <quic_rgnanase@quicinc.com> writes: > >> On 5/29/2024 11:39 PM, Ramya Gnanasekar wrote: >>> From: Rajat Soni <quic_rajson@quicinc.com> >>> >>> Currently, firmware stats is requested by host through HTT interface. >>> Since HTT interface is already overloaded for data path stats, >>> leveraging control path to request other stats through WMI interface. >>> >>> Add debugfs to request the stats and dump the stats forwarded by firmware. >>> >>> ath12k >>> `-- pci-0000:06:00.0 >>> |-- mac0 >>> `-- wmi_ctrl_stats >>> >>> This patch also adds support to request PDEV Tx stats, parse and dump >>> the data sent from firmware. >>> >>> Usage: >>> echo <stats id> <action> > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats >>> >>> Sample: >>> echo 1 1 > /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats >>> cat /sys/kernel/debug/ath12k/pci-0000\:06\:00.0/mac0/wmi_ctrl_stats >>> WMI_CTRL_PATH_PDEV_TX_STATS: >>> fw_tx_mgmt_subtype = 0:0, 1:2, 2:0, 3:0, 4:0, 5:37, 6:0, 7:0, 8:908, 9:0, 10:0, 11:18, 12:2, 13:3, 14:0, 15:0, >>> fw_rx_mgmt_subtype = 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0, 13:0, 14:0, 15:0, >>> scan_fail_dfs_violation_time_ms = 0 >>> nol_chk_fail_last_chan_freq = 0 >>> nol_chk_fail_time_stamp_ms = 0 >>> tot_peer_create_cnt = 7 >>> tot_peer_del_cnt = 7 >>> tot_peer_del_resp_cnt = 7 >>> vdev_pause_fail_rt_to_sched_algo_fifo_full_cnt = 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: Rajat Soni <quic_rajson@quicinc.com> >>> Co-developed-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com> >>> Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com> >>> --- >>> Depends-On: [PATCH v2 3/5] wifi: ath12k: Fix Pdev id in HTT stats request for WCN7850 >>> Link: https://lore.kernel.org/linux-wireless/20240510050806.514126-1-quic_rgnanase@quicinc.com > > [removing over 600 lines unnecessary quotes] > >> Please drop this patch. I'll send a re-based version > > Dropped, thanks for letting us know. But please edit your quotes, > including the full patch in the reply makes the use of patchwork > practically unusable: > > https://patchwork.kernel.org/project/linux-wireless/patch/20240529180920.1472003-1-quic_rgnanase@quicinc.com/ > Also FYI I acked it because it applied to my work branch with minimal conflicts which i was able to easily resolve, but of course it is always appreciated when patches are rebased to apply cleanly. /jeff
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 8acf6424f981..18f9b8e0eb85 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -484,6 +484,13 @@ struct ath12k_dbg_htt_stats { struct ath12k_debug { struct dentry *debugfs_pdev; struct ath12k_dbg_htt_stats htt_stats; + struct ath12k_wmi_ctrl_path_stats_list wmi_ctrl_path_stats; + enum wmi_tlv_tag wmi_ctrl_path_stats_tagid; + struct completion wmi_ctrl_path_stats_rcvd; + u8 wmi_ctrl_path_stats_reqid; + /* To protect wmi_list manipulation */ + spinlock_t wmi_ctrl_path_stats_lock; + bool wmi_ctrl_path_stats_more_enabled; }; struct ath12k_per_peer_tx_stats { diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index 30a80f04d824..5cd5b69324d2 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -68,6 +68,148 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) */ } +static ssize_t ath12k_write_wmi_ctrl_path_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + struct wmi_ctrl_path_stats_arg arg = {}; + u8 buf[128] = {0}; + int ret; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count); + if (ret < 0) + return ret; + + buf[ret] = '\0'; + + ret = sscanf(buf, "%u %u", &arg.stats_id, &arg.action); + if (ret != 2) + return -EINVAL; + + if (!arg.action || arg.action > WMI_REQUEST_CTRL_PATH_STAT_RESET) + return -EINVAL; + + ret = ath12k_wmi_send_wmi_ctrl_stats_cmd(ar, &arg); + return ret ? ret : count; +} + +static int wmi_ctrl_path_pdev_stat(struct ath12k *ar, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char fw_tx_mgmt_subtype[WMI_MAX_STRING_LEN] = {0}; + char fw_rx_mgmt_subtype[WMI_MAX_STRING_LEN] = {0}; + struct wmi_ctrl_path_pdev_stats *stats, *tmp; + u16 index_tx, index_rx; + const int size = 2048; + u8 i; + int len = 0; + + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + LIST_HEAD(wmi_stats_list); + + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + list_splice_tail_init(&ar->debug.wmi_ctrl_path_stats.pdev_stats, &wmi_stats_list); + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + + list_for_each_entry_safe(stats, tmp, &wmi_stats_list, list) { + if (!stats) + break; + + index_tx = 0; + index_rx = 0; + + for (i = 0; i < IEEE80211_MGMT_FRAME_SUBTYPE_MAX; i++) { + index_tx += scnprintf(&fw_tx_mgmt_subtype[index_tx], + WMI_MAX_STRING_LEN - index_tx, + " %u:%u,", i, + stats->tx_mgmt_subtype[i]); + index_rx += scnprintf(&fw_rx_mgmt_subtype[index_rx], + WMI_MAX_STRING_LEN - index_rx, + " %u:%u,", i, + stats->rx_mgmt_subtype[i]); + } + + len += scnprintf(buf + len, size - len, + "WMI_CTRL_PATH_PDEV_TX_STATS:\n"); + len += scnprintf(buf + len, size - len, + "fw_tx_mgmt_subtype = %s\n", + fw_tx_mgmt_subtype); + len += scnprintf(buf + len, size - len, + "fw_rx_mgmt_subtype = %s\n", + fw_rx_mgmt_subtype); + len += scnprintf(buf + len, size - len, + "scan_fail_dfs_violation_time_ms = %u\n", + stats->scan_fail_dfs_viol_time_ms); + len += scnprintf(buf + len, size - len, + "nol_chk_fail_last_chan_freq = %u\n", + stats->nol_chk_fail_last_chan_freq); + len += scnprintf(buf + len, size - len, + "nol_chk_fail_time_stamp_ms = %u\n", + stats->nol_chk_fail_time_stamp_ms); + len += scnprintf(buf + len, size - len, + "tot_peer_create_cnt = %u\n", + stats->tot_peer_create_cnt); + len += scnprintf(buf + len, size - len, + "tot_peer_del_cnt = %u\n", + stats->tot_peer_del_cnt); + len += scnprintf(buf + len, size - len, + "tot_peer_del_resp_cnt = %u\n", + stats->tot_peer_del_resp_cnt); + len += scnprintf(buf + len, size - len, + "vdev_pause_fail_rt_to_sched_algo_fifo_full_cnt = %u\n", + stats->sched_algo_fifo_full_cnt); + list_del(&stats->list); + kfree(stats); + } + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static ssize_t ath12k_read_wmi_ctrl_path_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + int ret; + enum wmi_tlv_tag tagid; + + tagid = ar->debug.wmi_ctrl_path_stats_tagid; + + switch (tagid) { + case WMI_TAG_CTRL_PATH_PDEV_STATS: + ret = wmi_ctrl_path_pdev_stat(ar, ubuf, count, ppos); + break; + default: + /* Unsupported tag */ + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct file_operations ath12k_fops_wmi_ctrl_stats = { + .write = ath12k_write_wmi_ctrl_path_stats, + .open = simple_open, + .read = ath12k_read_wmi_ctrl_path_stats, +}; + +static void ath12k_debugfs_wmi_ctrl_stats_register(struct ath12k *ar) +{ + debugfs_create_file("wmi_ctrl_stats", 0600, + ar->debug.debugfs_pdev, + ar, + &ath12k_fops_wmi_ctrl_stats); + INIT_LIST_HEAD(&ar->debug.wmi_ctrl_path_stats.pdev_stats); + spin_lock_init(&ar->debug.wmi_ctrl_path_stats_lock); + init_completion(&ar->debug.wmi_ctrl_path_stats_rcvd); + ar->debug.wmi_ctrl_path_stats_more_enabled = false; +} + void ath12k_debugfs_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; @@ -90,4 +232,8 @@ void ath12k_debugfs_register(struct ath12k *ar) } ath12k_debugfs_htt_stats_init(ar); + + if (test_bit(WMI_TLV_SERVICE_CTRL_PATH_STATS_REQUEST, + ar->ab->wmi_ab.svc_map)) + ath12k_debugfs_wmi_ctrl_stats_register(ar); } diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 99106b088311..2d636baa1d53 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include "core.h" #include "debug.h" +#include "debugfs.h" #include "mac.h" #include "hw.h" #include "peer.h" @@ -7029,6 +7030,232 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab, kfree(tb); } +#ifdef CONFIG_ATH12K_DEBUGFS +static void +ath12k_wmi_ctrl_path_pdev_stats_list_free(struct list_head *head) +{ + struct wmi_ctrl_path_pdev_stats *stats_list, *tmp; + + list_for_each_entry_safe(stats_list, tmp, head, list) { + list_del(&stats_list->list); + kfree(stats_list); + } +} + +static void +ath12k_wmi_ctrl_path_stats_list_free(struct ath12k_wmi_ctrl_path_stats_list *param) +{ + ath12k_wmi_ctrl_path_pdev_stats_list_free(¶m->pdev_stats); +} + +static int wmi_pull_ctrl_path_pdev_tx_stats_tlv(struct ath12k_base *ab, u16 len, + const void *ptr, void *data) +{ + struct ath12k_wmi_ctrl_path_stats_list *stats_buff = data; + const struct wmi_ctrl_path_pdev_stats_params *stats = ptr; + struct ath12k_wmi_ctrl_path_stats_list *stats_list; + struct wmi_ctrl_path_pdev_stats *pdev_stats = + kzalloc(sizeof(*pdev_stats), GFP_ATOMIC); + struct ath12k *ar; + u32 pdev_id; + int i; + + if (!pdev_stats) + return -ENOMEM; + + for (i = 0; i < IEEE80211_MGMT_FRAME_SUBTYPE_MAX; i++) { + pdev_stats->tx_mgmt_subtype[i] = + __le32_to_cpu(stats->tx_mgmt_subtype[i]); + pdev_stats->rx_mgmt_subtype[i] = + __le32_to_cpu(stats->rx_mgmt_subtype[i]); + } + pdev_stats->scan_fail_dfs_viol_time_ms = + __le32_to_cpu(stats->scan_fail_dfs_viol_time_ms); + pdev_stats->nol_chk_fail_last_chan_freq = + __le32_to_cpu(stats->nol_chk_fail_last_chan_freq); + pdev_stats->nol_chk_fail_time_stamp_ms = + __le32_to_cpu(stats->nol_chk_fail_time_stamp_ms); + pdev_stats->tot_peer_create_cnt = + __le32_to_cpu(stats->tot_peer_create_cnt); + pdev_stats->tot_peer_del_cnt = + __le32_to_cpu(stats->tot_peer_del_cnt); + pdev_stats->tot_peer_del_resp_cnt = + __le32_to_cpu(stats->tot_peer_del_resp_cnt); + pdev_stats->sched_algo_fifo_full_cnt = + __le32_to_cpu(stats->sched_algo_fifo_full_cnt); + + list_add_tail(&pdev_stats->list, &stats_buff->pdev_stats); + pdev_id = le32_to_cpu(stats->pdev_id); + + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id + 1); + if (!ar) { + rcu_read_unlock(); + ath12k_warn(ab, "Failed to get ar for wmi ctrl stats\n"); + kfree(pdev_stats); + return -EINVAL; + } + + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + stats_list = &ar->debug.wmi_ctrl_path_stats; + ath12k_wmi_ctrl_path_pdev_stats_list_free(&stats_list->pdev_stats); + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + ar->debug.wmi_ctrl_path_stats_tagid = WMI_TAG_CTRL_PATH_PDEV_STATS; + stats_buff->ar = ar; + rcu_read_unlock(); + return 0; +} + +static int ath12k_wmi_ctrl_stats_subtlv_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + int ret; + + switch (tag) { + case WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM: + break; + case WMI_TAG_CTRL_PATH_PDEV_STATS: + ret = wmi_pull_ctrl_path_pdev_tx_stats_tlv(ab, len, ptr, data); + break; + /* Add case for newly wmi ctrl path added stats here */ + default: + ath12k_warn(ab, + "Received invalid tag for wmi ctrl path stats in subtlvs, tag : 0x%x\n", + tag); + ret = -EINVAL; + break; + } + + return ret; +} + +static int ath12k_wmi_ctrl_stats_event_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + int ret; + + ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi ctrl path stats tag 0x%x of len %d rcvd\n", + tag, len); + + switch (tag) { + case WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM: + /* Fixed param is already processed*/ + ret = 0; + break; + case WMI_TAG_ARRAY_STRUCT: + /* len 0 is expected for array of struct when there + * is no content of that type to pack inside that tlv + */ + if (len == 0) + return 0; + + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_ctrl_stats_subtlv_parser, + data); + break; + default: + ath12k_warn(ab, "Received invalid tag for wmi ctrl path stats\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +static void ath12k_wmi_ctrl_path_stats_event(struct ath12k_base *ab, struct sk_buff *skb) +{ + struct wmi_ctrl_path_stats_event *fixed_param; + struct ath12k_wmi_ctrl_path_stats_list param = {0}; + struct ath12k_wmi_ctrl_path_stats_list *stats; + const struct wmi_tlv *tlv; + struct list_head *src, *dst; + struct ath12k *ar; + void *ptr = skb->data; + u16 tlv_tag, tag_id; + u32 more; + int ret; + + if (!skb->data) { + ath12k_warn(ab, "No data present in wmi ctrl stats event\n"); + return; + } + + if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) { + ath12k_warn(ab, "wmi ctrl stats event size invalid\n"); + return; + } + + param.ar = NULL; + + tlv = ptr; + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + ptr += sizeof(*tlv); + + if (tlv_tag != WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM) { + ath12k_warn(ab, "wmi ctrl stats without fixed param tlv at start\n"); + return; + } + + INIT_LIST_HEAD(¶m.pdev_stats); + + fixed_param = ptr; + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_ctrl_stats_event_parser, + ¶m); + if (ret) { + ath12k_warn(ab, "failed to parse wmi_ctrl_path_stats tlv: %d\n", ret); + goto free; + } + + ar = param.ar; + if (!ar) + return; + + tag_id = ar->debug.wmi_ctrl_path_stats_tagid; + stats = &ar->debug.wmi_ctrl_path_stats; + more = __le32_to_cpu(fixed_param->more); + + switch (tag_id) { + case WMI_TAG_CTRL_PATH_PDEV_STATS: + src = ¶m.pdev_stats; + dst = &stats->pdev_stats; + break; + default: + goto free; + } + + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + if (!more) { + if (!ar->debug.wmi_ctrl_path_stats_more_enabled) + ath12k_wmi_ctrl_path_stats_list_free(stats); + else + ar->debug.wmi_ctrl_path_stats_more_enabled = false; + + list_splice_tail_init(src, dst); + complete(&ar->debug.wmi_ctrl_path_stats_rcvd); + } else { + if (!ar->debug.wmi_ctrl_path_stats_more_enabled) { + ath12k_wmi_ctrl_path_stats_list_free(stats); + ar->debug.wmi_ctrl_path_stats_more_enabled = true; + } + list_splice_tail_init(src, dst); + } + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + return; +free: + spin_lock_bh(&ar->debug.wmi_ctrl_path_stats_lock); + ath12k_wmi_ctrl_path_stats_list_free(¶m); + spin_unlock_bh(&ar->debug.wmi_ctrl_path_stats_lock); +} +#else +static void ath12k_wmi_ctrl_path_stats_event(struct ath12k_base *ab, + struct sk_buff *skb) +{ +} +#endif /* CONFIG_ATH12K_DEBUGFS */ + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -7149,6 +7376,10 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_DIAG_EVENTID: ath12k_wmi_diag_event(ab, skb); break; + case WMI_CTRL_PATH_STATS_EVENTID: + ath12k_wmi_ctrl_path_stats_event(ab, skb); + break; + /* TODO: Add remaining events */ default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); @@ -7284,6 +7515,95 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); } +#ifdef CONFIG_ATH12K_DEBUGFS +int +ath12k_wmi_send_wmi_ctrl_stats_cmd(struct ath12k *ar, + struct wmi_ctrl_path_stats_arg *arg) +{ + struct wmi_ctrl_path_stats_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct ath12k_base *ab = wmi->wmi_ab->ab; + struct ath12k_debug *debug = &ar->debug; + __le32 pdev_id; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int len, ret; + void *ptr; + u32 stats_id; + + if (ab->hw_params->single_pdev_only) + pdev_id = cpu_to_le32(ath12k_mac_get_target_pdev_id(ar)); + else + pdev_id = cpu_to_le32(ar->pdev->pdev_id); + stats_id = (1 << arg->stats_id); + + len = sizeof(*cmd) + + TLV_HDR_SIZE + sizeof(u32) + + TLV_HDR_SIZE + TLV_HDR_SIZE; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (void *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CTRL_PATH_STATS_CMD_FIXED_PARAM, + sizeof(*cmd)); + cmd->stats_id = cpu_to_le32(stats_id); + cmd->req_id = cpu_to_le32(arg->req_id); + cmd->action = cpu_to_le32(arg->action); + + ptr = skb->data + sizeof(*cmd); + + /* The below TLV arrays optionally follow this fixed param TLV structure + * 1. ARRAY_UINT32 pdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the pdevs identified in the array. + * 2. ARRAY_UNIT32 vdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the vdevs identified in the array. + * 3. ath12k_wmi_mac_addr_params peer_macaddr[]; + * If this array is present and non-zero length, stats should only + * be provided from the peers with the MAC addresses specified + * in the array + */ + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); + ptr += TLV_HDR_SIZE; + memcpy(ptr, &pdev_id, sizeof(u32)); + ptr += sizeof(u32); + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + ptr += TLV_HDR_SIZE; + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_FIXED_STRUCT, 0); + ptr += TLV_HDR_SIZE; + + if (arg->action == WMI_REQUEST_CTRL_PATH_STAT_GET) + reinit_completion(&ar->debug.wmi_ctrl_path_stats_rcvd); + + ret = ath12k_wmi_cmd_send(wmi, skb, + WMI_REQUEST_CTRL_PATH_STATS_CMDID); + if (ret) { + dev_kfree_skb(skb); + ath12k_warn(ab, "Failed to send WMI_REQUEST_CTRL_PATH_STATS_CMDID: %d", + ret); + } else { + if (arg->action == WMI_REQUEST_CTRL_PATH_STAT_GET) { + if (!wait_for_completion_timeout(&debug->wmi_ctrl_path_stats_rcvd, + WMI_CTRL_STATS_READY_TIMEOUT)) { + ath12k_warn(ab, "wmi ctrl path stats timed out\n"); + ret = -ETIMEDOUT; + } + } + } + + return ret; +} +#endif + int ath12k_wmi_connect(struct ath12k_base *ab) { u32 i; diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index c2b86e187a03..6ecd6c326912 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -514,6 +514,8 @@ enum wmi_tlv_cmd_id { WMI_REQUEST_RCPI_CMDID, WMI_REQUEST_PEER_STATS_INFO_CMDID, WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_REQUEST_WLM_STATS_CMDID, + WMI_REQUEST_CTRL_PATH_STATS_CMDID, WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, @@ -776,6 +778,8 @@ enum wmi_tlv_event_id { WMI_UPDATE_RCPI_EVENTID, WMI_PEER_STATS_INFO_EVENTID, WMI_RADIO_CHAN_STATS_EVENTID, + WMI_WLM_STATS_EVENTID, + WMI_CTRL_PATH_STATS_EVENTID, WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), WMI_NLO_SCAN_COMPLETE_EVENTID, WMI_APFIND_EVENTID, @@ -1925,6 +1929,9 @@ enum wmi_tlv_tag { WMI_TAG_SERVICE_READY_EXT2_EVENT = 0x334, WMI_TAG_FILS_DISCOVERY_TMPL_CMD = 0x344, WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F, + WMI_TAG_CTRL_PATH_STATS_CMD_FIXED_PARAM = 0x388, + WMI_TAG_CTRL_PATH_STATS_EV_FIXED_PARAM, + WMI_TAG_CTRL_PATH_PDEV_STATS, WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, WMI_TAG_EHT_RATE_SET = 0x3C4, @@ -2154,6 +2161,7 @@ enum wmi_tlv_service { WMI_TLV_SERVICE_PER_PEER_HTT_STATS_RESET = 213, WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219, WMI_TLV_SERVICE_EXT2_MSG = 220, + WMI_TLV_SERVICE_CTRL_PATH_STATS_REQUEST = 250, WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253, WMI_MAX_EXT_SERVICE = 256, @@ -4902,6 +4910,82 @@ struct wmi_twt_disable_event { __le32 status; } __packed; +#define WMI_CTRL_STATS_READY_TIMEOUT (1 * HZ) + +enum wmi_ctrl_path_stats_id { + /* bit 0 is currently unused / reserved */ + WMI_REQ_CTRL_PATH_PDEV_TX_STAT = 1, +}; + +enum wmi_ctrl_path_stats_action { + WMI_REQUEST_CTRL_PATH_STAT_GET = 1, + WMI_REQUEST_CTRL_PATH_STAT_RESET = 2, + WMI_REQUEST_CTRL_PATH_STAT_START = 3, + WMI_REQUEST_CTRL_PATH_STAT_STOP = 4, +}; + +struct wmi_ctrl_path_stats_cmd { + __le32 tlv_header; + __le32 stats_id; + __le32 req_id; + /* get/reset/start/stop based on stats id is defined as + * a part of wmi_ctrl_path_stats_action + */ + __le32 action; +} __packed; + +struct wmi_ctrl_path_stats_arg { + u32 stats_id; + u32 req_id; + u32 action; +}; + +struct wmi_ctrl_path_stats_event { + __le32 req_id; + /* more flag + * 1 - More events sent after this event. + * 0 - no more events after this event. + */ + __le32 more; +}; + +/* WMI arrays of length WMI_MGMT_FRAME_SUBTYPE_MAX use the + * IEEE802.11 standard's enumeration of mgmt frame subtypes: + */ +#define IEEE80211_MGMT_FRAME_SUBTYPE_MAX 16 +#define WMI_MAX_STRING_LEN 256 + +struct wmi_ctrl_path_pdev_stats_params { + __le32 pdev_id; + __le32 tx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; + __le32 rx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; + __le32 scan_fail_dfs_viol_time_ms; + __le32 nol_chk_fail_last_chan_freq; + __le32 nol_chk_fail_time_stamp_ms; + __le32 tot_peer_create_cnt; + __le32 tot_peer_del_cnt; + __le32 tot_peer_del_resp_cnt; + __le32 sched_algo_fifo_full_cnt; +} __packed; + +struct ath12k_wmi_ctrl_path_stats_list { + struct list_head pdev_stats; + struct ath12k *ar; +}; + +struct wmi_ctrl_path_pdev_stats { + struct list_head list; + u32 tx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; + u32 rx_mgmt_subtype[IEEE80211_MGMT_FRAME_SUBTYPE_MAX]; + u32 scan_fail_dfs_viol_time_ms; + u32 nol_chk_fail_last_chan_freq; + u32 nol_chk_fail_time_stamp_ms; + u32 tot_peer_create_cnt; + u32 tot_peer_del_cnt; + u32 tot_peer_del_resp_cnt; + u32 sched_algo_fifo_full_cnt; +}; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5027,6 +5111,8 @@ int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, const u8 *buf, size_t buf_len); int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table); int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table); +int ath12k_wmi_send_wmi_ctrl_stats_cmd(struct ath12k *ar, + struct wmi_ctrl_path_stats_arg *arg); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param)