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