linux/net/mac80211/rc80211_minstrel_ht_debugfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
   4 */
   5#include <linux/netdevice.h>
   6#include <linux/types.h>
   7#include <linux/skbuff.h>
   8#include <linux/debugfs.h>
   9#include <linux/ieee80211.h>
  10#include <linux/export.h>
  11#include <net/mac80211.h>
  12#include "rc80211_minstrel_ht.h"
  13
  14struct minstrel_debugfs_info {
  15        size_t len;
  16        char buf[];
  17};
  18
  19static ssize_t
  20minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
  21{
  22        struct minstrel_debugfs_info *ms;
  23
  24        ms = file->private_data;
  25        return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len);
  26}
  27
  28static int
  29minstrel_stats_release(struct inode *inode, struct file *file)
  30{
  31        kfree(file->private_data);
  32        return 0;
  33}
  34
  35static bool
  36minstrel_ht_is_sample_rate(struct minstrel_ht_sta *mi, int idx)
  37{
  38        int type, i;
  39
  40        for (type = 0; type < ARRAY_SIZE(mi->sample); type++)
  41                for (i = 0; i < MINSTREL_SAMPLE_RATES; i++)
  42                        if (mi->sample[type].cur_sample_rates[i] == idx)
  43                                return true;
  44        return false;
  45}
  46
  47static char *
  48minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
  49{
  50        const struct mcs_group *mg;
  51        unsigned int j, tp_max, tp_avg, eprob, tx_time;
  52        char htmode = '2';
  53        char gimode = 'L';
  54        u32 gflags;
  55
  56        if (!mi->supported[i])
  57                return p;
  58
  59        mg = &minstrel_mcs_groups[i];
  60        gflags = mg->flags;
  61
  62        if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
  63                htmode = '4';
  64        else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
  65                htmode = '8';
  66        if (gflags & IEEE80211_TX_RC_SHORT_GI)
  67                gimode = 'S';
  68
  69        for (j = 0; j < MCS_GROUP_RATES; j++) {
  70                struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
  71                int idx = MI_RATE(i, j);
  72                unsigned int duration;
  73
  74                if (!(mi->supported[i] & BIT(j)))
  75                        continue;
  76
  77                if (gflags & IEEE80211_TX_RC_MCS) {
  78                        p += sprintf(p, "HT%c0  ", htmode);
  79                        p += sprintf(p, "%cGI  ", gimode);
  80                        p += sprintf(p, "%d  ", mg->streams);
  81                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
  82                        p += sprintf(p, "VHT%c0 ", htmode);
  83                        p += sprintf(p, "%cGI ", gimode);
  84                        p += sprintf(p, "%d  ", mg->streams);
  85                } else if (i == MINSTREL_OFDM_GROUP) {
  86                        p += sprintf(p, "OFDM       ");
  87                        p += sprintf(p, "1 ");
  88                } else {
  89                        p += sprintf(p, "CCK    ");
  90                        p += sprintf(p, "%cP  ", j < 4 ? 'L' : 'S');
  91                        p += sprintf(p, "1 ");
  92                }
  93
  94                *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
  95                *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
  96                *(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' ';
  97                *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
  98                *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
  99                *(p++) = minstrel_ht_is_sample_rate(mi, idx) ? 'S' : ' ';
 100
 101                if (gflags & IEEE80211_TX_RC_MCS) {
 102                        p += sprintf(p, "  MCS%-2u", (mg->streams - 1) * 8 + j);
 103                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
 104                        p += sprintf(p, "  MCS%-1u/%1u", j, mg->streams);
 105                } else {
 106                        int r;
 107
 108                        if (i == MINSTREL_OFDM_GROUP)
 109                                r = minstrel_ofdm_bitrates[j % 8];
 110                        else
 111                                r = minstrel_cck_bitrates[j % 4];
 112
 113                        p += sprintf(p, "   %2u.%1uM", r / 10, r % 10);
 114                }
 115
 116                p += sprintf(p, "  %3u  ", idx);
 117
 118                /* tx_time[rate(i)] in usec */
 119                duration = mg->duration[j];
 120                duration <<= mg->shift;
 121                tx_time = DIV_ROUND_CLOSEST(duration, 1000);
 122                p += sprintf(p, "%6u  ", tx_time);
 123
 124                tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
 125                tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg);
 126                eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
 127
 128                p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u"
 129                                "     %3u   %3u %-3u   "
 130                                "%9llu   %-9llu\n",
 131                                tp_max / 10, tp_max % 10,
 132                                tp_avg / 10, tp_avg % 10,
 133                                eprob / 10, eprob % 10,
 134                                mrs->retry_count,
 135                                mrs->last_success,
 136                                mrs->last_attempts,
 137                                (unsigned long long)mrs->succ_hist,
 138                                (unsigned long long)mrs->att_hist);
 139        }
 140
 141        return p;
 142}
 143
 144static int
 145minstrel_ht_stats_open(struct inode *inode, struct file *file)
 146{
 147        struct minstrel_ht_sta *mi = inode->i_private;
 148        struct minstrel_debugfs_info *ms;
 149        unsigned int i;
 150        char *p;
 151
 152        ms = kmalloc(32768, GFP_KERNEL);
 153        if (!ms)
 154                return -ENOMEM;
 155
 156        file->private_data = ms;
 157        p = ms->buf;
 158
 159        p += sprintf(p, "\n");
 160        p += sprintf(p,
 161                     "              best    ____________rate__________    ____statistics___    _____last____    ______sum-of________\n");
 162        p += sprintf(p,
 163                     "mode guard #  rate   [name   idx airtime  max_tp]  [avg(tp) avg(prob)]  [retry|suc|att]  [#success | #attempts]\n");
 164
 165        p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
 166        for (i = 0; i < MINSTREL_CCK_GROUP; i++)
 167                p = minstrel_ht_stats_dump(mi, i, p);
 168        for (i++; i < ARRAY_SIZE(mi->groups); i++)
 169                p = minstrel_ht_stats_dump(mi, i, p);
 170
 171        p += sprintf(p, "\nTotal packet count::    ideal %d      "
 172                        "lookaround %d\n",
 173                        max(0, (int) mi->total_packets - (int) mi->sample_packets),
 174                        mi->sample_packets);
 175        if (mi->avg_ampdu_len)
 176                p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n",
 177                        MINSTREL_TRUNC(mi->avg_ampdu_len),
 178                        MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
 179        ms->len = p - ms->buf;
 180        WARN_ON(ms->len + sizeof(*ms) > 32768);
 181
 182        return nonseekable_open(inode, file);
 183}
 184
 185static const struct file_operations minstrel_ht_stat_fops = {
 186        .owner = THIS_MODULE,
 187        .open = minstrel_ht_stats_open,
 188        .read = minstrel_stats_read,
 189        .release = minstrel_stats_release,
 190        .llseek = no_llseek,
 191};
 192
 193static char *
 194minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
 195{
 196        const struct mcs_group *mg;
 197        unsigned int j, tp_max, tp_avg, eprob, tx_time;
 198        char htmode = '2';
 199        char gimode = 'L';
 200        u32 gflags;
 201
 202        if (!mi->supported[i])
 203                return p;
 204
 205        mg = &minstrel_mcs_groups[i];
 206        gflags = mg->flags;
 207
 208        if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
 209                htmode = '4';
 210        else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
 211                htmode = '8';
 212        if (gflags & IEEE80211_TX_RC_SHORT_GI)
 213                gimode = 'S';
 214
 215        for (j = 0; j < MCS_GROUP_RATES; j++) {
 216                struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
 217                int idx = MI_RATE(i, j);
 218                unsigned int duration;
 219
 220                if (!(mi->supported[i] & BIT(j)))
 221                        continue;
 222
 223                if (gflags & IEEE80211_TX_RC_MCS) {
 224                        p += sprintf(p, "HT%c0,", htmode);
 225                        p += sprintf(p, "%cGI,", gimode);
 226                        p += sprintf(p, "%d,", mg->streams);
 227                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
 228                        p += sprintf(p, "VHT%c0,", htmode);
 229                        p += sprintf(p, "%cGI,", gimode);
 230                        p += sprintf(p, "%d,", mg->streams);
 231                } else if (i == MINSTREL_OFDM_GROUP) {
 232                        p += sprintf(p, "OFDM,,1,");
 233                } else {
 234                        p += sprintf(p, "CCK,");
 235                        p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S');
 236                        p += sprintf(p, "1,");
 237                }
 238
 239                p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : ""));
 240                p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : ""));
 241                p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : ""));
 242                p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : ""));
 243                p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : ""));
 244                p += sprintf(p, "%s", (minstrel_ht_is_sample_rate(mi, idx) ? "S" : ""));
 245
 246                if (gflags & IEEE80211_TX_RC_MCS) {
 247                        p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j);
 248                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
 249                        p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams);
 250                } else {
 251                        int r;
 252
 253                        if (i == MINSTREL_OFDM_GROUP)
 254                                r = minstrel_ofdm_bitrates[j % 8];
 255                        else
 256                                r = minstrel_cck_bitrates[j % 4];
 257
 258                        p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10);
 259                }
 260
 261                p += sprintf(p, "%u,", idx);
 262
 263                duration = mg->duration[j];
 264                duration <<= mg->shift;
 265                tx_time = DIV_ROUND_CLOSEST(duration, 1000);
 266                p += sprintf(p, "%u,", tx_time);
 267
 268                tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
 269                tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg);
 270                eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
 271
 272                p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,"
 273                                "%u,%llu,%llu,",
 274                                tp_max / 10, tp_max % 10,
 275                                tp_avg / 10, tp_avg % 10,
 276                                eprob / 10, eprob % 10,
 277                                mrs->retry_count,
 278                                mrs->last_success,
 279                                mrs->last_attempts,
 280                                (unsigned long long)mrs->succ_hist,
 281                                (unsigned long long)mrs->att_hist);
 282                p += sprintf(p, "%d,%d,%d.%d\n",
 283                                max(0, (int) mi->total_packets -
 284                                (int) mi->sample_packets),
 285                                mi->sample_packets,
 286                                MINSTREL_TRUNC(mi->avg_ampdu_len),
 287                                MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
 288        }
 289
 290        return p;
 291}
 292
 293static int
 294minstrel_ht_stats_csv_open(struct inode *inode, struct file *file)
 295{
 296        struct minstrel_ht_sta *mi = inode->i_private;
 297        struct minstrel_debugfs_info *ms;
 298        unsigned int i;
 299        char *p;
 300
 301        ms = kmalloc(32768, GFP_KERNEL);
 302        if (!ms)
 303                return -ENOMEM;
 304
 305        file->private_data = ms;
 306
 307        p = ms->buf;
 308
 309        p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p);
 310        for (i = 0; i < MINSTREL_CCK_GROUP; i++)
 311                p = minstrel_ht_stats_csv_dump(mi, i, p);
 312        for (i++; i < ARRAY_SIZE(mi->groups); i++)
 313                p = minstrel_ht_stats_csv_dump(mi, i, p);
 314
 315        ms->len = p - ms->buf;
 316        WARN_ON(ms->len + sizeof(*ms) > 32768);
 317
 318        return nonseekable_open(inode, file);
 319}
 320
 321static const struct file_operations minstrel_ht_stat_csv_fops = {
 322        .owner = THIS_MODULE,
 323        .open = minstrel_ht_stats_csv_open,
 324        .read = minstrel_stats_read,
 325        .release = minstrel_stats_release,
 326        .llseek = no_llseek,
 327};
 328
 329void
 330minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 331{
 332        debugfs_create_file("rc_stats", 0444, dir, priv_sta,
 333                            &minstrel_ht_stat_fops);
 334        debugfs_create_file("rc_stats_csv", 0444, dir, priv_sta,
 335                            &minstrel_ht_stat_csv_fops);
 336}
 337