2008-04-14 21:46:19 +04:00
/* -*- mode: c; c-basic-offset: 8; -*-
* vim : noexpandtab sw = 8 ts = 8 sts = 0 :
*
* netdebug . c
*
* debug functionality for o2net
*
* Copyright ( C ) 2005 , 2008 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*
*/
# ifdef CONFIG_DEBUG_FS
# include <linux/module.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/idr.h>
# include <linux/kref.h>
# include <linux/seq_file.h>
# include <linux/debugfs.h>
# include <linux/uaccess.h>
# include "tcp.h"
# include "nodemanager.h"
# define MLOG_MASK_PREFIX ML_TCP
# include "masklog.h"
# include "tcp_internal.h"
# define O2NET_DEBUG_DIR "o2net"
# define SC_DEBUG_NAME "sock_containers"
# define NST_DEBUG_NAME "send_tracking"
2010-12-22 23:39:42 +03:00
# define STATS_DEBUG_NAME "stats"
2011-07-24 21:32:54 +04:00
# define NODES_DEBUG_NAME "connected_nodes"
2010-12-22 23:39:42 +03:00
# define SHOW_SOCK_CONTAINERS 0
# define SHOW_SOCK_STATS 1
2008-04-14 21:46:19 +04:00
static struct dentry * o2net_dentry ;
static struct dentry * sc_dentry ;
static struct dentry * nst_dentry ;
2010-12-22 23:39:42 +03:00
static struct dentry * stats_dentry ;
2011-07-24 21:32:54 +04:00
static struct dentry * nodes_dentry ;
2008-04-14 21:46:19 +04:00
static DEFINE_SPINLOCK ( o2net_debug_lock ) ;
static LIST_HEAD ( sock_containers ) ;
static LIST_HEAD ( send_tracking ) ;
void o2net_debug_add_nst ( struct o2net_send_tracking * nst )
{
spin_lock ( & o2net_debug_lock ) ;
list_add ( & nst - > st_net_debug_item , & send_tracking ) ;
spin_unlock ( & o2net_debug_lock ) ;
}
void o2net_debug_del_nst ( struct o2net_send_tracking * nst )
{
spin_lock ( & o2net_debug_lock ) ;
if ( ! list_empty ( & nst - > st_net_debug_item ) )
list_del_init ( & nst - > st_net_debug_item ) ;
spin_unlock ( & o2net_debug_lock ) ;
}
static struct o2net_send_tracking
* next_nst ( struct o2net_send_tracking * nst_start )
{
struct o2net_send_tracking * nst , * ret = NULL ;
assert_spin_locked ( & o2net_debug_lock ) ;
list_for_each_entry ( nst , & nst_start - > st_net_debug_item ,
st_net_debug_item ) {
/* discover the head of the list */
if ( & nst - > st_net_debug_item = = & send_tracking )
break ;
/* use st_task to detect real nsts in the list */
if ( nst - > st_task ! = NULL ) {
ret = nst ;
break ;
}
}
return ret ;
}
static void * nst_seq_start ( struct seq_file * seq , loff_t * pos )
{
struct o2net_send_tracking * nst , * dummy_nst = seq - > private ;
spin_lock ( & o2net_debug_lock ) ;
nst = next_nst ( dummy_nst ) ;
spin_unlock ( & o2net_debug_lock ) ;
return nst ;
}
static void * nst_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct o2net_send_tracking * nst , * dummy_nst = seq - > private ;
spin_lock ( & o2net_debug_lock ) ;
nst = next_nst ( dummy_nst ) ;
list_del_init ( & dummy_nst - > st_net_debug_item ) ;
if ( nst )
list_add ( & dummy_nst - > st_net_debug_item ,
& nst - > st_net_debug_item ) ;
spin_unlock ( & o2net_debug_lock ) ;
return nst ; /* unused, just needs to be null when done */
}
static int nst_seq_show ( struct seq_file * seq , void * v )
{
struct o2net_send_tracking * nst , * dummy_nst = seq - > private ;
2010-12-22 23:39:38 +03:00
ktime_t now ;
s64 sock , send , status ;
2008-04-14 21:46:19 +04:00
spin_lock ( & o2net_debug_lock ) ;
nst = next_nst ( dummy_nst ) ;
2011-01-03 09:00:20 +03:00
if ( ! nst )
goto out ;
2008-04-14 21:46:19 +04:00
2010-12-22 23:39:38 +03:00
now = ktime_get ( ) ;
sock = ktime_to_us ( ktime_sub ( now , nst - > st_sock_time ) ) ;
send = ktime_to_us ( ktime_sub ( now , nst - > st_send_time ) ) ;
status = ktime_to_us ( ktime_sub ( now , nst - > st_status_time ) ) ;
2011-01-03 09:00:20 +03:00
/* get_task_comm isn't exported. oh well. */
seq_printf ( seq , " %p: \n "
" pid: %lu \n "
" tgid: %lu \n "
" process name: %s \n "
" node: %u \n "
" sc: %p \n "
" message id: %d \n "
" message type: %u \n "
" message key: 0x%08x \n "
" sock acquiry: %lld usecs ago \n "
" send start: %lld usecs ago \n "
" wait start: %lld usecs ago \n " ,
nst , ( unsigned long ) task_pid_nr ( nst - > st_task ) ,
( unsigned long ) nst - > st_task - > tgid ,
nst - > st_task - > comm , nst - > st_node ,
nst - > st_sc , nst - > st_id , nst - > st_msg_type ,
nst - > st_msg_key ,
( long long ) sock ,
( long long ) send ,
( long long ) status ) ;
2008-04-14 21:46:19 +04:00
2011-01-03 09:00:20 +03:00
out :
2008-04-14 21:46:19 +04:00
spin_unlock ( & o2net_debug_lock ) ;
return 0 ;
}
static void nst_seq_stop ( struct seq_file * seq , void * v )
{
}
2009-09-23 03:43:43 +04:00
static const struct seq_operations nst_seq_ops = {
2008-04-14 21:46:19 +04:00
. start = nst_seq_start ,
. next = nst_seq_next ,
. stop = nst_seq_stop ,
. show = nst_seq_show ,
} ;
static int nst_fop_open ( struct inode * inode , struct file * file )
{
struct o2net_send_tracking * dummy_nst ;
2014-10-10 02:25:07 +04:00
dummy_nst = __seq_open_private ( file , & nst_seq_ops , sizeof ( * dummy_nst ) ) ;
if ( ! dummy_nst )
return - ENOMEM ;
2008-04-14 21:46:19 +04:00
o2net_debug_add_nst ( dummy_nst ) ;
2014-10-10 02:25:07 +04:00
return 0 ;
2008-04-14 21:46:19 +04:00
}
static int nst_fop_release ( struct inode * inode , struct file * file )
{
struct seq_file * seq = file - > private_data ;
struct o2net_send_tracking * dummy_nst = seq - > private ;
o2net_debug_del_nst ( dummy_nst ) ;
return seq_release_private ( inode , file ) ;
}
2009-10-02 02:43:56 +04:00
static const struct file_operations nst_seq_fops = {
2008-04-14 21:46:19 +04:00
. open = nst_fop_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = nst_fop_release ,
} ;
void o2net_debug_add_sc ( struct o2net_sock_container * sc )
{
spin_lock ( & o2net_debug_lock ) ;
list_add ( & sc - > sc_net_debug_item , & sock_containers ) ;
spin_unlock ( & o2net_debug_lock ) ;
}
void o2net_debug_del_sc ( struct o2net_sock_container * sc )
{
spin_lock ( & o2net_debug_lock ) ;
list_del_init ( & sc - > sc_net_debug_item ) ;
spin_unlock ( & o2net_debug_lock ) ;
}
2010-12-22 23:39:42 +03:00
struct o2net_sock_debug {
int dbg_ctxt ;
struct o2net_sock_container * dbg_sock ;
} ;
2008-04-14 21:46:19 +04:00
static struct o2net_sock_container
* next_sc ( struct o2net_sock_container * sc_start )
{
struct o2net_sock_container * sc , * ret = NULL ;
assert_spin_locked ( & o2net_debug_lock ) ;
list_for_each_entry ( sc , & sc_start - > sc_net_debug_item ,
sc_net_debug_item ) {
/* discover the head of the list miscast as a sc */
if ( & sc - > sc_net_debug_item = = & sock_containers )
break ;
/* use sc_page to detect real scs in the list */
if ( sc - > sc_page ! = NULL ) {
ret = sc ;
break ;
}
}
return ret ;
}
static void * sc_seq_start ( struct seq_file * seq , loff_t * pos )
{
2010-12-22 23:39:42 +03:00
struct o2net_sock_debug * sd = seq - > private ;
struct o2net_sock_container * sc , * dummy_sc = sd - > dbg_sock ;
2008-04-14 21:46:19 +04:00
spin_lock ( & o2net_debug_lock ) ;
sc = next_sc ( dummy_sc ) ;
spin_unlock ( & o2net_debug_lock ) ;
return sc ;
}
static void * sc_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
2010-12-22 23:39:42 +03:00
struct o2net_sock_debug * sd = seq - > private ;
struct o2net_sock_container * sc , * dummy_sc = sd - > dbg_sock ;
2008-04-14 21:46:19 +04:00
spin_lock ( & o2net_debug_lock ) ;
sc = next_sc ( dummy_sc ) ;
list_del_init ( & dummy_sc - > sc_net_debug_item ) ;
if ( sc )
list_add ( & dummy_sc - > sc_net_debug_item , & sc - > sc_net_debug_item ) ;
spin_unlock ( & o2net_debug_lock ) ;
return sc ; /* unused, just needs to be null when done */
}
2010-12-22 23:39:42 +03:00
# ifdef CONFIG_OCFS2_FS_STATS
# define sc_send_count(_s) ((_s)->sc_send_count)
# define sc_recv_count(_s) ((_s)->sc_recv_count)
# define sc_tv_acquiry_total_ns(_s) (ktime_to_ns((_s)->sc_tv_acquiry_total))
# define sc_tv_send_total_ns(_s) (ktime_to_ns((_s)->sc_tv_send_total))
# define sc_tv_status_total_ns(_s) (ktime_to_ns((_s)->sc_tv_status_total))
# define sc_tv_process_total_ns(_s) (ktime_to_ns((_s)->sc_tv_process_total))
# else
# define sc_send_count(_s) (0U)
# define sc_recv_count(_s) (0U)
# define sc_tv_acquiry_total_ns(_s) (0LL)
# define sc_tv_send_total_ns(_s) (0LL)
# define sc_tv_status_total_ns(_s) (0LL)
# define sc_tv_process_total_ns(_s) (0LL)
# endif
/* So that debugfs.ocfs2 can determine which format is being used */
# define O2NET_STATS_STR_VERSION 1
static void sc_show_sock_stats ( struct seq_file * seq ,
struct o2net_sock_container * sc )
2008-04-14 21:46:19 +04:00
{
2010-12-22 23:39:42 +03:00
if ( ! sc )
return ;
seq_printf ( seq , " %d,%u,%lu,%lld,%lld,%lld,%lu,%lld \n " , O2NET_STATS_STR_VERSION ,
sc - > sc_node - > nd_num , ( unsigned long ) sc_send_count ( sc ) ,
( long long ) sc_tv_acquiry_total_ns ( sc ) ,
( long long ) sc_tv_send_total_ns ( sc ) ,
( long long ) sc_tv_status_total_ns ( sc ) ,
( unsigned long ) sc_recv_count ( sc ) ,
( long long ) sc_tv_process_total_ns ( sc ) ) ;
}
2008-04-14 21:46:19 +04:00
2010-12-22 23:39:42 +03:00
static void sc_show_sock_container ( struct seq_file * seq ,
struct o2net_sock_container * sc )
{
struct inet_sock * inet = NULL ;
__be32 saddr = 0 , daddr = 0 ;
__be16 sport = 0 , dport = 0 ;
if ( ! sc )
return ;
if ( sc - > sc_sock ) {
inet = inet_sk ( sc - > sc_sock - > sk ) ;
/* the stack's structs aren't sparse endian clean */
saddr = ( __force __be32 ) inet - > inet_saddr ;
daddr = ( __force __be32 ) inet - > inet_daddr ;
sport = ( __force __be16 ) inet - > inet_sport ;
dport = ( __force __be16 ) inet - > inet_dport ;
}
2008-04-14 21:46:19 +04:00
2010-12-22 23:39:42 +03:00
/* XXX sigh, inet-> doesn't have sparse annotation so any
* use of it here generates a warning with - Wbitwise */
seq_printf ( seq , " %p: \n "
" krefs: %d \n "
" sock: %pI4:%u -> "
" %pI4:%u \n "
" remote node: %s \n "
" page off: %zu \n "
" handshake ok: %u \n "
" timer: %lld usecs \n "
" data ready: %lld usecs \n "
" advance start: %lld usecs \n "
" advance stop: %lld usecs \n "
" func start: %lld usecs \n "
" func stop: %lld usecs \n "
" func key: 0x%08x \n "
" func type: %u \n " ,
sc ,
atomic_read ( & sc - > sc_kref . refcount ) ,
& saddr , inet ? ntohs ( sport ) : 0 ,
& daddr , inet ? ntohs ( dport ) : 0 ,
sc - > sc_node - > nd_name ,
sc - > sc_page_off ,
sc - > sc_handshake_ok ,
( long long ) ktime_to_us ( sc - > sc_tv_timer ) ,
( long long ) ktime_to_us ( sc - > sc_tv_data_ready ) ,
( long long ) ktime_to_us ( sc - > sc_tv_advance_start ) ,
( long long ) ktime_to_us ( sc - > sc_tv_advance_stop ) ,
( long long ) ktime_to_us ( sc - > sc_tv_func_start ) ,
( long long ) ktime_to_us ( sc - > sc_tv_func_stop ) ,
sc - > sc_msg_key ,
sc - > sc_msg_type ) ;
}
2008-04-14 21:46:19 +04:00
2010-12-22 23:39:42 +03:00
static int sc_seq_show ( struct seq_file * seq , void * v )
{
struct o2net_sock_debug * sd = seq - > private ;
struct o2net_sock_container * sc , * dummy_sc = sd - > dbg_sock ;
2008-04-14 21:46:19 +04:00
2010-12-22 23:39:42 +03:00
spin_lock ( & o2net_debug_lock ) ;
sc = next_sc ( dummy_sc ) ;
2008-04-14 21:46:19 +04:00
2010-12-22 23:39:42 +03:00
if ( sc ) {
if ( sd - > dbg_ctxt = = SHOW_SOCK_CONTAINERS )
sc_show_sock_container ( seq , sc ) ;
else
sc_show_sock_stats ( seq , sc ) ;
2008-04-14 21:46:19 +04:00
}
spin_unlock ( & o2net_debug_lock ) ;
return 0 ;
}
static void sc_seq_stop ( struct seq_file * seq , void * v )
{
}
2009-09-23 03:43:43 +04:00
static const struct seq_operations sc_seq_ops = {
2008-04-14 21:46:19 +04:00
. start = sc_seq_start ,
. next = sc_seq_next ,
. stop = sc_seq_stop ,
. show = sc_seq_show ,
} ;
2014-10-10 02:25:07 +04:00
static int sc_common_open ( struct file * file , int ctxt )
2008-04-14 21:46:19 +04:00
{
2014-10-10 02:25:07 +04:00
struct o2net_sock_debug * sd ;
2008-04-14 21:46:19 +04:00
struct o2net_sock_container * dummy_sc ;
2014-10-10 02:25:07 +04:00
dummy_sc = kzalloc ( sizeof ( * dummy_sc ) , GFP_KERNEL ) ;
if ( ! dummy_sc )
return - ENOMEM ;
2008-04-14 21:46:19 +04:00
2014-10-10 02:25:07 +04:00
sd = __seq_open_private ( file , & sc_seq_ops , sizeof ( * sd ) ) ;
if ( ! sd ) {
kfree ( dummy_sc ) ;
return - ENOMEM ;
}
2008-04-14 21:46:19 +04:00
2014-10-10 02:25:07 +04:00
sd - > dbg_ctxt = ctxt ;
2010-12-22 23:39:42 +03:00
sd - > dbg_sock = dummy_sc ;
2008-04-14 21:46:19 +04:00
2014-10-10 02:25:07 +04:00
o2net_debug_add_sc ( dummy_sc ) ;
2008-04-14 21:46:19 +04:00
2014-10-10 02:25:07 +04:00
return 0 ;
2008-04-14 21:46:19 +04:00
}
static int sc_fop_release ( struct inode * inode , struct file * file )
{
struct seq_file * seq = file - > private_data ;
2010-12-22 23:39:42 +03:00
struct o2net_sock_debug * sd = seq - > private ;
struct o2net_sock_container * dummy_sc = sd - > dbg_sock ;
2008-04-14 21:46:19 +04:00
o2net_debug_del_sc ( dummy_sc ) ;
return seq_release_private ( inode , file ) ;
}
2010-12-22 23:39:42 +03:00
static int stats_fop_open ( struct inode * inode , struct file * file )
{
2014-10-10 02:25:07 +04:00
return sc_common_open ( file , SHOW_SOCK_STATS ) ;
2010-12-22 23:39:42 +03:00
}
static const struct file_operations stats_seq_fops = {
. open = stats_fop_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = sc_fop_release ,
} ;
static int sc_fop_open ( struct inode * inode , struct file * file )
{
2014-10-10 02:25:07 +04:00
return sc_common_open ( file , SHOW_SOCK_CONTAINERS ) ;
2010-12-22 23:39:42 +03:00
}
2009-10-02 02:43:56 +04:00
static const struct file_operations sc_seq_fops = {
2008-04-14 21:46:19 +04:00
. open = sc_fop_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = sc_fop_release ,
} ;
2011-07-24 21:32:54 +04:00
static int o2net_fill_bitmap ( char * buf , int len )
2008-04-14 21:46:19 +04:00
{
2011-07-24 21:32:54 +04:00
unsigned long map [ BITS_TO_LONGS ( O2NM_MAX_NODES ) ] ;
int i = - 1 , out = 0 ;
2008-04-14 21:46:19 +04:00
2011-07-24 21:32:54 +04:00
o2net_fill_node_map ( map , sizeof ( map ) ) ;
2008-04-14 21:46:19 +04:00
2011-07-24 21:32:54 +04:00
while ( ( i = find_next_bit ( map , O2NM_MAX_NODES , i + 1 ) ) < O2NM_MAX_NODES )
out + = snprintf ( buf + out , PAGE_SIZE - out , " %d " , i ) ;
out + = snprintf ( buf + out , PAGE_SIZE - out , " \n " ) ;
2008-04-14 21:46:19 +04:00
2011-07-24 21:32:54 +04:00
return out ;
}
static int nodes_fop_open ( struct inode * inode , struct file * file )
{
char * buf ;
buf = kmalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
i_size_write ( inode , o2net_fill_bitmap ( buf , PAGE_SIZE ) ) ;
file - > private_data = buf ;
2010-12-22 23:39:42 +03:00
2008-04-14 21:46:19 +04:00
return 0 ;
}
2011-07-24 21:32:54 +04:00
static int o2net_debug_release ( struct inode * inode , struct file * file )
{
kfree ( file - > private_data ) ;
return 0 ;
}
static ssize_t o2net_debug_read ( struct file * file , char __user * buf ,
size_t nbytes , loff_t * ppos )
{
return simple_read_from_buffer ( buf , nbytes , ppos , file - > private_data ,
i_size_read ( file - > f_mapping - > host ) ) ;
}
static const struct file_operations nodes_fops = {
. open = nodes_fop_open ,
. release = o2net_debug_release ,
. read = o2net_debug_read ,
. llseek = generic_file_llseek ,
} ;
2008-04-14 21:46:19 +04:00
void o2net_debugfs_exit ( void )
{
2011-07-24 21:32:54 +04:00
debugfs_remove ( nodes_dentry ) ;
2010-12-22 23:39:42 +03:00
debugfs_remove ( stats_dentry ) ;
2010-12-21 03:35:00 +03:00
debugfs_remove ( sc_dentry ) ;
debugfs_remove ( nst_dentry ) ;
debugfs_remove ( o2net_dentry ) ;
2008-04-14 21:46:19 +04:00
}
2011-07-24 21:32:54 +04:00
int o2net_debugfs_init ( void )
{
2011-07-24 12:33:43 +04:00
umode_t mode = S_IFREG | S_IRUSR ;
2011-07-24 21:32:54 +04:00
o2net_dentry = debugfs_create_dir ( O2NET_DEBUG_DIR , NULL ) ;
if ( o2net_dentry )
nst_dentry = debugfs_create_file ( NST_DEBUG_NAME , mode ,
o2net_dentry , NULL , & nst_seq_fops ) ;
if ( nst_dentry )
sc_dentry = debugfs_create_file ( SC_DEBUG_NAME , mode ,
o2net_dentry , NULL , & sc_seq_fops ) ;
if ( sc_dentry )
stats_dentry = debugfs_create_file ( STATS_DEBUG_NAME , mode ,
o2net_dentry , NULL , & stats_seq_fops ) ;
if ( stats_dentry )
nodes_dentry = debugfs_create_file ( NODES_DEBUG_NAME , mode ,
o2net_dentry , NULL , & nodes_fops ) ;
if ( nodes_dentry )
return 0 ;
o2net_debugfs_exit ( ) ;
mlog_errno ( - ENOMEM ) ;
return - ENOMEM ;
}
2008-04-14 21:46:19 +04:00
# endif /* CONFIG_DEBUG_FS */