2010-05-13 18:48:03 +04:00
/*
* Copyright ( C ) 2010 Felix Fietkau < nbd @ openwrt . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# 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.h"
# include "rc80211_minstrel_ht.h"
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 ;
2015-03-24 23:09:35 +03:00
unsigned int j , tp , prob , 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
if ( ! mi - > groups [ i ] . supported )
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 ] ;
2013-02-13 13:51:08 +04:00
static const int bitrates [ 4 ] = { 10 , 20 , 55 , 110 } ;
int idx = i * MCS_GROUP_RATES + j ;
if ( ! ( mi - > groups [ i ] . supported & BIT ( j ) ) )
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 ) ;
} 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 ' : ' ' ;
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 {
2014-10-22 20:20:37 +04:00
int r = bitrates [ j % 4 ] ;
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 */
tx_time = DIV_ROUND_CLOSEST ( mg - > duration [ j ] , 1000 ) ;
p + = sprintf ( p , " %6u " , tx_time ) ;
2015-03-24 23:09:39 +03:00
tp = mrs - > cur_tp / 10 ;
prob = MINSTREL_TRUNC ( mrs - > cur_prob * 1000 ) ;
eprob = MINSTREL_TRUNC ( mrs - > prob_ewma * 1000 ) ;
2013-02-13 13:51:08 +04:00
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " %4u.%1u %3u.%1u %3u.%1u "
" %3u %3u %-3u %9llu %-9llu \n " ,
2013-02-13 13:51:08 +04:00
tp / 10 , tp % 10 ,
eprob / 10 , eprob % 10 ,
prob / 10 , prob % 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 )
{
struct minstrel_ht_sta_priv * msp = inode - > i_private ;
struct minstrel_ht_sta * mi = & msp - > ht ;
struct minstrel_debugfs_info * ms ;
2013-02-13 13:51:08 +04:00
unsigned int i ;
2010-05-13 18:48:03 +04:00
int ret ;
2015-03-24 23:09:37 +03:00
char * p ;
2010-05-13 18:48:03 +04:00
if ( ! msp - > is_ht ) {
inode - > i_private = & msp - > legacy ;
ret = minstrel_stats_open ( inode , file ) ;
inode - > i_private = msp ;
return ret ;
}
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 " ) ;
p + = sprintf ( p , " best ________rate______ "
" __statistics__ ________last_______ "
" ______sum-of________ \n " ) ;
p + = sprintf ( p , " mode guard # rate [name idx airtime] [ ø(tp) "
" ø(prob)] [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 ) ;
2015-03-24 23:09:35 +03:00
p + = sprintf ( p , " Average # of aggregated frames per A-MPDU: %d.%d \n " ,
2010-05-13 18:48:03 +04:00
MINSTREL_TRUNC ( mi - > avg_ampdu_len ) ,
MINSTREL_TRUNC ( mi - > avg_ampdu_len * 10 ) % 10 ) ;
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 ;
unsigned int j , tp , prob , eprob , tx_time ;
char htmode = ' 2 ' ;
char gimode = ' L ' ;
u32 gflags ;
if ( ! mi - > groups [ i ] . supported )
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 ] ;
2015-03-24 23:09:37 +03:00
static const int bitrates [ 4 ] = { 10 , 20 , 55 , 110 } ;
int idx = i * MCS_GROUP_RATES + j ;
if ( ! ( mi - > groups [ i ] . supported & BIT ( j ) ) )
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 ) ;
} 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 " : " " ) ) ;
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 {
int r = bitrates [ j % 4 ] ;
p + = sprintf ( p , " ,%2u.%1uM, " , r / 10 , r % 10 ) ;
}
p + = sprintf ( p , " %u, " , idx ) ;
tx_time = DIV_ROUND_CLOSEST ( mg - > duration [ j ] , 1000 ) ;
p + = sprintf ( p , " %u, " , tx_time ) ;
2015-03-24 23:09:39 +03:00
tp = mrs - > cur_tp / 10 ;
prob = MINSTREL_TRUNC ( mrs - > cur_prob * 1000 ) ;
eprob = MINSTREL_TRUNC ( mrs - > prob_ewma * 1000 ) ;
2015-03-24 23:09:37 +03:00
p + = sprintf ( p , " %u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu, " ,
tp / 10 , tp % 10 ,
eprob / 10 , eprob % 10 ,
prob / 10 , prob % 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 )
{
struct minstrel_ht_sta_priv * msp = inode - > i_private ;
struct minstrel_ht_sta * mi = & msp - > ht ;
struct minstrel_debugfs_info * ms ;
unsigned int i ;
int ret ;
char * p ;
if ( ! msp - > is_ht ) {
inode - > i_private = & msp - > legacy ;
ret = minstrel_stats_csv_open ( inode , file ) ;
inode - > i_private = msp ;
return ret ;
}
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 )
{
struct minstrel_ht_sta_priv * msp = priv_sta ;
msp - > dbg_stats = debugfs_create_file ( " rc_stats " , S_IRUGO , dir , msp ,
& minstrel_ht_stat_fops ) ;
2015-03-24 23:09:37 +03:00
msp - > dbg_stats_csv = debugfs_create_file ( " rc_stats_csv " , S_IRUGO ,
dir , msp , & minstrel_ht_stat_csv_fops ) ;
2010-05-13 18:48:03 +04:00
}
void
minstrel_ht_remove_sta_debugfs ( void * priv , void * priv_sta )
{
struct minstrel_ht_sta_priv * msp = priv_sta ;
debugfs_remove ( msp - > dbg_stats ) ;
2015-03-24 23:09:37 +03:00
debugfs_remove ( msp - > dbg_stats_csv ) ;
2010-05-13 18:48:03 +04:00
}