0001
0002
0003
0004
0005 #include <linux/netdevice.h>
0006 #include <linux/types.h>
0007 #include <linux/skbuff.h>
0008 #include <linux/debugfs.h>
0009 #include <linux/ieee80211.h>
0010 #include <linux/export.h>
0011 #include <net/mac80211.h>
0012 #include "rc80211_minstrel_ht.h"
0013
0014 struct minstrel_debugfs_info {
0015 size_t len;
0016 char buf[];
0017 };
0018
0019 static ssize_t
0020 minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
0021 {
0022 struct minstrel_debugfs_info *ms;
0023
0024 ms = file->private_data;
0025 return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
0026 }
0027
0028 static int
0029 minstrel_stats_release(struct inode *inode, struct file *file)
0030 {
0031 kfree(file->private_data);
0032 return 0;
0033 }
0034
0035 static bool
0036 minstrel_ht_is_sample_rate(struct minstrel_ht_sta *mi, int idx)
0037 {
0038 int type, i;
0039
0040 for (type = 0; type < ARRAY_SIZE(mi->sample); type++)
0041 for (i = 0; i < MINSTREL_SAMPLE_RATES; i++)
0042 if (mi->sample[type].cur_sample_rates[i] == idx)
0043 return true;
0044 return false;
0045 }
0046
0047 static char *
0048 minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
0049 {
0050 const struct mcs_group *mg;
0051 unsigned int j, tp_max, tp_avg, eprob, tx_time;
0052 char htmode = '2';
0053 char gimode = 'L';
0054 u32 gflags;
0055
0056 if (!mi->supported[i])
0057 return p;
0058
0059 mg = &minstrel_mcs_groups[i];
0060 gflags = mg->flags;
0061
0062 if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
0063 htmode = '4';
0064 else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
0065 htmode = '8';
0066 if (gflags & IEEE80211_TX_RC_SHORT_GI)
0067 gimode = 'S';
0068
0069 for (j = 0; j < MCS_GROUP_RATES; j++) {
0070 struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
0071 int idx = MI_RATE(i, j);
0072 unsigned int duration;
0073
0074 if (!(mi->supported[i] & BIT(j)))
0075 continue;
0076
0077 if (gflags & IEEE80211_TX_RC_MCS) {
0078 p += sprintf(p, "HT%c0 ", htmode);
0079 p += sprintf(p, "%cGI ", gimode);
0080 p += sprintf(p, "%d ", mg->streams);
0081 } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
0082 p += sprintf(p, "VHT%c0 ", htmode);
0083 p += sprintf(p, "%cGI ", gimode);
0084 p += sprintf(p, "%d ", mg->streams);
0085 } else if (i == MINSTREL_OFDM_GROUP) {
0086 p += sprintf(p, "OFDM ");
0087 p += sprintf(p, "1 ");
0088 } else {
0089 p += sprintf(p, "CCK ");
0090 p += sprintf(p, "%cP ", j < 4 ? 'L' : 'S');
0091 p += sprintf(p, "1 ");
0092 }
0093
0094 *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
0095 *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
0096 *(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' ';
0097 *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
0098 *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
0099 *(p++) = minstrel_ht_is_sample_rate(mi, idx) ? 'S' : ' ';
0100
0101 if (gflags & IEEE80211_TX_RC_MCS) {
0102 p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
0103 } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
0104 p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
0105 } else {
0106 int r;
0107
0108 if (i == MINSTREL_OFDM_GROUP)
0109 r = minstrel_ofdm_bitrates[j % 8];
0110 else
0111 r = minstrel_cck_bitrates[j % 4];
0112
0113 p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
0114 }
0115
0116 p += sprintf(p, " %3u ", idx);
0117
0118
0119 duration = mg->duration[j];
0120 duration <<= mg->shift;
0121 tx_time = DIV_ROUND_CLOSEST(duration, 1000);
0122 p += sprintf(p, "%6u ", tx_time);
0123
0124 tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
0125 tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg);
0126 eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
0127
0128 p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u"
0129 " %3u %3u %-3u "
0130 "%9llu %-9llu\n",
0131 tp_max / 10, tp_max % 10,
0132 tp_avg / 10, tp_avg % 10,
0133 eprob / 10, eprob % 10,
0134 mrs->retry_count,
0135 mrs->last_success,
0136 mrs->last_attempts,
0137 (unsigned long long)mrs->succ_hist,
0138 (unsigned long long)mrs->att_hist);
0139 }
0140
0141 return p;
0142 }
0143
0144 static int
0145 minstrel_ht_stats_open(struct inode *inode, struct file *file)
0146 {
0147 struct minstrel_ht_sta *mi = inode->i_private;
0148 struct minstrel_debugfs_info *ms;
0149 unsigned int i;
0150 char *p;
0151
0152 ms = kmalloc(32768, GFP_KERNEL);
0153 if (!ms)
0154 return -ENOMEM;
0155
0156 file->private_data = ms;
0157 p = ms->buf;
0158
0159 p += sprintf(p, "\n");
0160 p += sprintf(p,
0161 " best ____________rate__________ ____statistics___ _____last____ ______sum-of________\n");
0162 p += sprintf(p,
0163 "mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts]\n");
0164
0165 p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
0166 for (i = 0; i < MINSTREL_CCK_GROUP; i++)
0167 p = minstrel_ht_stats_dump(mi, i, p);
0168 for (i++; i < ARRAY_SIZE(mi->groups); i++)
0169 p = minstrel_ht_stats_dump(mi, i, p);
0170
0171 p += sprintf(p, "\nTotal packet count:: ideal %d "
0172 "lookaround %d\n",
0173 max(0, (int) mi->total_packets - (int) mi->sample_packets),
0174 mi->sample_packets);
0175 if (mi->avg_ampdu_len)
0176 p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
0177 MINSTREL_TRUNC(mi->avg_ampdu_len),
0178 MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
0179 ms->len = p - ms->buf;
0180 WARN_ON(ms->len + sizeof(*ms) > 32768);
0181
0182 return nonseekable_open(inode, file);
0183 }
0184
0185 static const struct file_operations minstrel_ht_stat_fops = {
0186 .owner = THIS_MODULE,
0187 .open = minstrel_ht_stats_open,
0188 .read = minstrel_stats_read,
0189 .release = minstrel_stats_release,
0190 .llseek = no_llseek,
0191 };
0192
0193 static char *
0194 minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
0195 {
0196 const struct mcs_group *mg;
0197 unsigned int j, tp_max, tp_avg, eprob, tx_time;
0198 char htmode = '2';
0199 char gimode = 'L';
0200 u32 gflags;
0201
0202 if (!mi->supported[i])
0203 return p;
0204
0205 mg = &minstrel_mcs_groups[i];
0206 gflags = mg->flags;
0207
0208 if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
0209 htmode = '4';
0210 else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
0211 htmode = '8';
0212 if (gflags & IEEE80211_TX_RC_SHORT_GI)
0213 gimode = 'S';
0214
0215 for (j = 0; j < MCS_GROUP_RATES; j++) {
0216 struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
0217 int idx = MI_RATE(i, j);
0218 unsigned int duration;
0219
0220 if (!(mi->supported[i] & BIT(j)))
0221 continue;
0222
0223 if (gflags & IEEE80211_TX_RC_MCS) {
0224 p += sprintf(p, "HT%c0,", htmode);
0225 p += sprintf(p, "%cGI,", gimode);
0226 p += sprintf(p, "%d,", mg->streams);
0227 } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
0228 p += sprintf(p, "VHT%c0,", htmode);
0229 p += sprintf(p, "%cGI,", gimode);
0230 p += sprintf(p, "%d,", mg->streams);
0231 } else if (i == MINSTREL_OFDM_GROUP) {
0232 p += sprintf(p, "OFDM,,1,");
0233 } else {
0234 p += sprintf(p, "CCK,");
0235 p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S');
0236 p += sprintf(p, "1,");
0237 }
0238
0239 p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : ""));
0240 p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : ""));
0241 p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : ""));
0242 p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : ""));
0243 p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : ""));
0244 p += sprintf(p, "%s", (minstrel_ht_is_sample_rate(mi, idx) ? "S" : ""));
0245
0246 if (gflags & IEEE80211_TX_RC_MCS) {
0247 p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j);
0248 } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
0249 p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams);
0250 } else {
0251 int r;
0252
0253 if (i == MINSTREL_OFDM_GROUP)
0254 r = minstrel_ofdm_bitrates[j % 8];
0255 else
0256 r = minstrel_cck_bitrates[j % 4];
0257
0258 p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10);
0259 }
0260
0261 p += sprintf(p, "%u,", idx);
0262
0263 duration = mg->duration[j];
0264 duration <<= mg->shift;
0265 tx_time = DIV_ROUND_CLOSEST(duration, 1000);
0266 p += sprintf(p, "%u,", tx_time);
0267
0268 tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
0269 tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg);
0270 eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
0271
0272 p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,"
0273 "%u,%llu,%llu,",
0274 tp_max / 10, tp_max % 10,
0275 tp_avg / 10, tp_avg % 10,
0276 eprob / 10, eprob % 10,
0277 mrs->retry_count,
0278 mrs->last_success,
0279 mrs->last_attempts,
0280 (unsigned long long)mrs->succ_hist,
0281 (unsigned long long)mrs->att_hist);
0282 p += sprintf(p, "%d,%d,%d.%d\n",
0283 max(0, (int) mi->total_packets -
0284 (int) mi->sample_packets),
0285 mi->sample_packets,
0286 MINSTREL_TRUNC(mi->avg_ampdu_len),
0287 MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
0288 }
0289
0290 return p;
0291 }
0292
0293 static int
0294 minstrel_ht_stats_csv_open(struct inode *inode, struct file *file)
0295 {
0296 struct minstrel_ht_sta *mi = inode->i_private;
0297 struct minstrel_debugfs_info *ms;
0298 unsigned int i;
0299 char *p;
0300
0301 ms = kmalloc(32768, GFP_KERNEL);
0302 if (!ms)
0303 return -ENOMEM;
0304
0305 file->private_data = ms;
0306
0307 p = ms->buf;
0308
0309 p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p);
0310 for (i = 0; i < MINSTREL_CCK_GROUP; i++)
0311 p = minstrel_ht_stats_csv_dump(mi, i, p);
0312 for (i++; i < ARRAY_SIZE(mi->groups); i++)
0313 p = minstrel_ht_stats_csv_dump(mi, i, p);
0314
0315 ms->len = p - ms->buf;
0316 WARN_ON(ms->len + sizeof(*ms) > 32768);
0317
0318 return nonseekable_open(inode, file);
0319 }
0320
0321 static const struct file_operations minstrel_ht_stat_csv_fops = {
0322 .owner = THIS_MODULE,
0323 .open = minstrel_ht_stats_csv_open,
0324 .read = minstrel_stats_read,
0325 .release = minstrel_stats_release,
0326 .llseek = no_llseek,
0327 };
0328
0329 void
0330 minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
0331 {
0332 debugfs_create_file("rc_stats", 0444, dir, priv_sta,
0333 &minstrel_ht_stat_fops);
0334 debugfs_create_file("rc_stats_csv", 0444, dir, priv_sta,
0335 &minstrel_ht_stat_csv_fops);
0336 }