2005-04-17 02:20:36 +04:00
/*
* proc_llc . c - proc interface for LLC
*
* Copyright ( c ) 2001 by Jay Schulist < jschlst @ samba . org >
* 2002 - 2003 by Arnaldo Carvalho de Melo < acme @ conectiva . com . br >
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation .
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose .
*
* See the GNU General Public License for more details .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/proc_fs.h>
# include <linux/errno.h>
# include <linux/seq_file.h>
2007-09-12 14:01:34 +04:00
# include <net/net_namespace.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <net/llc.h>
# include <net/llc_c_ac.h>
# include <net/llc_c_ev.h>
# include <net/llc_c_st.h>
# include <net/llc_conn.h>
2007-10-04 04:59:30 +04:00
static void llc_ui_format_mac ( struct seq_file * seq , u8 * addr )
2005-04-17 02:20:36 +04:00
{
2007-10-04 04:59:30 +04:00
DECLARE_MAC_BUF ( mac ) ;
seq_printf ( seq , " %s " , print_mac ( mac , addr ) ) ;
2005-04-17 02:20:36 +04:00
}
static struct sock * llc_get_sk_idx ( loff_t pos )
{
struct list_head * sap_entry ;
struct llc_sap * sap ;
struct hlist_node * node ;
struct sock * sk = NULL ;
list_for_each ( sap_entry , & llc_sap_list ) {
sap = list_entry ( sap_entry , struct llc_sap , node ) ;
read_lock_bh ( & sap - > sk_list . lock ) ;
sk_for_each ( sk , node , & sap - > sk_list . list ) {
if ( ! pos )
goto found ;
- - pos ;
}
read_unlock_bh ( & sap - > sk_list . lock ) ;
}
sk = NULL ;
found :
return sk ;
}
static void * llc_seq_start ( struct seq_file * seq , loff_t * pos )
{
loff_t l = * pos ;
read_lock_bh ( & llc_sap_list_lock ) ;
return l ? llc_get_sk_idx ( - - l ) : SEQ_START_TOKEN ;
}
static void * llc_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct sock * sk , * next ;
struct llc_sock * llc ;
struct llc_sap * sap ;
+ + * pos ;
if ( v = = SEQ_START_TOKEN ) {
sk = llc_get_sk_idx ( 0 ) ;
goto out ;
}
sk = v ;
next = sk_next ( sk ) ;
if ( next ) {
sk = next ;
goto out ;
}
llc = llc_sk ( sk ) ;
sap = llc - > sap ;
read_unlock_bh ( & sap - > sk_list . lock ) ;
sk = NULL ;
for ( ; ; ) {
if ( sap - > node . next = = & llc_sap_list )
break ;
sap = list_entry ( sap - > node . next , struct llc_sap , node ) ;
read_lock_bh ( & sap - > sk_list . lock ) ;
if ( ! hlist_empty ( & sap - > sk_list . list ) ) {
sk = sk_head ( & sap - > sk_list . list ) ;
break ;
}
read_unlock_bh ( & sap - > sk_list . lock ) ;
}
out :
return sk ;
}
static void llc_seq_stop ( struct seq_file * seq , void * v )
{
if ( v & & v ! = SEQ_START_TOKEN ) {
struct sock * sk = v ;
struct llc_sock * llc = llc_sk ( sk ) ;
struct llc_sap * sap = llc - > sap ;
read_unlock_bh ( & sap - > sk_list . lock ) ;
}
read_unlock_bh ( & llc_sap_list_lock ) ;
}
static int llc_seq_socket_show ( struct seq_file * seq , void * v )
{
struct sock * sk ;
struct llc_sock * llc ;
if ( v = = SEQ_START_TOKEN ) {
seq_puts ( seq , " SKt Mc local_mac_sap remote_mac_sap "
" tx_queue rx_queue st uid link \n " ) ;
goto out ;
}
sk = v ;
llc = llc_sk ( sk ) ;
/* FIXME: check if the address is multicast */
seq_printf ( seq , " %2X %2X " , sk - > sk_type , 0 ) ;
if ( llc - > dev )
llc_ui_format_mac ( seq , llc - > dev - > dev_addr ) ;
2007-10-04 04:59:30 +04:00
else {
u8 addr [ 6 ] = { 0 , 0 , 0 , 0 , 0 , 0 } ;
llc_ui_format_mac ( seq , addr ) ;
}
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " @%02X " , llc - > sap - > laddr . lsap ) ;
llc_ui_format_mac ( seq , llc - > daddr . mac ) ;
seq_printf ( seq , " @%02X %8d %8d %2d %3d %4d \n " , llc - > daddr . lsap ,
atomic_read ( & sk - > sk_wmem_alloc ) ,
2005-09-22 15:29:08 +04:00
atomic_read ( & sk - > sk_rmem_alloc ) - llc - > copied_seq ,
2005-04-17 02:20:36 +04:00
sk - > sk_state ,
sk - > sk_socket ? SOCK_INODE ( sk - > sk_socket ) - > i_uid : - 1 ,
llc - > link ) ;
out :
return 0 ;
}
static char * llc_conn_state_names [ ] = {
2007-02-09 17:25:01 +03:00
[ LLC_CONN_STATE_ADM ] = " adm " ,
[ LLC_CONN_STATE_SETUP ] = " setup " ,
2005-04-17 02:20:36 +04:00
[ LLC_CONN_STATE_NORMAL ] = " normal " ,
2007-02-09 17:25:01 +03:00
[ LLC_CONN_STATE_BUSY ] = " busy " ,
[ LLC_CONN_STATE_REJ ] = " rej " ,
[ LLC_CONN_STATE_AWAIT ] = " await " ,
2005-04-17 02:20:36 +04:00
[ LLC_CONN_STATE_AWAIT_BUSY ] = " await_busy " ,
[ LLC_CONN_STATE_AWAIT_REJ ] = " await_rej " ,
[ LLC_CONN_STATE_D_CONN ] = " d_conn " ,
2007-02-09 17:25:01 +03:00
[ LLC_CONN_STATE_RESET ] = " reset " ,
[ LLC_CONN_STATE_ERROR ] = " error " ,
[ LLC_CONN_STATE_TEMP ] = " temp " ,
2005-04-17 02:20:36 +04:00
} ;
static int llc_seq_core_show ( struct seq_file * seq , void * v )
{
struct sock * sk ;
struct llc_sock * llc ;
if ( v = = SEQ_START_TOKEN ) {
seq_puts ( seq , " Connection list: \n "
" dsap state retr txw rxw pf ff sf df rs cs "
" tack tpfc trs tbs blog busr \n " ) ;
goto out ;
}
sk = v ;
llc = llc_sk ( sk ) ;
seq_printf ( seq , " %02X %-10s %3d %3d %3d %2d %2d %2d %2d %2d %2d "
" %4d %4d %3d %3d %4d %4d \n " ,
llc - > daddr . lsap , llc_conn_state_names [ llc - > state ] ,
llc - > retry_count , llc - > k , llc - > rw , llc - > p_flag , llc - > f_flag ,
llc - > s_flag , llc - > data_flag , llc - > remote_busy_flag ,
llc - > cause_flag , timer_pending ( & llc - > ack_timer . timer ) ,
timer_pending ( & llc - > pf_cycle_timer . timer ) ,
timer_pending ( & llc - > rej_sent_timer . timer ) ,
timer_pending ( & llc - > busy_state_timer . timer ) ,
! ! sk - > sk_backlog . tail , ! ! sock_owned_by_user ( sk ) ) ;
out :
return 0 ;
}
2007-07-11 10:07:31 +04:00
static const struct seq_operations llc_seq_socket_ops = {
2005-04-17 02:20:36 +04:00
. start = llc_seq_start ,
. next = llc_seq_next ,
. stop = llc_seq_stop ,
. show = llc_seq_socket_show ,
} ;
2007-07-11 10:07:31 +04:00
static const struct seq_operations llc_seq_core_ops = {
2005-04-17 02:20:36 +04:00
. start = llc_seq_start ,
. next = llc_seq_next ,
. stop = llc_seq_stop ,
. show = llc_seq_core_show ,
} ;
static int llc_seq_socket_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & llc_seq_socket_ops ) ;
}
static int llc_seq_core_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & llc_seq_core_ops ) ;
}
2007-02-12 11:55:36 +03:00
static const struct file_operations llc_seq_socket_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = llc_seq_socket_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
2007-02-12 11:55:36 +03:00
static const struct file_operations llc_seq_core_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = llc_seq_core_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static struct proc_dir_entry * llc_proc_dir ;
int __init llc_proc_init ( void )
{
int rc = - ENOMEM ;
struct proc_dir_entry * p ;
2007-09-12 14:01:34 +04:00
llc_proc_dir = proc_mkdir ( " llc " , init_net . proc_net ) ;
2005-04-17 02:20:36 +04:00
if ( ! llc_proc_dir )
goto out ;
llc_proc_dir - > owner = THIS_MODULE ;
p = create_proc_entry ( " socket " , S_IRUGO , llc_proc_dir ) ;
if ( ! p )
goto out_socket ;
p - > proc_fops = & llc_seq_socket_fops ;
p = create_proc_entry ( " core " , S_IRUGO , llc_proc_dir ) ;
if ( ! p )
goto out_core ;
p - > proc_fops = & llc_seq_core_fops ;
rc = 0 ;
out :
return rc ;
out_core :
remove_proc_entry ( " socket " , llc_proc_dir ) ;
out_socket :
2007-09-12 14:01:34 +04:00
remove_proc_entry ( " llc " , init_net . proc_net ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
void llc_proc_exit ( void )
{
remove_proc_entry ( " socket " , llc_proc_dir ) ;
remove_proc_entry ( " core " , llc_proc_dir ) ;
2007-09-12 14:01:34 +04:00
remove_proc_entry ( " llc " , init_net . proc_net ) ;
2005-04-17 02:20:36 +04:00
}