Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
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         /* tx_time[rate(i)] in usec */
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 }