1 // SPDX-License-Identifier: BSD-3-Clause-Clear 2 /* 3 * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/vmalloc.h> 7 8 #include "debugfs_sta.h" 9 #include "core.h" 10 #include "peer.h" 11 #include "debug.h" 12 #include "debugfs_htt_stats.h" 13 #include "debugfs.h" 14 15 static 16 u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size, 17 bool he_rates_avail, 18 const struct ath12k_rx_peer_rate_stats *stats) 19 { 20 static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = { 21 "1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps", 22 "9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps", 23 "24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"}; 24 u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS; 25 int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0; 26 u32 i, len = offset, max = max_bw * max_gi * max_mcs; 27 bool found; 28 29 len += scnprintf(buf + len, size - len, "\nEHT stats:\n"); 30 for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++) 31 len += scnprintf(buf + len, size - len, 32 "MCS %d: %llu%s", i, stats->be_mcs_count[i], 33 (i + 1) % 8 ? "\t" : "\n"); 34 35 len += scnprintf(buf + len, size - len, "\nHE stats:\n"); 36 for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++) 37 len += scnprintf(buf + len, size - len, 38 "MCS %d: %llu%s", i, stats->he_mcs_count[i], 39 (i + 1) % 6 ? "\t" : "\n"); 40 41 len += scnprintf(buf + len, size - len, "\nVHT stats:\n"); 42 for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++) 43 len += scnprintf(buf + len, size - len, 44 "MCS %d: %llu%s", i, stats->vht_mcs_count[i], 45 (i + 1) % 5 ? "\t" : "\n"); 46 47 len += scnprintf(buf + len, size - len, "\nHT stats:\n"); 48 for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++) 49 len += scnprintf(buf + len, size - len, 50 "MCS %d: %llu%s", i, stats->ht_mcs_count[i], 51 (i + 1) % 8 ? "\t" : "\n"); 52 53 len += scnprintf(buf + len, size - len, "\nLegacy stats:\n"); 54 for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++) 55 len += scnprintf(buf + len, size - len, 56 "%s: %llu%s", legacy_rate_str[i], 57 stats->legacy_count[i], 58 (i + 1) % 4 ? "\t" : "\n"); 59 60 len += scnprintf(buf + len, size - len, "\nNSS stats:\n"); 61 for (i = 0; i < HAL_RX_MAX_NSS; i++) 62 len += scnprintf(buf + len, size - len, 63 "%dx%d: %llu ", i + 1, i + 1, 64 stats->nss_count[i]); 65 66 len += scnprintf(buf + len, size - len, 67 "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n", 68 stats->gi_count[0], 69 stats->gi_count[1], 70 stats->gi_count[2], 71 stats->gi_count[3]); 72 73 len += scnprintf(buf + len, size - len, 74 "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n", 75 stats->bw_count[0], 76 stats->bw_count[1], 77 stats->bw_count[2], 78 stats->bw_count[3], 79 stats->bw_count[4]); 80 81 for (i = 0; i < max; i++) { 82 found = false; 83 84 for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { 85 if (stats->rx_rate[bw][gi][nss][mcs]) { 86 found = true; 87 break; 88 } 89 } 90 91 if (!found) 92 goto skip_report; 93 94 switch (bw) { 95 case HAL_RX_BW_20MHZ: 96 bw_num = 20; 97 break; 98 case HAL_RX_BW_40MHZ: 99 bw_num = 40; 100 break; 101 case HAL_RX_BW_80MHZ: 102 bw_num = 80; 103 break; 104 case HAL_RX_BW_160MHZ: 105 bw_num = 160; 106 break; 107 case HAL_RX_BW_320MHZ: 108 bw_num = 320; 109 break; 110 } 111 112 len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ", 113 bw_num, gi, nss + 1, nss + 1); 114 115 for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { 116 if (stats->rx_rate[bw][gi][nss][mcs]) 117 len += scnprintf(buf + len, size - len, 118 " %d:%llu", mcs, 119 stats->rx_rate[bw][gi][nss][mcs]); 120 } 121 122 skip_report: 123 if (nss++ >= max_mcs - 1) { 124 nss = 0; 125 if (gi++ >= max_gi - 1) { 126 gi = 0; 127 if (bw < max_bw - 1) 128 bw++; 129 } 130 } 131 } 132 133 len += scnprintf(buf + len, size - len, "\n"); 134 135 return len - offset; 136 } 137 138 static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file, 139 char __user *user_buf, 140 size_t count, loff_t *ppos) 141 { 142 struct ieee80211_link_sta *link_sta = file->private_data; 143 struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); 144 const int size = ATH12K_STA_RX_STATS_BUF_SIZE; 145 struct ath12k_hw *ah = ahsta->ahvif->ah; 146 struct ath12k_rx_peer_stats *rx_stats; 147 struct ath12k_link_sta *arsta; 148 u8 link_id = link_sta->link_id; 149 int len = 0, i, ret = 0; 150 bool he_rates_avail; 151 struct ath12k *ar; 152 153 wiphy_lock(ah->hw->wiphy); 154 155 if (!(BIT(link_id) & ahsta->links_map)) { 156 wiphy_unlock(ah->hw->wiphy); 157 return -ENOENT; 158 } 159 160 arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); 161 if (!arsta || !arsta->arvif->ar) { 162 wiphy_unlock(ah->hw->wiphy); 163 return -ENOENT; 164 } 165 166 ar = arsta->arvif->ar; 167 168 u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL); 169 if (!buf) { 170 ret = -ENOENT; 171 goto out; 172 } 173 174 spin_lock_bh(&ar->ab->base_lock); 175 176 rx_stats = arsta->rx_stats; 177 if (!rx_stats) { 178 ret = -ENOENT; 179 goto unlock; 180 } 181 182 len += scnprintf(buf + len, size - len, "RX peer stats:\n\n"); 183 len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", 184 rx_stats->num_msdu); 185 len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", 186 rx_stats->tcp_msdu_count); 187 len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", 188 rx_stats->udp_msdu_count); 189 len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n", 190 rx_stats->other_msdu_count); 191 len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", 192 rx_stats->ampdu_msdu_count); 193 len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", 194 rx_stats->non_ampdu_msdu_count); 195 len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", 196 rx_stats->stbc_count); 197 len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", 198 rx_stats->beamformed_count); 199 len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", 200 rx_stats->num_mpdu_fcs_ok); 201 len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", 202 rx_stats->num_mpdu_fcs_err); 203 204 he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false; 205 206 len += scnprintf(buf + len, size - len, 207 "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n", 208 rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], 209 rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], 210 rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]); 211 len += scnprintf(buf + len, size - len, 212 "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", 213 rx_stats->reception_type[0], rx_stats->reception_type[1], 214 rx_stats->reception_type[2], rx_stats->reception_type[3]); 215 216 len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); 217 for (i = 0; i <= IEEE80211_NUM_TIDS; i++) 218 len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); 219 220 len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n", 221 rx_stats->rx_duration); 222 223 len += scnprintf(buf + len, size - len, 224 "\nDCM: %llu\nRU26: %llu\nRU52: %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n", 225 rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], 226 rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], 227 rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], 228 rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]); 229 230 len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n"); 231 len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, 232 &rx_stats->pkt_stats); 233 234 len += scnprintf(buf + len, size - len, "\n"); 235 236 len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n"); 237 len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, 238 &rx_stats->byte_stats); 239 240 unlock: 241 spin_unlock_bh(&ar->ab->base_lock); 242 243 if (len) 244 ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); 245 out: 246 wiphy_unlock(ah->hw->wiphy); 247 return ret; 248 } 249 250 static const struct file_operations fops_rx_stats = { 251 .read = ath12k_dbg_sta_dump_rx_stats, 252 .open = simple_open, 253 .owner = THIS_MODULE, 254 .llseek = default_llseek, 255 }; 256 257 static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file, 258 const char __user *buf, 259 size_t count, loff_t *ppos) 260 { 261 struct ieee80211_link_sta *link_sta = file->private_data; 262 struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); 263 struct ath12k_hw *ah = ahsta->ahvif->ah; 264 struct ath12k_rx_peer_stats *rx_stats; 265 struct ath12k_link_sta *arsta; 266 u8 link_id = link_sta->link_id; 267 struct ath12k *ar; 268 bool reset; 269 int ret; 270 271 ret = kstrtobool_from_user(buf, count, &reset); 272 if (ret) 273 return ret; 274 275 if (!reset) 276 return -EINVAL; 277 278 wiphy_lock(ah->hw->wiphy); 279 280 if (!(BIT(link_id) & ahsta->links_map)) { 281 ret = -ENOENT; 282 goto out; 283 } 284 285 arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); 286 if (!arsta || !arsta->arvif->ar) { 287 ret = -ENOENT; 288 goto out; 289 } 290 291 ar = arsta->arvif->ar; 292 293 spin_lock_bh(&ar->ab->base_lock); 294 295 rx_stats = arsta->rx_stats; 296 if (!rx_stats) { 297 spin_unlock_bh(&ar->ab->base_lock); 298 ret = -ENOENT; 299 goto out; 300 } 301 302 memset(rx_stats, 0, sizeof(*rx_stats)); 303 spin_unlock_bh(&ar->ab->base_lock); 304 305 ret = count; 306 out: 307 wiphy_unlock(ah->hw->wiphy); 308 return ret; 309 } 310 311 static const struct file_operations fops_reset_rx_stats = { 312 .write = ath12k_dbg_sta_reset_rx_stats, 313 .open = simple_open, 314 .owner = THIS_MODULE, 315 .llseek = default_llseek, 316 }; 317 318 void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, 319 struct ieee80211_vif *vif, 320 struct ieee80211_link_sta *link_sta, 321 struct dentry *dir) 322 { 323 struct ath12k *ar; 324 325 lockdep_assert_wiphy(hw->wiphy); 326 327 ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id); 328 if (!ar) 329 return; 330 331 if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) { 332 debugfs_create_file("rx_stats", 0400, dir, link_sta, 333 &fops_rx_stats); 334 debugfs_create_file("reset_rx_stats", 0200, dir, link_sta, 335 &fops_reset_rx_stats); 336 } 337 } 338