Message ID | 20250127104738.4174862-10-quic_periyasa@quicinc.com |
---|---|
State | Superseded |
Headers | show |
Series | wifi: ath12k: Add peer extended Rx statistics debugfs support | expand |
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@quicinc.com> > Signed-off-by: P Praneesh <quic_ppranees@quicinc.com> > Co-developed-by: Balamurugan Mahalingam <quic_bmahalin@quicinc.com> > Signed-off-by: Balamurugan Mahalingam <quic_bmahalin@quicinc.com> > Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@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@oss.qualcomm.com>
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