[PATCH v5 9/9] wifi: ath12k: Add peer extended Rx statistics debugfs support

Vasanthakumar Thiagarajan quic_vthiagar at quicinc.com
Tue Jan 28 21:56:20 PST 2025



On 1/27/2025 4:17 PM, Karthikeyan Periyasamy wrote:
> Currently, peer extended Rx statistics are not supported. Therefore, expose
> peer extended Rx statistics support through debugfs, allowing users to
> enable or disable the collection of statistics information. After that
> the statistics information can be dumped through debugfs. Below are the
> debugfs commands exposed.
> 
> Enable/Disable:
>   echo <1/0> > /sys/kernel/debug/ieee80211/phyX/ath12k/ext_rx_stats
> 
> Dump:
>   cat /sys/kernel/debug/ieee80211/phyX/netdev:wlanX/stations/<peer MAC addr>/rx_stats
> 
> Sample output:
> ==============
> RX peer stats:
> 
> Num of MSDUs: 1087
> Num of MSDUs with TCP L4: 0
> Num of MSDUs with UDP L4: 13
> Num of other MSDUs: 1074
> Num of MSDUs part of AMPDU: 363
> Num of MSDUs not part of AMPDU: 724
> Num of MSDUs using STBC: 0
> Num of MSDUs beamformed: 0
> Num of MPDUs with FCS ok: 695
> Num of MPDUs with FCS error: 0
> preamble: 11A 395 11B 0 11N 0 11AC 0 11AX 692 11BE 0
> reception type: SU 1087 MU_MIMO 0 MU_OFDMA 0 MU_OFDMA_MIMO 0
> TID(0-15) Legacy TID(16):690 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 395
> RX Duration:39537
> 
> DCM: 0
> RU26:  0
> RU52:  0
> RU106: 0
> RU242: 0
> RU484: 0
> RU996: 0
> 
> RX success packet stats:
> 
> EHT stats:
> MCS 0: 0        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0        MCS 5: 0        MCS 6: 0        MCS 7: 0
> MCS 8: 0        MCS 9: 0        MCS 10: 0       MCS 11: 0       MCS 12: 0       MCS 13: 0       MCS 14: 0       MCS 15: 0
> 
> HE stats:
> MCS 0: 1        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0        MCS 5: 0
> MCS 6: 66       MCS 7: 46       MCS 8: 46       MCS 9: 34       MCS 10: 28      MCS 11: 471
> 
> VHT stats:
> MCS 0: 0        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0
> MCS 5: 0        MCS 6: 0        MCS 7: 0        MCS 8: 0        MCS 9: 0
> 
> HT stats:
> MCS 0: 0        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0        MCS 5: 0        MCS 6: 0        MCS 7: 0
> MCS 8: 0        MCS 9: 0        MCS 10: 0       MCS 11: 0       MCS 12: 0       MCS 13: 0       MCS 14: 0       MCS 15: 0
> MCS 16: 0       MCS 17: 0       MCS 18: 0       MCS 19: 0       MCS 20: 0       MCS 21: 0       MCS 22: 0       MCS 23: 0
> MCS 24: 0       MCS 25: 0       MCS 26: 0       MCS 27: 0       MCS 28: 0       MCS 29: 0       MCS 30: 0       MCS 31: 0
> 
> Legacy stats:
> 1 Mbps: 0       2 Mbps: 0       5.5 Mbps: 0     6 Mbps: 395
> 9 Mbps: 0       11 Mbps: 0      12 Mbps: 0      18 Mbps: 0
> 24 Mbps: 0      36 Mbps: 0      48 Mbps: 0      54 Mbps: 0
> 
> NSS stats:
> 1x1: 1086 2x2: 0 3x3: 0 4x4: 0 5x5: 0 6x6: 0 7x7: 0 8x8: 0
> 
> GI: 0.8 us 0 0.4 us 396 1.6 us 691 3.2 us 0
> BW: 20 MHz 785 40 MHz 2 80 MHz 300 160 MHz 0 320 MHz 0
> 
> 20 Mhz gi 1 us 1x1 :  6:5 7:3 8:3 9:4 10:4 11:374 12:391
> 40 Mhz gi 1 us 1x1 :  12:2
> 80 Mhz gi 1 us 1x1 :  6:61 7:43 8:43 9:30 10:24 11:97 12:2
> 
> RX success byte stats:
> 
> EHT stats:
> MCS 0: 0        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0        MCS 5: 0        MCS 6: 0        MCS 7: 0
> MCS 8: 0        MCS 9: 0        MCS 10: 0       MCS 11: 0       MCS 12: 0       MCS 13: 0       MCS 14: 0       MCS 15: 0
> 
> HE stats:
> MCS 0: 41       MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0        MCS 5: 0
> MCS 6: 1435     MCS 7: 943      MCS 8: 697      MCS 9: 533      MCS 10: 492     MCS 11: 8159
> 
> VHT stats:
> MCS 0: 0        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0
> MCS 5: 0        MCS 6: 0        MCS 7: 0        MCS 8: 0        MCS 9: 0
> 
> HT stats:
> MCS 0: 0        MCS 1: 0        MCS 2: 0        MCS 3: 0        MCS 4: 0        MCS 5: 0        MCS 6: 0        MCS 7: 0
> MCS 8: 0        MCS 9: 0        MCS 10: 0       MCS 11: 0       MCS 12: 0       MCS 13: 0       MCS 14: 0       MCS 15: 0
> MCS 16: 0       MCS 17: 0       MCS 18: 0       MCS 19: 0       MCS 20: 0       MCS 21: 0       MCS 22: 0       MCS 23: 0
> MCS 24: 0       MCS 25: 0       MCS 26: 0       MCS 27: 0       MCS 28: 0       MCS 29: 0       MCS 30: 0       MCS 31: 0
> 
> Legacy stats:
> 1 Mbps: 0       2 Mbps: 0       5.5 Mbps: 0     6 Mbps: 16195
> 9 Mbps: 0       11 Mbps: 0      12 Mbps: 0      18 Mbps: 0
> 24 Mbps: 0      36 Mbps: 0      48 Mbps: 0      54 Mbps: 0
> 
> NSS stats:
> 1x1: 28454 2x2: 0 3x3: 0 4x4: 0 5x5: 0 6x6: 0 7x7: 0 8x8: 0
> 
> GI: 0.8 us 0 0.4 us 16236 1.6 us 12259 3.2 us 0
> BW: 20 MHz 24108 40 MHz 82 80 MHz 4305 160 MHz 0 320 MHz 0
> 
> 20 Mhz gi 1 us 1x1 :  6:205 7:123 8:123 9:164 10:164 11:7257 12:16031
> 40 Mhz gi 1 us 1x1 :  12:82
> 80 Mhz gi 1 us 1x1 :  6:1230 7:820 8:574 9:369 10:328 11:902 12:82
> 
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3
> 
> Co-developed-by: P Praneesh <quic_ppranees at quicinc.com>
> Signed-off-by: P Praneesh <quic_ppranees at quicinc.com>
> Co-developed-by: Balamurugan Mahalingam <quic_bmahalin at quicinc.com>
> Signed-off-by: Balamurugan Mahalingam <quic_bmahalin at quicinc.com>
> Signed-off-by: Karthikeyan Periyasamy <quic_periyasa at quicinc.com>
> ---
>   drivers/net/wireless/ath/ath12k/Makefile      |   2 +-
>   drivers/net/wireless/ath/ath12k/core.h        |   2 +
>   drivers/net/wireless/ath/ath12k/debugfs.c     |  97 +++++
>   drivers/net/wireless/ath/ath12k/debugfs.h     |  20 ++
>   drivers/net/wireless/ath/ath12k/debugfs_sta.c | 337 ++++++++++++++++++
>   drivers/net/wireless/ath/ath12k/debugfs_sta.h |  24 ++
>   drivers/net/wireless/ath/ath12k/hal_rx.h      |   3 -
>   drivers/net/wireless/ath/ath12k/mac.c         |  18 +-
>   drivers/net/wireless/ath/ath12k/mac.h         |   4 +-
>   9 files changed, 497 insertions(+), 10 deletions(-)
>   create mode 100644 drivers/net/wireless/ath/ath12k/debugfs_sta.c
>   create mode 100644 drivers/net/wireless/ath/ath12k/debugfs_sta.h
> 
> diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile
> index 4a7f5e87384c..60644cb42c76 100644
> --- a/drivers/net/wireless/ath/ath12k/Makefile
> +++ b/drivers/net/wireless/ath/ath12k/Makefile
> @@ -23,7 +23,7 @@ ath12k-y += core.o \
>   	    fw.o \
>   	    p2p.o
>   
> -ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o
> +ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
>   ath12k-$(CONFIG_ACPI) += acpi.o
>   ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
>   ath12k-$(CONFIG_PM) += wow.o
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 50e81d6aa280..2c0bee476604 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -567,9 +567,11 @@ struct ath12k_dbg_htt_stats {
>   };
>   
>   struct ath12k_debug {
> +	u32 rx_filter;
>   	struct dentry *debugfs_pdev;
>   	struct dentry *debugfs_pdev_symlink;
>   	struct ath12k_dbg_htt_stats htt_stats;
> +	bool extd_rx_stats;
>   };
>   
>   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 6d6708486d14..44a3e7080bb6 100644
> --- a/drivers/net/wireless/ath/ath12k/debugfs.c
> +++ b/drivers/net/wireless/ath/ath12k/debugfs.c
> @@ -5,6 +5,7 @@
>    */
>   
>   #include "core.h"
> +#include "dp_tx.h"
>   #include "debug.h"
>   #include "debugfs.h"
>   #include "debugfs_htt_stats.h"
> @@ -32,6 +33,98 @@ static const struct file_operations fops_simulate_radar = {
>   	.open = simple_open
>   };
>   
> +static ssize_t ath12k_write_extd_rx_stats(struct file *file,
> +					  const char __user *ubuf,
> +					  size_t count, loff_t *ppos)
> +{
> +	struct ath12k *ar = file->private_data;
> +	struct htt_rx_ring_tlv_filter tlv_filter = {0};
> +	u32 ring_id, rx_filter = 0;
> +	bool enable;
> +	int ret, i;
> +
> +	if (kstrtobool_from_user(ubuf, count, &enable))
> +		return -EINVAL;
> +
> +	wiphy_lock(ath12k_ar_to_hw(ar)->wiphy);
> +
> +	if (!ar->ab->hw_params->rxdma1_enable) {
> +		ret = count;
> +		goto exit;
> +	}
> +
> +	if (ar->ah->state != ATH12K_HW_STATE_ON) {
> +		ret = -ENETDOWN;
> +		goto exit;
> +	}
> +
> +	if (enable == ar->debug.extd_rx_stats) {
> +		ret = count;
> +		goto exit;
> +	}
> +
> +	if (enable) {
> +		rx_filter =  HTT_RX_FILTER_TLV_FLAGS_MPDU_START;
> +		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;
> +		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;
> +		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;
> +		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;
> +		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;
> +		rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO;
> +
> +		tlv_filter.rx_filter = rx_filter;
> +		tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;
> +		tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;
> +		tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;
> +		tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |
> +			HTT_RX_FP_DATA_FILTER_FLASG3;
> +	} else {
> +		tlv_filter = ath12k_mac_mon_status_filter_default;
> +	}
> +
> +	ar->debug.rx_filter = tlv_filter.rx_filter;
> +
> +	for (i = 0; i < ar->ab->hw_params->num_rxdma_per_pdev; i++) {
> +		ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id;
> +		ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i,
> +						       HAL_RXDMA_MONITOR_DST,
> +						       DP_RXDMA_REFILL_RING_SIZE,
> +						       &tlv_filter);
> +		if (ret) {
> +			ath12k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");
> +			goto exit;
> +		}
> +	}
> +
> +	ar->debug.extd_rx_stats = !!enable;
> +	ret = count;
> +exit:
> +	wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy);
> +	return ret;
> +}
> +
> +static ssize_t ath12k_read_extd_rx_stats(struct file *file,
> +					 char __user *ubuf,
> +					 size_t count, loff_t *ppos)
> +{
> +	struct ath12k *ar = file->private_data;
> +	char buf[32];
> +	int len = 0;
> +
> +	wiphy_lock(ath12k_ar_to_hw(ar)->wiphy);
> +	len = scnprintf(buf, sizeof(buf) - len, "%d\n",
> +			ar->debug.extd_rx_stats);
> +	wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy);
> +
> +	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
> +}
> +
> +static const struct file_operations fops_extd_rx_stats = {
> +	.read = ath12k_read_extd_rx_stats,
> +	.write = ath12k_write_extd_rx_stats,
> +	.open = simple_open,
> +};
> +
>   void ath12k_debugfs_soc_create(struct ath12k_base *ab)
>   {
>   	bool dput_needed;
> @@ -470,6 +563,10 @@ void ath12k_debugfs_register(struct ath12k *ar)
>   
>   	ath12k_debugfs_htt_stats_register(ar);
>   	ath12k_debugfs_fw_stats_register(ar);
> +
> +	debugfs_create_file("ext_rx_stats", 0644,
> +			    ar->debug.debugfs_pdev, ar,
> +			    &fops_extd_rx_stats);
>   }
>   
>   void ath12k_debugfs_unregister(struct ath12k *ar)
> diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h
> index 1c30745ee415..ac154f8b41a5 100644
> --- a/drivers/net/wireless/ath/ath12k/debugfs.h
> +++ b/drivers/net/wireless/ath/ath12k/debugfs.h
> @@ -15,6 +15,16 @@ void ath12k_debugfs_unregister(struct ath12k *ar);
>   void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
>   				     struct ath12k_fw_stats *stats);
>   void ath12k_debugfs_fw_stats_reset(struct ath12k *ar);
> +
> +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
> +{
> +	return ar->debug.extd_rx_stats;
> +}
> +
> +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar)
> +{
> +	return ar->debug.rx_filter;
> +}
>   #else
>   static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab)
>   {
> @@ -40,6 +50,16 @@ static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar,
>   static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar)
>   {
>   }
> +
> +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar)
> +{
> +	return false;
> +}
> +
> +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar)
> +{
> +	return 0;
> +}
>   #endif /* CONFIG_ATH12K_DEBUGFS */
>   
>   #endif /* _ATH12K_DEBUGFS_H_ */
> diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.c b/drivers/net/wireless/ath/ath12k/debugfs_sta.c
> new file mode 100644
> index 000000000000..5bd2bf4c9dac
> --- /dev/null
> +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.c
> @@ -0,0 +1,337 @@
> +// SPDX-License-Identifier: BSD-3-Clause-Clear
> +/*
> + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#include <linux/vmalloc.h>
> +
> +#include "debugfs_sta.h"
> +#include "core.h"
> +#include "peer.h"
> +#include "debug.h"
> +#include "debugfs_htt_stats.h"
> +#include "debugfs.h"
> +
> +static
> +u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size,
> +				   bool he_rates_avail,
> +				   const struct ath12k_rx_peer_rate_stats *stats)
> +{
> +	static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = {
> +					"1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps",
> +					"9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps",
> +					"24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"};
> +	u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS;
> +	int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0;
> +	u32 i, len = offset, max = max_bw * max_gi * max_mcs;
> +	bool found;
> +
> +	len += scnprintf(buf + len, size - len, "\nEHT stats:\n");
> +	for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++)
> +		len += scnprintf(buf + len, size - len,
> +				   "MCS %d: %llu%s", i, stats->be_mcs_count[i],
> +				   (i + 1) % 8 ? "\t" : "\n");
> +
> +	len += scnprintf(buf + len, size - len, "\nHE stats:\n");
> +	for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++)
> +		len += scnprintf(buf + len, size - len,
> +				   "MCS %d: %llu%s", i, stats->he_mcs_count[i],
> +				   (i + 1) % 6 ? "\t" : "\n");
> +
> +	len += scnprintf(buf + len, size - len, "\nVHT stats:\n");
> +	for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++)
> +		len += scnprintf(buf + len, size - len,
> +				   "MCS %d: %llu%s", i, stats->vht_mcs_count[i],
> +				   (i + 1) % 5 ? "\t" : "\n");
> +
> +	len += scnprintf(buf + len, size - len, "\nHT stats:\n");
> +	for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++)
> +		len += scnprintf(buf + len, size - len,
> +				   "MCS %d: %llu%s", i, stats->ht_mcs_count[i],
> +				   (i + 1) % 8 ? "\t" : "\n");
> +
> +	len += scnprintf(buf + len, size - len, "\nLegacy stats:\n");
> +	for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++)
> +		len += scnprintf(buf + len, size - len,
> +				   "%s: %llu%s", legacy_rate_str[i],
> +				   stats->legacy_count[i],
> +				   (i + 1) % 4 ? "\t" : "\n");
> +
> +	len += scnprintf(buf + len, size - len, "\nNSS stats:\n");
> +	for (i = 0; i < HAL_RX_MAX_NSS; i++)
> +		len += scnprintf(buf + len, size - len,
> +				   "%dx%d: %llu ", i + 1, i + 1,
> +				   stats->nss_count[i]);
> +
> +	len += scnprintf(buf + len, size - len,
> +			  "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n",
> +			  stats->gi_count[0],
> +			  stats->gi_count[1],
> +			  stats->gi_count[2],
> +			  stats->gi_count[3]);
> +
> +	len += scnprintf(buf + len, size - len,
> +			   "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n",
> +			   stats->bw_count[0],
> +			   stats->bw_count[1],
> +			   stats->bw_count[2],
> +			   stats->bw_count[3],
> +			   stats->bw_count[4]);
> +
> +	for (i = 0; i < max; i++) {
> +		found = false;
> +
> +		for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) {
> +			if (stats->rx_rate[bw][gi][nss][mcs]) {
> +				found = true;
> +				break;
> +			}
> +		}
> +
> +		if (!found)
> +			goto skip_report;
> +
> +		switch (bw) {
> +		case HAL_RX_BW_20MHZ:
> +			bw_num = 20;
> +			break;
> +		case HAL_RX_BW_40MHZ:
> +			bw_num = 40;
> +			break;
> +		case HAL_RX_BW_80MHZ:
> +			bw_num = 80;
> +			break;
> +		case HAL_RX_BW_160MHZ:
> +			bw_num = 160;
> +			break;
> +		case HAL_RX_BW_320MHZ:
> +			bw_num = 320;
> +			break;
> +		}
> +
> +		len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ",
> +				 bw_num, gi, nss + 1, nss + 1);
> +
> +		for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) {
> +			if (stats->rx_rate[bw][gi][nss][mcs])
> +				len += scnprintf(buf + len, size - len,
> +						 " %d:%llu", mcs,
> +						 stats->rx_rate[bw][gi][nss][mcs]);
> +		}
> +
> +skip_report:
> +		if (nss++ >= max_mcs - 1) {
> +			nss = 0;
> +			if (gi++ >= max_gi - 1) {
> +				gi = 0;
> +				if (bw < max_bw - 1)
> +					bw++;
> +			}
> +		}
> +	}
> +
> +	len += scnprintf(buf + len, size - len, "\n");
> +
> +	return len - offset;
> +}
> +
> +static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file,
> +					    char __user *user_buf,
> +					    size_t count, loff_t *ppos)
> +{
> +	struct ieee80211_link_sta *link_sta = file->private_data;
> +	struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta);
> +	const int size = ATH12K_STA_RX_STATS_BUF_SIZE;
> +	struct ath12k_hw *ah = ahsta->ahvif->ah;
> +	struct ath12k_rx_peer_stats *rx_stats;
> +	struct ath12k_link_sta *arsta;
> +	u8 link_id = link_sta->link_id;
> +	int len = 0, i, ret = 0;
> +	bool he_rates_avail;
> +	struct ath12k *ar;
> +
> +	wiphy_lock(ah->hw->wiphy);
> +
> +	if (!(BIT(link_id) & ahsta->links_map)) {
> +		wiphy_unlock(ah->hw->wiphy);
> +		return -ENOENT;
> +	}
> +
> +	arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
> +	if (!arsta || !arsta->arvif->ar) {
> +		wiphy_unlock(ah->hw->wiphy);
> +		return -ENOENT;
> +	}
> +
> +	ar = arsta->arvif->ar;
> +
> +	u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL);
> +	if (!buf) {
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	spin_lock_bh(&ar->ab->base_lock);
> +
> +	rx_stats = arsta->rx_stats;
> +	if (!rx_stats) {
> +		ret = -ENOENT;
> +		goto unlock;
> +	}
> +
> +	len += scnprintf(buf + len, size - len, "RX peer stats:\n\n");
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n",
> +			 rx_stats->num_msdu);
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n",
> +			 rx_stats->tcp_msdu_count);
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n",
> +			 rx_stats->udp_msdu_count);
> +	len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n",
> +			 rx_stats->other_msdu_count);
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n",
> +			 rx_stats->ampdu_msdu_count);
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n",
> +			 rx_stats->non_ampdu_msdu_count);
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n",
> +			 rx_stats->stbc_count);
> +	len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n",
> +			 rx_stats->beamformed_count);
> +	len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n",
> +			 rx_stats->num_mpdu_fcs_ok);
> +	len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n",
> +			 rx_stats->num_mpdu_fcs_err);
> +
> +	he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false;
> +
> +	len += scnprintf(buf + len, size - len,
> +			 "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n",
> +			 rx_stats->pream_cnt[0], rx_stats->pream_cnt[1],
> +			 rx_stats->pream_cnt[2], rx_stats->pream_cnt[3],
> +			 rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]);
> +	len += scnprintf(buf + len, size - len,
> +			 "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n",
> +			 rx_stats->reception_type[0], rx_stats->reception_type[1],
> +			 rx_stats->reception_type[2], rx_stats->reception_type[3]);
> +
> +	len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):");
> +	for (i = 0; i <= IEEE80211_NUM_TIDS; i++)
> +		len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]);
> +
> +	len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n",
> +			 rx_stats->rx_duration);
> +
> +	len += scnprintf(buf + len, size - len,
> +			 "\nDCM: %llu\nRU26:  %llu\nRU52:  %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n",
> +			 rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0],
> +			 rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2],
> +			 rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4],
> +			 rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]);
> +
> +	len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n");
> +	len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail,
> +					      &rx_stats->pkt_stats);
> +
> +	len += scnprintf(buf + len, size - len, "\n");
> +
> +	len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n");
> +	len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail,
> +					      &rx_stats->byte_stats);
> +
> +unlock:
> +	spin_unlock_bh(&ar->ab->base_lock);
> +
> +	if (len)
> +		ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
> +out:
> +	wiphy_unlock(ah->hw->wiphy);
> +	return ret;
> +}
> +
> +static const struct file_operations fops_rx_stats = {
> +	.read = ath12k_dbg_sta_dump_rx_stats,
> +	.open = simple_open,
> +	.owner = THIS_MODULE,
> +	.llseek = default_llseek,
> +};
> +
> +static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file,
> +					     const char __user *buf,
> +					     size_t count, loff_t *ppos)
> +{
> +	struct ieee80211_link_sta *link_sta = file->private_data;
> +	struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta);
> +	struct ath12k_hw *ah = ahsta->ahvif->ah;
> +	struct ath12k_rx_peer_stats *rx_stats;
> +	struct ath12k_link_sta *arsta;
> +	u8 link_id = link_sta->link_id;
> +	struct ath12k *ar;
> +	bool reset;
> +	int ret;
> +
> +	ret = kstrtobool_from_user(buf, count, &reset);
> +	if (ret)
> +		return ret;
> +
> +	if (!reset)
> +		return -EINVAL;
> +
> +	wiphy_lock(ah->hw->wiphy);
> +
> +	if (!(BIT(link_id) & ahsta->links_map)) {
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]);
> +	if (!arsta || !arsta->arvif->ar) {
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	ar = arsta->arvif->ar;
> +
> +	spin_lock_bh(&ar->ab->base_lock);
> +
> +	rx_stats = arsta->rx_stats;
> +	if (!rx_stats) {
> +		spin_unlock_bh(&ar->ab->base_lock);
> +		ret = -ENOENT;
> +		goto out;
> +	}
> +
> +	memset(rx_stats, 0, sizeof(*rx_stats));
> +	spin_unlock_bh(&ar->ab->base_lock);
> +
> +	ret = count;
> +out:
> +	wiphy_unlock(ah->hw->wiphy);
> +	return ret;
> +}
> +
> +static const struct file_operations fops_reset_rx_stats = {
> +	.write = ath12k_dbg_sta_reset_rx_stats,
> +	.open = simple_open,
> +	.owner = THIS_MODULE,
> +	.llseek = default_llseek,
> +};
> +
> +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif,
> +				    struct ieee80211_link_sta *link_sta,
> +				    struct dentry *dir)
> +{
> +	struct ath12k *ar;
> +
> +	lockdep_assert_wiphy(hw->wiphy);
> +
> +	ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id);
> +	if (!ar)
> +		return;
> +
> +	if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) {
> +		debugfs_create_file("rx_stats", 0400, dir, link_sta,
> +				    &fops_rx_stats);
> +		debugfs_create_file("reset_rx_stats", 0200, dir, link_sta,
> +				    &fops_reset_rx_stats);
> +	}
> +}
> diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h
> new file mode 100644
> index 000000000000..8de924f4d7d5
> --- /dev/null
> +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: BSD-3-Clause-Clear */
> +/*
> + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
> + */
> +
> +#ifndef _ATH12K_DEBUGFS_STA_H_
> +#define _ATH12K_DEBUGFS_STA_H_
> +
> +#include <net/mac80211.h>
> +
> +#include "core.h"
> +
> +#define ATH12K_STA_RX_STATS_BUF_SIZE		(1024 * 16)
> +
> +#ifdef CONFIG_ATH12K_DEBUGFS
> +
> +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif,
> +				    struct ieee80211_link_sta *link_sta,
> +				    struct dentry *dir);
> +
> +#endif /* CONFIG_ATH12K_DEBUGFS */
> +
> +#endif /* _ATH12K_DEBUGFS_STA_H_ */
> diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h
> index 163f9235fcf0..6bdcd0867d86 100644
> --- a/drivers/net/wireless/ath/ath12k/hal_rx.h
> +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h
> @@ -22,9 +22,6 @@ struct hal_rx_wbm_rel_info {
>   #define HAL_INVALID_PEERID	0x3fff
>   #define VHT_SIG_SU_NSS_MASK 0x7
>   
> -#define HAL_RX_MAX_MCS 12
> -#define HAL_RX_MAX_NSS 8
> -
>   #define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \
>   	le32_get_bits((__val), GENMASK(7, 0))
>   
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 4fb7e235be66..c15e0e329776 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -20,6 +20,7 @@
>   #include "debugfs.h"
>   #include "hif.h"
>   #include "wow.h"
> +#include "debugfs_sta.h"
>   
>   #define CHAN2G(_channel, _freq, _flags) { \
>   	.band                   = NL80211_BAND_2GHZ, \
> @@ -805,9 +806,9 @@ static struct ath12k *ath12k_get_ar_by_ctx(struct ieee80211_hw *hw,
>   	return ath12k_mac_get_ar_by_chan(hw, ctx->def.chan);
>   }
>   
> -static struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
> -					   struct ieee80211_vif *vif,
> -					   u8 link_id)
> +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif,
> +				    u8 link_id)
>   {
>   	struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
>   	struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
> @@ -7346,10 +7347,14 @@ static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable)
>   	if (!ab->hw_params->rxdma1_enable)
>   		return ret;
>   
> -	if (enable)
> +	if (enable) {
>   		tlv_filter = ath12k_mac_mon_status_filter_default;
> -	else
> +
> +		if (ath12k_debugfs_rx_filter(ar))
> +			tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar);
> +	} else {
>   		tlv_filter.rxmon_disable = true;
> +	}
>   
>   	for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) {
>   		ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id;
> @@ -10447,6 +10452,9 @@ static const struct ieee80211_ops ath12k_ops = {
>   	.set_wakeup			= ath12k_wow_op_set_wakeup,
>   #endif
>   	CFG80211_TESTMODE_CMD(ath12k_tm_cmd)
> +#ifdef CONFIG_ATH12K_DEBUGFS
> +	.link_sta_add_debugfs           = ath12k_debugfs_link_sta_op_add,
> +#endif
>   };
>   
>   static void ath12k_mac_update_ch_list(struct ath12k *ar,
> diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
> index 1acaf3f68292..8e49a8276a70 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.h
> +++ b/drivers/net/wireless/ath/ath12k/mac.h
> @@ -111,5 +111,7 @@ void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw,
>   u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones);
>   enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones);
>   enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi);
> -
> +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw,
> +				    struct ieee80211_vif *vif,
> +				    u8 link_id);
>   #endif


Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan at oss.qualcomm.com>



More information about the ath12k mailing list