2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-05-13 18:48:03 +04:00
/*
* Copyright ( C ) 2010 Felix Fietkau < nbd @ openwrt . org >
*/
# include <linux/netdevice.h>
# include <linux/types.h>
# include <linux/skbuff.h>
# include <linux/debugfs.h>
# include <linux/ieee80211.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2010-05-13 18:48:03 +04:00
# include <net/mac80211.h>
# include "rc80211_minstrel_ht.h"
2021-01-15 15:02:36 +03:00
struct minstrel_debugfs_info {
size_t len ;
char buf [ ] ;
} ;
2018-10-06 20:35:01 +03:00
static ssize_t
minstrel_stats_read ( struct file * file , char __user * buf , size_t len , loff_t * ppos )
{
struct minstrel_debugfs_info * ms ;
ms = file - > private_data ;
return simple_read_from_buffer ( buf , len , ppos , ms - > buf , ms - > len ) ;
}
static int
minstrel_stats_release ( struct inode * inode , struct file * file )
{
kfree ( file - > private_data ) ;
return 0 ;
}
2021-01-27 08:57:34 +03:00
static bool
minstrel_ht_is_sample_rate ( struct minstrel_ht_sta * mi , int idx )
{
int type , i ;
for ( type = 0 ; type < ARRAY_SIZE ( mi - > sample ) ; type + + )
for ( i = 0 ; i < MINSTREL_SAMPLE_RATES ; i + + )
if ( mi - > sample [ type ] . cur_sample_rates [ i ] = = idx )
return true ;
return false ;
}
2013-02-13 13:51:08 +04:00
static char *
minstrel_ht_stats_dump ( struct minstrel_ht_sta * mi , int i , char * p )
{
const struct mcs_group * mg ;
2016-12-14 22:46:58 +03:00
unsigned int j , tp_max , tp_avg , eprob , tx_time ;
2013-02-13 13:51:08 +04:00
char htmode = ' 2 ' ;
char gimode = ' L ' ;
2014-10-22 20:20:37 +04:00
u32 gflags ;
2013-02-13 13:51:08 +04:00
2016-12-14 22:46:54 +03:00
if ( ! mi - > supported [ i ] )
2013-02-13 13:51:08 +04:00
return p ;
mg = & minstrel_mcs_groups [ i ] ;
2014-10-22 20:20:37 +04:00
gflags = mg - > flags ;
if ( gflags & IEEE80211_TX_RC_40_MHZ_WIDTH )
2013-02-13 13:51:08 +04:00
htmode = ' 4 ' ;
2014-10-22 20:20:37 +04:00
else if ( gflags & IEEE80211_TX_RC_80_MHZ_WIDTH )
2014-10-21 12:38:38 +04:00
htmode = ' 8 ' ;
2014-10-22 20:20:37 +04:00
if ( gflags & IEEE80211_TX_RC_SHORT_GI )
2013-02-13 13:51:08 +04:00
gimode = ' S ' ;
for ( j = 0 ; j < MCS_GROUP_RATES ; j + + ) {
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs = & mi - > groups [ i ] . rates [ j ] ;
2021-01-27 08:57:30 +03:00
int idx = MI_RATE ( i , j ) ;
2018-10-06 20:35:02 +03:00
unsigned int duration ;
2013-02-13 13:51:08 +04:00
2016-12-14 22:46:54 +03:00
if ( ! ( mi - > supported [ i ] & BIT ( j ) ) )
2013-02-13 13:51:08 +04:00
continue ;
2015-03-24 23:09:35 +03:00
if ( gflags & IEEE80211_TX_RC_MCS ) {
p + = sprintf ( p , " HT%c0 " , htmode ) ;
p + = sprintf ( p , " %cGI " , gimode ) ;
p + = sprintf ( p , " %d " , mg - > streams ) ;
} else if ( gflags & IEEE80211_TX_RC_VHT_MCS ) {
p + = sprintf ( p , " VHT%c0 " , htmode ) ;
p + = sprintf ( p , " %cGI " , gimode ) ;
p + = sprintf ( p , " %d " , mg - > streams ) ;
2021-01-15 15:02:35 +03:00
} else if ( i = = MINSTREL_OFDM_GROUP ) {
p + = sprintf ( p , " OFDM " ) ;
p + = sprintf ( p , " 1 " ) ;
2015-03-24 23:09:35 +03:00
} else {
p + = sprintf ( p , " CCK " ) ;
p + = sprintf ( p , " %cP " , j < 4 ? ' L ' : ' S ' ) ;
p + = sprintf ( p , " 1 " ) ;
}
2013-02-13 13:51:08 +04:00
2014-09-10 01:22:14 +04:00
* ( p + + ) = ( idx = = mi - > max_tp_rate [ 0 ] ) ? ' A ' : ' ' ;
* ( p + + ) = ( idx = = mi - > max_tp_rate [ 1 ] ) ? ' B ' : ' ' ;
* ( p + + ) = ( idx = = mi - > max_tp_rate [ 2 ] ) ? ' C ' : ' ' ;
* ( p + + ) = ( idx = = mi - > max_tp_rate [ 3 ] ) ? ' D ' : ' ' ;
2013-02-13 13:51:08 +04:00
* ( p + + ) = ( idx = = mi - > max_prob_rate ) ? ' P ' : ' ' ;
2021-01-27 08:57:34 +03:00
* ( p + + ) = minstrel_ht_is_sample_rate ( mi , idx ) ? ' S ' : ' ' ;
2013-02-13 13:51:08 +04:00
2014-10-22 20:20:37 +04:00
if ( gflags & IEEE80211_TX_RC_MCS ) {
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " MCS%-2u " , ( mg - > streams - 1 ) * 8 + j ) ;
2014-10-22 20:20:37 +04:00
} else if ( gflags & IEEE80211_TX_RC_VHT_MCS ) {
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " MCS%-1u/%1u " , j , mg - > streams ) ;
2013-02-13 13:51:08 +04:00
} else {
2021-01-15 15:02:35 +03:00
int r ;
if ( i = = MINSTREL_OFDM_GROUP )
r = minstrel_ofdm_bitrates [ j % 8 ] ;
else
r = minstrel_cck_bitrates [ j % 4 ] ;
2014-10-22 20:20:37 +04:00
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " %2u.%1uM " , r / 10 , r % 10 ) ;
2013-02-13 13:51:08 +04:00
}
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " %3u " , idx ) ;
/* tx_time[rate(i)] in usec */
2018-10-06 20:35:02 +03:00
duration = mg - > duration [ j ] ;
duration < < = mg - > shift ;
tx_time = DIV_ROUND_CLOSEST ( duration , 1000 ) ;
2015-03-24 23:09:41 +03:00
p + = sprintf ( p , " %6u " , tx_time ) ;
2015-03-24 23:09:35 +03:00
2015-03-24 23:09:41 +03:00
tp_max = minstrel_ht_get_tp_avg ( mi , i , j , MINSTREL_FRAC ( 100 , 100 ) ) ;
2019-10-08 20:11:39 +03:00
tp_avg = minstrel_ht_get_tp_avg ( mi , i , j , mrs - > prob_avg ) ;
eprob = MINSTREL_TRUNC ( mrs - > prob_avg * 1000 ) ;
2013-02-13 13:51:08 +04:00
2018-10-06 20:35:07 +03:00
p + = sprintf ( p , " %4u.%1u %4u.%1u %3u.%1u "
2016-12-14 22:46:58 +03:00
" %3u %3u %-3u "
2015-03-24 23:09:43 +03:00
" %9llu %-9llu \n " ,
2015-03-24 23:09:41 +03:00
tp_max / 10 , tp_max % 10 ,
2015-03-24 23:09:40 +03:00
tp_avg / 10 , tp_avg % 10 ,
2013-02-13 13:51:08 +04:00
eprob / 10 , eprob % 10 ,
2015-03-24 23:09:39 +03:00
mrs - > retry_count ,
mrs - > last_success ,
mrs - > last_attempts ,
( unsigned long long ) mrs - > succ_hist ,
( unsigned long long ) mrs - > att_hist ) ;
2013-02-13 13:51:08 +04:00
}
return p ;
}
2010-05-13 18:48:03 +04:00
static int
minstrel_ht_stats_open ( struct inode * inode , struct file * file )
{
2021-01-15 15:02:36 +03:00
struct minstrel_ht_sta * mi = inode - > i_private ;
2010-05-13 18:48:03 +04:00
struct minstrel_debugfs_info * ms ;
2013-02-13 13:51:08 +04:00
unsigned int i ;
2015-03-24 23:09:37 +03:00
char * p ;
2010-05-13 18:48:03 +04:00
2014-10-21 12:38:38 +04:00
ms = kmalloc ( 32768 , GFP_KERNEL ) ;
2010-05-13 18:48:03 +04:00
if ( ! ms )
return - ENOMEM ;
file - > private_data = ms ;
p = ms - > buf ;
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " \n " ) ;
2015-09-23 11:29:20 +03:00
p + = sprintf ( p ,
2021-01-27 08:57:34 +03:00
" best ____________rate__________ ____statistics___ _____last____ ______sum-of________ \n " ) ;
2015-09-23 11:29:20 +03:00
p + = sprintf ( p ,
2021-01-27 08:57:34 +03:00
" mode guard # rate [name idx airtime max_tp] [avg(tp) avg(prob)] [retry|suc|att] [#success | #attempts] \n " ) ;
2014-10-20 12:54:36 +04:00
2014-10-20 17:46:00 +04:00
p = minstrel_ht_stats_dump ( mi , MINSTREL_CCK_GROUP , p ) ;
for ( i = 0 ; i < MINSTREL_CCK_GROUP ; i + + )
2013-02-13 13:51:08 +04:00
p = minstrel_ht_stats_dump ( mi , i , p ) ;
2014-10-21 12:38:38 +04:00
for ( i + + ; i < ARRAY_SIZE ( mi - > groups ) ; i + + )
p = minstrel_ht_stats_dump ( mi , i , p ) ;
2010-05-13 18:48:03 +04:00
p + = sprintf ( p , " \n Total packet count:: ideal %d "
" lookaround %d \n " ,
max ( 0 , ( int ) mi - > total_packets - ( int ) mi - > sample_packets ) ,
mi - > sample_packets ) ;
2019-01-17 00:32:12 +03:00
if ( mi - > avg_ampdu_len )
p + = sprintf ( p , " Average # of aggregated frames per A-MPDU: %d.%d \n " ,
MINSTREL_TRUNC ( mi - > avg_ampdu_len ) ,
MINSTREL_TRUNC ( mi - > avg_ampdu_len * 10 ) % 10 ) ;
2010-05-13 18:48:03 +04:00
ms - > len = p - ms - > buf ;
2014-10-21 12:38:38 +04:00
WARN_ON ( ms - > len + sizeof ( * ms ) > 32768 ) ;
2014-10-20 12:54:36 +04:00
2010-08-15 20:32:42 +04:00
return nonseekable_open ( inode , file ) ;
2010-05-13 18:48:03 +04:00
}
static const struct file_operations minstrel_ht_stat_fops = {
. owner = THIS_MODULE ,
. open = minstrel_ht_stats_open ,
. read = minstrel_stats_read ,
. release = minstrel_stats_release ,
2010-08-15 20:32:42 +04:00
. llseek = no_llseek ,
2010-05-13 18:48:03 +04:00
} ;
2015-03-24 23:09:37 +03:00
static char *
minstrel_ht_stats_csv_dump ( struct minstrel_ht_sta * mi , int i , char * p )
{
const struct mcs_group * mg ;
2016-12-14 22:46:58 +03:00
unsigned int j , tp_max , tp_avg , eprob , tx_time ;
2015-03-24 23:09:37 +03:00
char htmode = ' 2 ' ;
char gimode = ' L ' ;
u32 gflags ;
2016-12-14 22:46:54 +03:00
if ( ! mi - > supported [ i ] )
2015-03-24 23:09:37 +03:00
return p ;
mg = & minstrel_mcs_groups [ i ] ;
gflags = mg - > flags ;
if ( gflags & IEEE80211_TX_RC_40_MHZ_WIDTH )
htmode = ' 4 ' ;
else if ( gflags & IEEE80211_TX_RC_80_MHZ_WIDTH )
htmode = ' 8 ' ;
if ( gflags & IEEE80211_TX_RC_SHORT_GI )
gimode = ' S ' ;
for ( j = 0 ; j < MCS_GROUP_RATES ; j + + ) {
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs = & mi - > groups [ i ] . rates [ j ] ;
2021-01-27 08:57:30 +03:00
int idx = MI_RATE ( i , j ) ;
2018-10-06 20:35:02 +03:00
unsigned int duration ;
2015-03-24 23:09:37 +03:00
2016-12-14 22:46:54 +03:00
if ( ! ( mi - > supported [ i ] & BIT ( j ) ) )
2015-03-24 23:09:37 +03:00
continue ;
if ( gflags & IEEE80211_TX_RC_MCS ) {
p + = sprintf ( p , " HT%c0, " , htmode ) ;
p + = sprintf ( p , " %cGI, " , gimode ) ;
p + = sprintf ( p , " %d, " , mg - > streams ) ;
} else if ( gflags & IEEE80211_TX_RC_VHT_MCS ) {
p + = sprintf ( p , " VHT%c0, " , htmode ) ;
p + = sprintf ( p , " %cGI, " , gimode ) ;
p + = sprintf ( p , " %d, " , mg - > streams ) ;
2021-01-15 15:02:35 +03:00
} else if ( i = = MINSTREL_OFDM_GROUP ) {
p + = sprintf ( p , " OFDM,,1, " ) ;
2015-03-24 23:09:37 +03:00
} else {
p + = sprintf ( p , " CCK, " ) ;
p + = sprintf ( p , " %cP, " , j < 4 ? ' L ' : ' S ' ) ;
p + = sprintf ( p , " 1, " ) ;
}
p + = sprintf ( p , " %s " , ( ( idx = = mi - > max_tp_rate [ 0 ] ) ? " A " : " " ) ) ;
p + = sprintf ( p , " %s " , ( ( idx = = mi - > max_tp_rate [ 1 ] ) ? " B " : " " ) ) ;
p + = sprintf ( p , " %s " , ( ( idx = = mi - > max_tp_rate [ 2 ] ) ? " C " : " " ) ) ;
p + = sprintf ( p , " %s " , ( ( idx = = mi - > max_tp_rate [ 3 ] ) ? " D " : " " ) ) ;
p + = sprintf ( p , " %s " , ( ( idx = = mi - > max_prob_rate ) ? " P " : " " ) ) ;
2021-01-27 08:57:34 +03:00
p + = sprintf ( p , " %s " , ( minstrel_ht_is_sample_rate ( mi , idx ) ? " S " : " " ) ) ;
2015-03-24 23:09:37 +03:00
if ( gflags & IEEE80211_TX_RC_MCS ) {
p + = sprintf ( p , " ,MCS%-2u, " , ( mg - > streams - 1 ) * 8 + j ) ;
} else if ( gflags & IEEE80211_TX_RC_VHT_MCS ) {
p + = sprintf ( p , " ,MCS%-1u/%1u, " , j , mg - > streams ) ;
} else {
2021-01-15 15:02:35 +03:00
int r ;
if ( i = = MINSTREL_OFDM_GROUP )
r = minstrel_ofdm_bitrates [ j % 8 ] ;
else
r = minstrel_cck_bitrates [ j % 4 ] ;
2015-03-24 23:09:37 +03:00
p + = sprintf ( p , " ,%2u.%1uM, " , r / 10 , r % 10 ) ;
}
p + = sprintf ( p , " %u, " , idx ) ;
2018-10-06 20:35:02 +03:00
duration = mg - > duration [ j ] ;
duration < < = mg - > shift ;
tx_time = DIV_ROUND_CLOSEST ( duration , 1000 ) ;
2015-03-24 23:09:37 +03:00
p + = sprintf ( p , " %u, " , tx_time ) ;
2015-03-24 23:09:41 +03:00
tp_max = minstrel_ht_get_tp_avg ( mi , i , j , MINSTREL_FRAC ( 100 , 100 ) ) ;
2019-10-08 20:11:39 +03:00
tp_avg = minstrel_ht_get_tp_avg ( mi , i , j , mrs - > prob_avg ) ;
eprob = MINSTREL_TRUNC ( mrs - > prob_avg * 1000 ) ;
2015-03-24 23:09:37 +03:00
2018-10-06 20:35:07 +03:00
p + = sprintf ( p , " %u.%u,%u.%u,%u.%u,%u,%u, "
2015-03-24 23:09:43 +03:00
" %u,%llu,%llu, " ,
2015-03-24 23:09:41 +03:00
tp_max / 10 , tp_max % 10 ,
2015-03-24 23:09:40 +03:00
tp_avg / 10 , tp_avg % 10 ,
2015-03-24 23:09:37 +03:00
eprob / 10 , eprob % 10 ,
2015-03-24 23:09:39 +03:00
mrs - > retry_count ,
mrs - > last_success ,
mrs - > last_attempts ,
( unsigned long long ) mrs - > succ_hist ,
( unsigned long long ) mrs - > att_hist ) ;
2015-03-24 23:09:37 +03:00
p + = sprintf ( p , " %d,%d,%d.%d \n " ,
max ( 0 , ( int ) mi - > total_packets -
( int ) mi - > sample_packets ) ,
mi - > sample_packets ,
MINSTREL_TRUNC ( mi - > avg_ampdu_len ) ,
MINSTREL_TRUNC ( mi - > avg_ampdu_len * 10 ) % 10 ) ;
}
return p ;
}
static int
minstrel_ht_stats_csv_open ( struct inode * inode , struct file * file )
{
2021-01-15 15:02:36 +03:00
struct minstrel_ht_sta * mi = inode - > i_private ;
2015-03-24 23:09:37 +03:00
struct minstrel_debugfs_info * ms ;
unsigned int i ;
char * p ;
ms = kmalloc ( 32768 , GFP_KERNEL ) ;
if ( ! ms )
return - ENOMEM ;
file - > private_data = ms ;
p = ms - > buf ;
p = minstrel_ht_stats_csv_dump ( mi , MINSTREL_CCK_GROUP , p ) ;
for ( i = 0 ; i < MINSTREL_CCK_GROUP ; i + + )
p = minstrel_ht_stats_csv_dump ( mi , i , p ) ;
for ( i + + ; i < ARRAY_SIZE ( mi - > groups ) ; i + + )
p = minstrel_ht_stats_csv_dump ( mi , i , p ) ;
ms - > len = p - ms - > buf ;
WARN_ON ( ms - > len + sizeof ( * ms ) > 32768 ) ;
return nonseekable_open ( inode , file ) ;
}
static const struct file_operations minstrel_ht_stat_csv_fops = {
. owner = THIS_MODULE ,
. open = minstrel_ht_stats_csv_open ,
. read = minstrel_stats_read ,
. release = minstrel_stats_release ,
. llseek = no_llseek ,
} ;
2010-05-13 18:48:03 +04:00
void
minstrel_ht_add_sta_debugfs ( void * priv , void * priv_sta , struct dentry * dir )
{
2021-01-15 15:02:36 +03:00
debugfs_create_file ( " rc_stats " , 0444 , dir , priv_sta ,
2018-10-06 20:35:00 +03:00
& minstrel_ht_stat_fops ) ;
2021-01-15 15:02:36 +03:00
debugfs_create_file ( " rc_stats_csv " , 0444 , dir , priv_sta ,
2018-10-06 20:35:00 +03:00
& minstrel_ht_stat_csv_fops ) ;
2010-05-13 18:48:03 +04:00
}