2005-04-17 02:20:36 +04:00
/*
* arch / s390 / kernel / debug . c
* S / 390 debug facility
*
* Copyright ( C ) 1999 , 2000 IBM Deutschland Entwicklung GmbH ,
* IBM Corporation
* Author ( s ) : Michael Holzheu ( holzheu @ de . ibm . com ) ,
* Holger Smolinski ( Holger . Smolinski @ de . ibm . com )
*
* Bugreports to : < Linux390 @ de . ibm . com >
*/
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/ctype.h>
# include <linux/sysctl.h>
# include <asm/uaccess.h>
# include <linux/module.h>
# include <linux/init.h>
2005-06-26 01:55:33 +04:00
# include <linux/fs.h>
# include <linux/debugfs.h>
2005-04-17 02:20:36 +04:00
# include <asm/debug.h>
# define DEBUG_PROLOG_ENTRY -1
2005-06-26 01:55:33 +04:00
# define ALL_AREAS 0 /* copy all debug areas */
# define NO_AREAS 1 /* copy no debug areas */
2005-04-17 02:20:36 +04:00
/* typedefs */
typedef struct file_private_info {
loff_t offset ; /* offset of last read in file */
int act_area ; /* number of last formated area */
2005-06-26 01:55:33 +04:00
int act_page ; /* act page in given area */
2005-04-17 02:20:36 +04:00
int act_entry ; /* last formated entry (offset */
/* relative to beginning of last */
2005-06-26 01:55:33 +04:00
/* formated page) */
2005-04-17 02:20:36 +04:00
size_t act_entry_offset ; /* up to this offset we copied */
/* in last read the last formated */
/* entry to userland */
char temp_buf [ 2048 ] ; /* buffer for output */
debug_info_t * debug_info_org ; /* original debug information */
debug_info_t * debug_info_snap ; /* snapshot of debug information */
struct debug_view * view ; /* used view of debug info */
} file_private_info_t ;
typedef struct
{
char * string ;
/*
* This assumes that all args are converted into longs
* on L / 390 this is the case for all types of parameter
* except of floats , and long long ( 32 bit )
2005-06-26 01:55:33 +04:00
*
*/
2005-04-17 02:20:36 +04:00
long args [ 0 ] ;
} debug_sprintf_entry_t ;
2005-09-04 02:57:58 +04:00
extern void tod_to_timeval ( uint64_t todval , struct timespec * xtime ) ;
2005-04-17 02:20:36 +04:00
/* internal function prototyes */
static int debug_init ( void ) ;
static ssize_t debug_output ( struct file * file , char __user * user_buf ,
2005-06-26 01:55:33 +04:00
size_t user_len , loff_t * offset ) ;
2005-04-17 02:20:36 +04:00
static ssize_t debug_input ( struct file * file , const char __user * user_buf ,
2005-06-26 01:55:33 +04:00
size_t user_len , loff_t * offset ) ;
2005-04-17 02:20:36 +04:00
static int debug_open ( struct inode * inode , struct file * file ) ;
static int debug_close ( struct inode * inode , struct file * file ) ;
2008-05-15 18:52:35 +04:00
static debug_info_t * debug_info_create ( const char * name , int pages_per_area ,
2008-04-17 09:46:18 +04:00
int nr_areas , int buf_size , mode_t mode ) ;
2005-04-17 02:20:36 +04:00
static void debug_info_get ( debug_info_t * ) ;
static void debug_info_put ( debug_info_t * ) ;
static int debug_prolog_level_fn ( debug_info_t * id ,
2005-06-26 01:55:33 +04:00
struct debug_view * view , char * out_buf ) ;
2005-04-17 02:20:36 +04:00
static int debug_input_level_fn ( debug_info_t * id , struct debug_view * view ,
2005-06-26 01:55:33 +04:00
struct file * file , const char __user * user_buf ,
size_t user_buf_size , loff_t * offset ) ;
static int debug_prolog_pages_fn ( debug_info_t * id ,
struct debug_view * view , char * out_buf ) ;
static int debug_input_pages_fn ( debug_info_t * id , struct debug_view * view ,
struct file * file , const char __user * user_buf ,
size_t user_buf_size , loff_t * offset ) ;
2005-04-17 02:20:36 +04:00
static int debug_input_flush_fn ( debug_info_t * id , struct debug_view * view ,
2005-06-26 01:55:33 +04:00
struct file * file , const char __user * user_buf ,
size_t user_buf_size , loff_t * offset ) ;
2005-04-17 02:20:36 +04:00
static int debug_hex_ascii_format_fn ( debug_info_t * id , struct debug_view * view ,
2005-06-26 01:55:33 +04:00
char * out_buf , const char * in_buf ) ;
2005-04-17 02:20:36 +04:00
static int debug_raw_format_fn ( debug_info_t * id ,
2005-06-26 01:55:33 +04:00
struct debug_view * view , char * out_buf ,
const char * in_buf ) ;
2005-04-17 02:20:36 +04:00
static int debug_raw_header_fn ( debug_info_t * id , struct debug_view * view ,
2005-06-26 01:55:33 +04:00
int area , debug_entry_t * entry , char * out_buf ) ;
2005-04-17 02:20:36 +04:00
static int debug_sprintf_format_fn ( debug_info_t * id , struct debug_view * view ,
2005-06-26 01:55:33 +04:00
char * out_buf , debug_sprintf_entry_t * curr_event ) ;
2005-04-17 02:20:36 +04:00
/* globals */
struct debug_view debug_raw_view = {
" raw " ,
NULL ,
& debug_raw_header_fn ,
& debug_raw_format_fn ,
NULL ,
NULL
} ;
struct debug_view debug_hex_ascii_view = {
" hex_ascii " ,
NULL ,
& debug_dflt_header_fn ,
& debug_hex_ascii_format_fn ,
NULL ,
NULL
} ;
2007-02-05 23:16:47 +03:00
static struct debug_view debug_level_view = {
2005-04-17 02:20:36 +04:00
" level " ,
& debug_prolog_level_fn ,
NULL ,
NULL ,
& debug_input_level_fn ,
NULL
} ;
2007-02-05 23:16:47 +03:00
static struct debug_view debug_pages_view = {
2005-06-26 01:55:33 +04:00
" pages " ,
& debug_prolog_pages_fn ,
NULL ,
NULL ,
& debug_input_pages_fn ,
NULL
} ;
2007-02-05 23:16:47 +03:00
static struct debug_view debug_flush_view = {
2005-04-17 02:20:36 +04:00
" flush " ,
NULL ,
NULL ,
NULL ,
& debug_input_flush_fn ,
NULL
} ;
struct debug_view debug_sprintf_view = {
" sprintf " ,
NULL ,
& debug_dflt_header_fn ,
( debug_format_proc_t * ) & debug_sprintf_format_fn ,
NULL ,
NULL
} ;
2007-02-05 23:16:47 +03:00
/* used by dump analysis tools to determine version of debug feature */
2008-04-17 09:46:26 +04:00
static unsigned int __used debug_feature_version = __DEBUG_FEATURE_VERSION ;
2005-04-17 02:20:36 +04:00
/* static globals */
static debug_info_t * debug_area_first = NULL ;
static debug_info_t * debug_area_last = NULL ;
2007-05-31 19:38:03 +04:00
static DEFINE_MUTEX ( debug_mutex ) ;
2005-04-17 02:20:36 +04:00
static int initialized ;
2007-02-12 11:55:31 +03:00
static const struct file_operations debug_file_ops = {
2005-06-26 01:55:33 +04:00
. owner = THIS_MODULE ,
2005-04-17 02:20:36 +04:00
. read = debug_output ,
2005-06-26 01:55:33 +04:00
. write = debug_input ,
2005-04-17 02:20:36 +04:00
. open = debug_open ,
. release = debug_close ,
} ;
2005-06-26 01:55:33 +04:00
static struct dentry * debug_debugfs_root_entry ;
2005-04-17 02:20:36 +04:00
/* functions */
2005-06-26 01:55:33 +04:00
/*
* debug_areas_alloc
* - Debug areas are implemented as a threedimensonal array :
* areas [ areanumber ] [ pagenumber ] [ pageoffset ]
*/
static debug_entry_t * * *
debug_areas_alloc ( int pages_per_area , int nr_areas )
{
debug_entry_t * * * areas ;
int i , j ;
2006-12-13 11:35:56 +03:00
areas = kmalloc ( nr_areas *
2005-06-26 01:55:33 +04:00
sizeof ( debug_entry_t * * ) ,
GFP_KERNEL ) ;
if ( ! areas )
goto fail_malloc_areas ;
for ( i = 0 ; i < nr_areas ; i + + ) {
2006-12-13 11:35:56 +03:00
areas [ i ] = kmalloc ( pages_per_area *
2005-06-26 01:55:33 +04:00
sizeof ( debug_entry_t * ) , GFP_KERNEL ) ;
if ( ! areas [ i ] ) {
goto fail_malloc_areas2 ;
}
for ( j = 0 ; j < pages_per_area ; j + + ) {
2006-03-24 14:15:31 +03:00
areas [ i ] [ j ] = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
2005-06-26 01:55:33 +04:00
if ( ! areas [ i ] [ j ] ) {
for ( j - - ; j > = 0 ; j - - ) {
kfree ( areas [ i ] [ j ] ) ;
}
kfree ( areas [ i ] ) ;
goto fail_malloc_areas2 ;
}
}
}
return areas ;
fail_malloc_areas2 :
for ( i - - ; i > = 0 ; i - - ) {
for ( j = 0 ; j < pages_per_area ; j + + ) {
kfree ( areas [ i ] [ j ] ) ;
}
kfree ( areas [ i ] ) ;
}
kfree ( areas ) ;
fail_malloc_areas :
return NULL ;
}
2005-04-17 02:20:36 +04:00
/*
* debug_info_alloc
* - alloc new debug - info
*/
2005-06-26 01:55:33 +04:00
static debug_info_t *
2008-05-15 18:52:35 +04:00
debug_info_alloc ( const char * name , int pages_per_area , int nr_areas ,
int buf_size , int level , int mode )
2005-04-17 02:20:36 +04:00
{
debug_info_t * rc ;
/* alloc everything */
2006-12-13 11:35:56 +03:00
rc = kmalloc ( sizeof ( debug_info_t ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! rc )
goto fail_malloc_rc ;
2006-03-24 14:15:31 +03:00
rc - > active_entries = kcalloc ( nr_areas , sizeof ( int ) , GFP_KERNEL ) ;
2005-06-26 01:55:33 +04:00
if ( ! rc - > active_entries )
goto fail_malloc_active_entries ;
2006-03-24 14:15:31 +03:00
rc - > active_pages = kcalloc ( nr_areas , sizeof ( int ) , GFP_KERNEL ) ;
2005-06-26 01:55:33 +04:00
if ( ! rc - > active_pages )
goto fail_malloc_active_pages ;
if ( ( mode = = ALL_AREAS ) & & ( pages_per_area ! = 0 ) ) {
rc - > areas = debug_areas_alloc ( pages_per_area , nr_areas ) ;
if ( ! rc - > areas )
goto fail_malloc_areas ;
} else {
rc - > areas = NULL ;
2005-04-17 02:20:36 +04:00
}
/* initialize members */
spin_lock_init ( & rc - > lock ) ;
2005-06-26 01:55:33 +04:00
rc - > pages_per_area = pages_per_area ;
rc - > nr_areas = nr_areas ;
rc - > active_area = 0 ;
rc - > level = level ;
rc - > buf_size = buf_size ;
rc - > entry_size = sizeof ( debug_entry_t ) + buf_size ;
2007-03-19 15:18:53 +03:00
strlcpy ( rc - > name , name , sizeof ( rc - > name ) ) ;
2005-04-17 02:20:36 +04:00
memset ( rc - > views , 0 , DEBUG_MAX_VIEWS * sizeof ( struct debug_view * ) ) ;
2005-06-26 01:55:33 +04:00
memset ( rc - > debugfs_entries , 0 , DEBUG_MAX_VIEWS *
sizeof ( struct dentry * ) ) ;
2005-04-17 02:20:36 +04:00
atomic_set ( & ( rc - > ref_count ) , 0 ) ;
return rc ;
fail_malloc_areas :
2005-06-26 01:55:33 +04:00
kfree ( rc - > active_pages ) ;
fail_malloc_active_pages :
kfree ( rc - > active_entries ) ;
fail_malloc_active_entries :
2005-04-17 02:20:36 +04:00
kfree ( rc ) ;
fail_malloc_rc :
return NULL ;
}
/*
2005-06-26 01:55:33 +04:00
* debug_areas_free
* - free all debug areas
2005-04-17 02:20:36 +04:00
*/
2005-06-26 01:55:33 +04:00
static void
debug_areas_free ( debug_info_t * db_info )
{
int i , j ;
if ( ! db_info - > areas )
return ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < db_info - > nr_areas ; i + + ) {
2005-06-26 01:55:33 +04:00
for ( j = 0 ; j < db_info - > pages_per_area ; j + + ) {
kfree ( db_info - > areas [ i ] [ j ] ) ;
}
kfree ( db_info - > areas [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
kfree ( db_info - > areas ) ;
2005-06-26 01:55:33 +04:00
db_info - > areas = NULL ;
}
/*
* debug_info_free
* - free memory debug - info
*/
static void
debug_info_free ( debug_info_t * db_info ) {
debug_areas_free ( db_info ) ;
kfree ( db_info - > active_entries ) ;
kfree ( db_info - > active_pages ) ;
2005-04-17 02:20:36 +04:00
kfree ( db_info ) ;
}
/*
* debug_info_create
* - create new debug - info
*/
2005-06-26 01:55:33 +04:00
static debug_info_t *
2008-05-15 18:52:35 +04:00
debug_info_create ( const char * name , int pages_per_area , int nr_areas ,
int buf_size , mode_t mode )
2005-04-17 02:20:36 +04:00
{
debug_info_t * rc ;
2005-06-26 01:55:33 +04:00
rc = debug_info_alloc ( name , pages_per_area , nr_areas , buf_size ,
DEBUG_DEFAULT_LEVEL , ALL_AREAS ) ;
2005-04-17 02:20:36 +04:00
if ( ! rc )
goto out ;
2008-04-17 09:46:18 +04:00
rc - > mode = mode & ~ S_IFMT ;
2005-06-26 01:55:33 +04:00
/* create root directory */
rc - > debugfs_root_entry = debugfs_create_dir ( rc - > name ,
debug_debugfs_root_entry ) ;
2005-04-17 02:20:36 +04:00
/* append new element to linked list */
2005-06-26 01:55:33 +04:00
if ( ! debug_area_first ) {
2005-04-17 02:20:36 +04:00
/* first element in list */
debug_area_first = rc ;
rc - > prev = NULL ;
} else {
/* append element to end of list */
debug_area_last - > next = rc ;
rc - > prev = debug_area_last ;
}
debug_area_last = rc ;
rc - > next = NULL ;
debug_info_get ( rc ) ;
out :
return rc ;
}
/*
* debug_info_copy
* - copy debug - info
*/
2005-06-26 01:55:33 +04:00
static debug_info_t *
debug_info_copy ( debug_info_t * in , int mode )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:33 +04:00
int i , j ;
2005-04-17 02:20:36 +04:00
debug_info_t * rc ;
2005-09-04 02:57:58 +04:00
unsigned long flags ;
/* get a consistent copy of the debug areas */
do {
rc = debug_info_alloc ( in - > name , in - > pages_per_area ,
in - > nr_areas , in - > buf_size , in - > level , mode ) ;
spin_lock_irqsave ( & in - > lock , flags ) ;
if ( ! rc )
goto out ;
/* has something changed in the meantime ? */
if ( ( rc - > pages_per_area = = in - > pages_per_area ) & &
( rc - > nr_areas = = in - > nr_areas ) ) {
break ;
}
spin_unlock_irqrestore ( & in - > lock , flags ) ;
debug_info_free ( rc ) ;
} while ( 1 ) ;
2005-06-26 01:55:33 +04:00
if ( ! rc | | ( mode = = NO_AREAS ) )
2005-04-17 02:20:36 +04:00
goto out ;
for ( i = 0 ; i < in - > nr_areas ; i + + ) {
2005-06-26 01:55:33 +04:00
for ( j = 0 ; j < in - > pages_per_area ; j + + ) {
memcpy ( rc - > areas [ i ] [ j ] , in - > areas [ i ] [ j ] , PAGE_SIZE ) ;
}
2005-04-17 02:20:36 +04:00
}
out :
2005-09-04 02:57:58 +04:00
spin_unlock_irqrestore ( & in - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* debug_info_get
* - increments reference count for debug - info
*/
2005-06-26 01:55:33 +04:00
static void
debug_info_get ( debug_info_t * db_info )
2005-04-17 02:20:36 +04:00
{
if ( db_info )
atomic_inc ( & db_info - > ref_count ) ;
}
/*
* debug_info_put :
* - decreases reference count for debug - info and frees it if necessary
*/
2005-06-26 01:55:33 +04:00
static void
debug_info_put ( debug_info_t * db_info )
2005-04-17 02:20:36 +04:00
{
int i ;
if ( ! db_info )
return ;
if ( atomic_dec_and_test ( & db_info - > ref_count ) ) {
for ( i = 0 ; i < DEBUG_MAX_VIEWS ; i + + ) {
2005-06-26 01:55:33 +04:00
if ( ! db_info - > views [ i ] )
2005-04-17 02:20:36 +04:00
continue ;
2005-06-26 01:55:33 +04:00
debugfs_remove ( db_info - > debugfs_entries [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:55:33 +04:00
debugfs_remove ( db_info - > debugfs_root_entry ) ;
2005-04-17 02:20:36 +04:00
if ( db_info = = debug_area_first )
debug_area_first = db_info - > next ;
if ( db_info = = debug_area_last )
debug_area_last = db_info - > prev ;
if ( db_info - > prev ) db_info - > prev - > next = db_info - > next ;
if ( db_info - > next ) db_info - > next - > prev = db_info - > prev ;
debug_info_free ( db_info ) ;
}
}
/*
* debug_format_entry :
* - format one debug entry and return size of formated data
*/
2005-06-26 01:55:33 +04:00
static int
debug_format_entry ( file_private_info_t * p_info )
2005-04-17 02:20:36 +04:00
{
debug_info_t * id_snap = p_info - > debug_info_snap ;
struct debug_view * view = p_info - > view ;
debug_entry_t * act_entry ;
size_t len = 0 ;
if ( p_info - > act_entry = = DEBUG_PROLOG_ENTRY ) {
/* print prolog */
if ( view - > prolog_proc )
2005-06-26 01:55:33 +04:00
len + = view - > prolog_proc ( id_snap , view , p_info - > temp_buf ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2005-06-26 01:55:33 +04:00
if ( ! id_snap - > areas ) /* this is true, if we have a prolog only view */
goto out ; /* or if 'pages_per_area' is 0 */
act_entry = ( debug_entry_t * ) ( ( char * ) id_snap - > areas [ p_info - > act_area ]
[ p_info - > act_page ] + p_info - > act_entry ) ;
2005-04-17 02:20:36 +04:00
if ( act_entry - > id . stck = = 0LL )
goto out ; /* empty entry */
if ( view - > header_proc )
2005-06-26 01:55:33 +04:00
len + = view - > header_proc ( id_snap , view , p_info - > act_area ,
2005-04-17 02:20:36 +04:00
act_entry , p_info - > temp_buf + len ) ;
if ( view - > format_proc )
2005-06-26 01:55:33 +04:00
len + = view - > format_proc ( id_snap , view , p_info - > temp_buf + len ,
2005-04-17 02:20:36 +04:00
DEBUG_DATA ( act_entry ) ) ;
2005-06-26 01:55:33 +04:00
out :
2005-04-17 02:20:36 +04:00
return len ;
}
/*
* debug_next_entry :
* - goto next entry in p_info
*/
2005-11-09 08:34:42 +03:00
static inline int
2005-06-26 01:55:33 +04:00
debug_next_entry ( file_private_info_t * p_info )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:33 +04:00
debug_info_t * id ;
id = p_info - > debug_info_snap ;
2005-04-17 02:20:36 +04:00
if ( p_info - > act_entry = = DEBUG_PROLOG_ENTRY ) {
p_info - > act_entry = 0 ;
2005-06-26 01:55:33 +04:00
p_info - > act_page = 0 ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2005-06-26 01:55:33 +04:00
if ( ! id - > areas )
return 1 ;
p_info - > act_entry + = id - > entry_size ;
/* switch to next page, if we reached the end of the page */
if ( p_info - > act_entry > ( PAGE_SIZE - id - > entry_size ) ) {
/* next page */
2005-04-17 02:20:36 +04:00
p_info - > act_entry = 0 ;
2005-06-26 01:55:33 +04:00
p_info - > act_page + = 1 ;
if ( ( p_info - > act_page % id - > pages_per_area ) = = 0 ) {
/* next area */
p_info - > act_area + + ;
p_info - > act_page = 0 ;
}
2005-04-17 02:20:36 +04:00
if ( p_info - > act_area > = id - > nr_areas )
return 1 ;
}
out :
return 0 ;
}
/*
* debug_output :
* - called for user read ( )
* - copies formated debug entries to the user buffer
*/
2005-06-26 01:55:33 +04:00
static ssize_t
debug_output ( struct file * file , /* file descriptor */
char __user * user_buf , /* user buffer */
size_t len , /* length of buffer */
loff_t * offset ) /* offset in the file */
2005-04-17 02:20:36 +04:00
{
size_t count = 0 ;
2005-06-26 01:55:33 +04:00
size_t entry_offset ;
2005-04-17 02:20:36 +04:00
file_private_info_t * p_info ;
p_info = ( ( file_private_info_t * ) file - > private_data ) ;
if ( * offset ! = p_info - > offset )
return - EPIPE ;
if ( p_info - > act_area > = p_info - > debug_info_snap - > nr_areas )
return 0 ;
entry_offset = p_info - > act_entry_offset ;
while ( count < len ) {
2005-06-26 01:55:33 +04:00
int formatted_line_size ;
int formatted_line_residue ;
int user_buf_residue ;
size_t copy_size ;
formatted_line_size = debug_format_entry ( p_info ) ;
formatted_line_residue = formatted_line_size - entry_offset ;
user_buf_residue = len - count ;
copy_size = min ( user_buf_residue , formatted_line_residue ) ;
if ( copy_size ) {
if ( copy_to_user ( user_buf + count , p_info - > temp_buf
+ entry_offset , copy_size ) )
return - EFAULT ;
count + = copy_size ;
entry_offset + = copy_size ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:55:33 +04:00
if ( copy_size = = formatted_line_residue ) {
entry_offset = 0 ;
if ( debug_next_entry ( p_info ) )
2005-04-17 02:20:36 +04:00
goto out ;
2005-06-26 01:55:33 +04:00
}
2005-04-17 02:20:36 +04:00
}
out :
p_info - > offset = * offset + count ;
2005-06-26 01:55:33 +04:00
p_info - > act_entry_offset = entry_offset ;
2005-04-17 02:20:36 +04:00
* offset = p_info - > offset ;
return count ;
}
/*
* debug_input :
* - called for user write ( )
* - calls input function of view
*/
2005-06-26 01:55:33 +04:00
static ssize_t
debug_input ( struct file * file , const char __user * user_buf , size_t length ,
loff_t * offset )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
file_private_info_t * p_info ;
2007-05-31 19:38:03 +04:00
mutex_lock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
p_info = ( ( file_private_info_t * ) file - > private_data ) ;
if ( p_info - > view - > input_proc )
rc = p_info - > view - > input_proc ( p_info - > debug_info_org ,
p_info - > view , file , user_buf ,
length , offset ) ;
else
rc = - EPERM ;
2007-05-31 19:38:03 +04:00
mutex_unlock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
return rc ; /* number of input characters */
}
/*
* debug_open :
* - called for user open ( )
* - copies formated output to private_data area of the file
* handle
*/
2005-06-26 01:55:33 +04:00
static int
debug_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
int i = 0 , rc = 0 ;
file_private_info_t * p_info ;
debug_info_t * debug_info , * debug_info_snapshot ;
2007-05-31 19:38:03 +04:00
mutex_lock ( & debug_mutex ) ;
2006-12-08 13:37:35 +03:00
debug_info = file - > f_path . dentry - > d_inode - > i_private ;
2005-09-04 02:57:58 +04:00
/* find debug view */
for ( i = 0 ; i < DEBUG_MAX_VIEWS ; i + + ) {
if ( ! debug_info - > views [ i ] )
continue ;
else if ( debug_info - > debugfs_entries [ i ] = =
2006-12-08 13:37:35 +03:00
file - > f_path . dentry ) {
2005-09-04 02:57:58 +04:00
goto found ; /* found view ! */
2005-04-17 02:20:36 +04:00
}
}
/* no entry found */
rc = - EINVAL ;
goto out ;
2005-06-26 01:55:33 +04:00
found :
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:33 +04:00
/* Make snapshot of current debug areas to get it consistent. */
/* To copy all the areas is only needed, if we have a view which */
/* formats the debug areas. */
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:33 +04:00
if ( ! debug_info - > views [ i ] - > format_proc & &
! debug_info - > views [ i ] - > header_proc ) {
debug_info_snapshot = debug_info_copy ( debug_info , NO_AREAS ) ;
} else {
debug_info_snapshot = debug_info_copy ( debug_info , ALL_AREAS ) ;
}
2005-04-17 02:20:36 +04:00
if ( ! debug_info_snapshot ) {
rc = - ENOMEM ;
goto out ;
}
2006-12-13 11:35:56 +03:00
p_info = kmalloc ( sizeof ( file_private_info_t ) ,
2005-06-26 01:55:33 +04:00
GFP_KERNEL ) ;
if ( ! p_info ) {
if ( debug_info_snapshot )
debug_info_free ( debug_info_snapshot ) ;
2005-04-17 02:20:36 +04:00
rc = - ENOMEM ;
goto out ;
}
p_info - > offset = 0 ;
p_info - > debug_info_snap = debug_info_snapshot ;
p_info - > debug_info_org = debug_info ;
p_info - > view = debug_info - > views [ i ] ;
p_info - > act_area = 0 ;
2005-06-26 01:55:33 +04:00
p_info - > act_page = 0 ;
2005-04-17 02:20:36 +04:00
p_info - > act_entry = DEBUG_PROLOG_ENTRY ;
p_info - > act_entry_offset = 0 ;
2005-06-26 01:55:33 +04:00
file - > private_data = p_info ;
2005-04-17 02:20:36 +04:00
debug_info_get ( debug_info ) ;
2005-06-26 01:55:33 +04:00
out :
2007-05-31 19:38:03 +04:00
mutex_unlock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* debug_close :
* - called for user close ( )
* - deletes private_data area of the file handle
*/
2005-06-26 01:55:33 +04:00
static int
debug_close ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
file_private_info_t * p_info ;
p_info = ( file_private_info_t * ) file - > private_data ;
2005-06-26 01:55:33 +04:00
if ( p_info - > debug_info_snap )
debug_info_free ( p_info - > debug_info_snap ) ;
2005-04-17 02:20:36 +04:00
debug_info_put ( p_info - > debug_info_org ) ;
kfree ( file - > private_data ) ;
return 0 ; /* success */
}
/*
2008-04-17 09:46:18 +04:00
* debug_register_mode :
* - Creates and initializes debug area for the caller
* The mode parameter allows to specify access rights for the s390dbf files
* - Returns handle for debug area
2005-04-17 02:20:36 +04:00
*/
2008-05-15 18:52:35 +04:00
debug_info_t * debug_register_mode ( const char * name , int pages_per_area ,
int nr_areas , int buf_size , mode_t mode ,
uid_t uid , gid_t gid )
2005-04-17 02:20:36 +04:00
{
debug_info_t * rc = NULL ;
2008-04-17 09:46:18 +04:00
/* Since debugfs currently does not support uid/gid other than root, */
/* we do not allow gid/uid != 0 until we get support for that. */
if ( ( uid ! = 0 ) | | ( gid ! = 0 ) )
printk ( KERN_WARNING " debug: Warning - Currently only uid/gid "
" = 0 are supported. Using root as owner now! " ) ;
2005-04-17 02:20:36 +04:00
if ( ! initialized )
BUG ( ) ;
2007-05-31 19:38:03 +04:00
mutex_lock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
/* create new debug_info */
2008-04-17 09:46:18 +04:00
rc = debug_info_create ( name , pages_per_area , nr_areas , buf_size , mode ) ;
2005-04-17 02:20:36 +04:00
if ( ! rc )
goto out ;
debug_register_view ( rc , & debug_level_view ) ;
debug_register_view ( rc , & debug_flush_view ) ;
2005-06-26 01:55:33 +04:00
debug_register_view ( rc , & debug_pages_view ) ;
out :
if ( ! rc ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " debug: debug_register failed for %s \n " , name ) ;
}
2007-05-31 19:38:03 +04:00
mutex_unlock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2008-04-17 09:46:18 +04:00
EXPORT_SYMBOL ( debug_register_mode ) ;
/*
* debug_register :
* - creates and initializes debug area for the caller
* - returns handle for debug area
*/
2008-05-15 18:52:35 +04:00
debug_info_t * debug_register ( const char * name , int pages_per_area ,
int nr_areas , int buf_size )
2008-04-17 09:46:18 +04:00
{
return debug_register_mode ( name , pages_per_area , nr_areas , buf_size ,
S_IRUSR | S_IWUSR , 0 , 0 ) ;
}
2005-04-17 02:20:36 +04:00
/*
* debug_unregister :
* - give back debug area
*/
2005-06-26 01:55:33 +04:00
void
debug_unregister ( debug_info_t * id )
2005-04-17 02:20:36 +04:00
{
if ( ! id )
goto out ;
2007-05-31 19:38:03 +04:00
mutex_lock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
debug_info_put ( id ) ;
2007-05-31 19:38:03 +04:00
mutex_unlock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:33 +04:00
out :
2005-04-17 02:20:36 +04:00
return ;
}
2005-06-26 01:55:33 +04:00
/*
* debug_set_size :
* - set area size ( number of pages ) and number of areas
*/
static int
debug_set_size ( debug_info_t * id , int nr_areas , int pages_per_area )
{
unsigned long flags ;
debug_entry_t * * * new_areas ;
int rc = 0 ;
if ( ! id | | ( nr_areas < = 0 ) | | ( pages_per_area < 0 ) )
return - EINVAL ;
if ( pages_per_area > 0 ) {
new_areas = debug_areas_alloc ( pages_per_area , nr_areas ) ;
if ( ! new_areas ) {
printk ( KERN_WARNING " debug: could not allocate memory " \
" for pagenumber: %i \n " , pages_per_area ) ;
rc = - ENOMEM ;
goto out ;
}
} else {
new_areas = NULL ;
}
spin_lock_irqsave ( & id - > lock , flags ) ;
debug_areas_free ( id ) ;
id - > areas = new_areas ;
id - > nr_areas = nr_areas ;
id - > pages_per_area = pages_per_area ;
id - > active_area = 0 ;
memset ( id - > active_entries , 0 , sizeof ( int ) * id - > nr_areas ) ;
memset ( id - > active_pages , 0 , sizeof ( int ) * id - > nr_areas ) ;
spin_unlock_irqrestore ( & id - > lock , flags ) ;
printk ( KERN_INFO " debug: %s: set new size (%i pages) \n " \
, id - > name , pages_per_area ) ;
out :
return rc ;
}
2005-04-17 02:20:36 +04:00
/*
* debug_set_level :
* - set actual debug level
*/
2005-06-26 01:55:33 +04:00
void
debug_set_level ( debug_info_t * id , int new_level )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
if ( ! id )
return ;
spin_lock_irqsave ( & id - > lock , flags ) ;
if ( new_level = = DEBUG_OFF_LEVEL ) {
id - > level = DEBUG_OFF_LEVEL ;
printk ( KERN_INFO " debug: %s: switched off \n " , id - > name ) ;
} else if ( ( new_level > DEBUG_MAX_LEVEL ) | | ( new_level < 0 ) ) {
printk ( KERN_INFO
" debug: %s: level %i is out of range (%i - %i) \n " ,
id - > name , new_level , 0 , DEBUG_MAX_LEVEL ) ;
} else {
id - > level = new_level ;
}
spin_unlock_irqrestore ( & id - > lock , flags ) ;
}
/*
* proceed_active_entry :
* - set active entry to next in the ring buffer
*/
2005-11-09 08:34:42 +03:00
static inline void
2005-06-26 01:55:33 +04:00
proceed_active_entry ( debug_info_t * id )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:33 +04:00
if ( ( id - > active_entries [ id - > active_area ] + = id - > entry_size )
> ( PAGE_SIZE - id - > entry_size ) ) {
id - > active_entries [ id - > active_area ] = 0 ;
id - > active_pages [ id - > active_area ] =
( id - > active_pages [ id - > active_area ] + 1 ) %
id - > pages_per_area ;
}
2005-04-17 02:20:36 +04:00
}
/*
* proceed_active_area :
* - set active area to next in the ring buffer
*/
2005-11-09 08:34:42 +03:00
static inline void
2005-06-26 01:55:33 +04:00
proceed_active_area ( debug_info_t * id )
2005-04-17 02:20:36 +04:00
{
id - > active_area + + ;
id - > active_area = id - > active_area % id - > nr_areas ;
}
/*
* get_active_entry :
*/
2005-11-09 08:34:42 +03:00
static inline debug_entry_t *
2005-06-26 01:55:33 +04:00
get_active_entry ( debug_info_t * id )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:33 +04:00
return ( debug_entry_t * ) ( ( ( char * ) id - > areas [ id - > active_area ]
[ id - > active_pages [ id - > active_area ] ] ) +
id - > active_entries [ id - > active_area ] ) ;
2005-04-17 02:20:36 +04:00
}
/*
* debug_finish_entry :
* - set timestamp , caller address , cpu number etc .
*/
2005-11-09 08:34:42 +03:00
static inline void
2005-06-26 01:55:33 +04:00
debug_finish_entry ( debug_info_t * id , debug_entry_t * active , int level ,
int exception )
2005-04-17 02:20:36 +04:00
{
2005-09-04 02:57:58 +04:00
active - > id . stck = get_clock ( ) ;
2005-04-17 02:20:36 +04:00
active - > id . fields . cpuid = smp_processor_id ( ) ;
active - > caller = __builtin_return_address ( 0 ) ;
active - > id . fields . exception = exception ;
active - > id . fields . level = level ;
proceed_active_entry ( id ) ;
if ( exception )
proceed_active_area ( id ) ;
}
static int debug_stoppable = 1 ;
static int debug_active = 1 ;
# define CTL_S390DBF_STOPPABLE 5678
# define CTL_S390DBF_ACTIVE 5679
/*
* proc handler for the running debug_active sysctl
* always allow read , allow write only if debug_stoppable is set or
* if debug_active is already off
*/
2005-06-26 01:55:33 +04:00
static int
s390dbf_procactive ( ctl_table * table , int write , struct file * filp ,
2005-04-17 02:20:36 +04:00
void __user * buffer , size_t * lenp , loff_t * ppos )
{
if ( ! write | | debug_stoppable | | ! debug_active )
return proc_dointvec ( table , write , filp , buffer , lenp , ppos ) ;
else
return 0 ;
}
static struct ctl_table s390dbf_table [ ] = {
{
. ctl_name = CTL_S390DBF_STOPPABLE ,
. procname = " debug_stoppable " ,
. data = & debug_stoppable ,
. maxlen = sizeof ( int ) ,
. mode = S_IRUGO | S_IWUSR ,
. proc_handler = & proc_dointvec ,
. strategy = & sysctl_intvec ,
} ,
{
. ctl_name = CTL_S390DBF_ACTIVE ,
. procname = " debug_active " ,
. data = & debug_active ,
. maxlen = sizeof ( int ) ,
. mode = S_IRUGO | S_IWUSR ,
. proc_handler = & s390dbf_procactive ,
. strategy = & sysctl_intvec ,
} ,
{ . ctl_name = 0 }
} ;
static struct ctl_table s390dbf_dir_table [ ] = {
{
. ctl_name = CTL_S390DBF ,
. procname = " s390dbf " ,
. maxlen = 0 ,
. mode = S_IRUGO | S_IXUGO ,
. child = s390dbf_table ,
} ,
{ . ctl_name = 0 }
} ;
2007-02-05 23:16:47 +03:00
static struct ctl_table_header * s390dbf_sysctl_header ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:33 +04:00
void
debug_stop_all ( void )
2005-04-17 02:20:36 +04:00
{
if ( debug_stoppable )
debug_active = 0 ;
}
/*
* debug_event_common :
* - write debug entry with given size
*/
2005-06-26 01:55:33 +04:00
debug_entry_t *
debug_event_common ( debug_info_t * id , int level , const void * buf , int len )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
debug_entry_t * active ;
2005-06-26 01:55:33 +04:00
if ( ! debug_active | | ! id - > areas )
2005-04-17 02:20:36 +04:00
return NULL ;
spin_lock_irqsave ( & id - > lock , flags ) ;
active = get_active_entry ( id ) ;
memset ( DEBUG_DATA ( active ) , 0 , id - > buf_size ) ;
memcpy ( DEBUG_DATA ( active ) , buf , min ( len , id - > buf_size ) ) ;
debug_finish_entry ( id , active , level , 0 ) ;
spin_unlock_irqrestore ( & id - > lock , flags ) ;
return active ;
}
/*
* debug_exception_common :
* - write debug entry with given size and switch to next debug area
*/
2005-06-26 01:55:33 +04:00
debug_entry_t
* debug_exception_common ( debug_info_t * id , int level , const void * buf , int len )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
debug_entry_t * active ;
2005-06-26 01:55:33 +04:00
if ( ! debug_active | | ! id - > areas )
2005-04-17 02:20:36 +04:00
return NULL ;
spin_lock_irqsave ( & id - > lock , flags ) ;
active = get_active_entry ( id ) ;
memset ( DEBUG_DATA ( active ) , 0 , id - > buf_size ) ;
memcpy ( DEBUG_DATA ( active ) , buf , min ( len , id - > buf_size ) ) ;
debug_finish_entry ( id , active , level , 1 ) ;
spin_unlock_irqrestore ( & id - > lock , flags ) ;
return active ;
}
/*
* counts arguments in format string for sprintf view
*/
2005-11-09 08:34:42 +03:00
static inline int
2005-06-26 01:55:33 +04:00
debug_count_numargs ( char * string )
2005-04-17 02:20:36 +04:00
{
int numargs = 0 ;
while ( * string ) {
if ( * string + + = = ' % ' )
numargs + + ;
}
return ( numargs ) ;
}
/*
* debug_sprintf_event :
*/
2005-06-26 01:55:33 +04:00
debug_entry_t *
debug_sprintf_event ( debug_info_t * id , int level , char * string , . . . )
2005-04-17 02:20:36 +04:00
{
va_list ap ;
int numargs , idx ;
unsigned long flags ;
debug_sprintf_entry_t * curr_event ;
debug_entry_t * active ;
if ( ( ! id ) | | ( level > id - > level ) )
return NULL ;
2005-06-26 01:55:33 +04:00
if ( ! debug_active | | ! id - > areas )
2005-04-17 02:20:36 +04:00
return NULL ;
numargs = debug_count_numargs ( string ) ;
spin_lock_irqsave ( & id - > lock , flags ) ;
active = get_active_entry ( id ) ;
curr_event = ( debug_sprintf_entry_t * ) DEBUG_DATA ( active ) ;
va_start ( ap , string ) ;
curr_event - > string = string ;
for ( idx = 0 ; idx < min ( numargs , ( int ) ( id - > buf_size / sizeof ( long ) ) - 1 ) ; idx + + )
curr_event - > args [ idx ] = va_arg ( ap , long ) ;
va_end ( ap ) ;
debug_finish_entry ( id , active , level , 0 ) ;
spin_unlock_irqrestore ( & id - > lock , flags ) ;
return active ;
}
/*
* debug_sprintf_exception :
*/
2005-06-26 01:55:33 +04:00
debug_entry_t *
debug_sprintf_exception ( debug_info_t * id , int level , char * string , . . . )
2005-04-17 02:20:36 +04:00
{
va_list ap ;
int numargs , idx ;
unsigned long flags ;
debug_sprintf_entry_t * curr_event ;
debug_entry_t * active ;
if ( ( ! id ) | | ( level > id - > level ) )
return NULL ;
2005-06-26 01:55:33 +04:00
if ( ! debug_active | | ! id - > areas )
2005-04-17 02:20:36 +04:00
return NULL ;
numargs = debug_count_numargs ( string ) ;
spin_lock_irqsave ( & id - > lock , flags ) ;
active = get_active_entry ( id ) ;
curr_event = ( debug_sprintf_entry_t * ) DEBUG_DATA ( active ) ;
va_start ( ap , string ) ;
curr_event - > string = string ;
for ( idx = 0 ; idx < min ( numargs , ( int ) ( id - > buf_size / sizeof ( long ) ) - 1 ) ; idx + + )
curr_event - > args [ idx ] = va_arg ( ap , long ) ;
va_end ( ap ) ;
debug_finish_entry ( id , active , level , 1 ) ;
spin_unlock_irqrestore ( & id - > lock , flags ) ;
return active ;
}
/*
* debug_init :
* - is called exactly once to initialize the debug feature
*/
2005-06-26 01:55:33 +04:00
static int
__init debug_init ( void )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
2007-02-14 11:34:09 +03:00
s390dbf_sysctl_header = register_sysctl_table ( s390dbf_dir_table ) ;
2007-05-31 19:38:03 +04:00
mutex_lock ( & debug_mutex ) ;
2005-06-26 01:55:33 +04:00
debug_debugfs_root_entry = debugfs_create_dir ( DEBUG_DIR_ROOT , NULL ) ;
2005-04-17 02:20:36 +04:00
initialized = 1 ;
2007-05-31 19:38:03 +04:00
mutex_unlock ( & debug_mutex ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* debug_register_view :
*/
2005-06-26 01:55:33 +04:00
int
debug_register_view ( debug_info_t * id , struct debug_view * view )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
int i ;
unsigned long flags ;
2008-04-17 09:46:18 +04:00
mode_t mode ;
2005-06-26 01:55:33 +04:00
struct dentry * pde ;
2005-04-17 02:20:36 +04:00
if ( ! id )
goto out ;
2008-04-17 09:46:18 +04:00
mode = ( id - > mode | S_IFREG ) & ~ S_IXUGO ;
if ( ! ( view - > prolog_proc | | view - > format_proc | | view - > header_proc ) )
mode & = ~ ( S_IRUSR | S_IRGRP | S_IROTH ) ;
if ( ! view - > input_proc )
mode & = ~ ( S_IWUSR | S_IWGRP | S_IWOTH ) ;
2005-06-26 01:55:33 +04:00
pde = debugfs_create_file ( view - > name , mode , id - > debugfs_root_entry ,
2005-09-04 02:57:58 +04:00
id , & debug_file_ops ) ;
2005-04-17 02:20:36 +04:00
if ( ! pde ) {
2005-06-26 01:55:33 +04:00
printk ( KERN_WARNING " debug: debugfs_create_file() failed! " \
" Cannot register view %s/%s \n " , id - > name , view - > name ) ;
2005-04-17 02:20:36 +04:00
rc = - 1 ;
goto out ;
}
spin_lock_irqsave ( & id - > lock , flags ) ;
for ( i = 0 ; i < DEBUG_MAX_VIEWS ; i + + ) {
2005-06-26 01:55:33 +04:00
if ( ! id - > views [ i ] )
2005-04-17 02:20:36 +04:00
break ;
}
if ( i = = DEBUG_MAX_VIEWS ) {
printk ( KERN_WARNING " debug: cannot register view %s/%s \n " ,
id - > name , view - > name ) ;
printk ( KERN_WARNING
" debug: maximum number of views reached (%i)! \n " , i ) ;
2005-06-26 01:55:33 +04:00
debugfs_remove ( pde ) ;
2005-04-17 02:20:36 +04:00
rc = - 1 ;
2005-06-26 01:55:33 +04:00
} else {
2005-04-17 02:20:36 +04:00
id - > views [ i ] = view ;
2005-06-26 01:55:33 +04:00
id - > debugfs_entries [ i ] = pde ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & id - > lock , flags ) ;
2005-06-26 01:55:33 +04:00
out :
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* debug_unregister_view :
*/
2005-06-26 01:55:33 +04:00
int
debug_unregister_view ( debug_info_t * id , struct debug_view * view )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
int i ;
unsigned long flags ;
if ( ! id )
goto out ;
spin_lock_irqsave ( & id - > lock , flags ) ;
for ( i = 0 ; i < DEBUG_MAX_VIEWS ; i + + ) {
if ( id - > views [ i ] = = view )
break ;
}
if ( i = = DEBUG_MAX_VIEWS )
rc = - 1 ;
else {
2005-06-26 01:55:33 +04:00
debugfs_remove ( id - > debugfs_entries [ i ] ) ;
2005-04-17 02:20:36 +04:00
id - > views [ i ] = NULL ;
rc = 0 ;
}
spin_unlock_irqrestore ( & id - > lock , flags ) ;
2005-06-26 01:55:33 +04:00
out :
return rc ;
}
static inline char *
debug_get_user_string ( const char __user * user_buf , size_t user_len )
{
char * buffer ;
buffer = kmalloc ( user_len + 1 , GFP_KERNEL ) ;
if ( ! buffer )
return ERR_PTR ( - ENOMEM ) ;
if ( copy_from_user ( buffer , user_buf , user_len ) ! = 0 ) {
kfree ( buffer ) ;
return ERR_PTR ( - EFAULT ) ;
}
/* got the string, now strip linefeed. */
if ( buffer [ user_len - 1 ] = = ' \n ' )
buffer [ user_len - 1 ] = 0 ;
else
buffer [ user_len ] = 0 ;
return buffer ;
}
static inline int
debug_get_uint ( char * buf )
{
int rc ;
for ( ; isspace ( * buf ) ; buf + + ) ;
rc = simple_strtoul ( buf , & buf , 10 ) ;
if ( * buf ) {
rc = - EINVAL ;
}
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* functions for debug - views
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* prints out actual debug level
*/
2005-06-26 01:55:33 +04:00
static int
debug_prolog_pages_fn ( debug_info_t * id ,
2005-04-17 02:20:36 +04:00
struct debug_view * view , char * out_buf )
2005-06-26 01:55:33 +04:00
{
return sprintf ( out_buf , " %i \n " , id - > pages_per_area ) ;
}
/*
* reads new size ( number of pages per debug area )
*/
static int
debug_input_pages_fn ( debug_info_t * id , struct debug_view * view ,
struct file * file , const char __user * user_buf ,
size_t user_len , loff_t * offset )
{
char * str ;
int rc , new_pages ;
if ( user_len > 0x10000 )
user_len = 0x10000 ;
if ( * offset ! = 0 ) {
rc = - EPIPE ;
goto out ;
}
str = debug_get_user_string ( user_buf , user_len ) ;
if ( IS_ERR ( str ) ) {
rc = PTR_ERR ( str ) ;
goto out ;
}
new_pages = debug_get_uint ( str ) ;
if ( new_pages < 0 ) {
rc = - EINVAL ;
goto free_str ;
}
rc = debug_set_size ( id , id - > nr_areas , new_pages ) ;
if ( rc ! = 0 ) {
rc = - EINVAL ;
goto free_str ;
}
rc = user_len ;
free_str :
kfree ( str ) ;
out :
* offset + = user_len ;
return rc ; /* number of input characters */
}
/*
* prints out actual debug level
*/
static int
debug_prolog_level_fn ( debug_info_t * id , struct debug_view * view , char * out_buf )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
2005-06-26 01:55:33 +04:00
if ( id - > level = = DEBUG_OFF_LEVEL ) {
rc = sprintf ( out_buf , " - \n " ) ;
}
else {
rc = sprintf ( out_buf , " %i \n " , id - > level ) ;
}
2005-04-17 02:20:36 +04:00
return rc ;
}
/*
* reads new debug level
*/
2005-06-26 01:55:33 +04:00
static int
debug_input_level_fn ( debug_info_t * id , struct debug_view * view ,
struct file * file , const char __user * user_buf ,
size_t user_len , loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:33 +04:00
char * str ;
int rc , new_level ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:33 +04:00
if ( user_len > 0x10000 )
user_len = 0x10000 ;
if ( * offset ! = 0 ) {
rc = - EPIPE ;
2005-04-17 02:20:36 +04:00
goto out ;
2005-06-26 01:55:33 +04:00
}
str = debug_get_user_string ( user_buf , user_len ) ;
if ( IS_ERR ( str ) ) {
rc = PTR_ERR ( str ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2005-06-26 01:55:33 +04:00
if ( str [ 0 ] = = ' - ' ) {
2005-04-17 02:20:36 +04:00
debug_set_level ( id , DEBUG_OFF_LEVEL ) ;
2005-06-26 01:55:33 +04:00
rc = user_len ;
goto free_str ;
2005-04-17 02:20:36 +04:00
} else {
2005-06-26 01:55:33 +04:00
new_level = debug_get_uint ( str ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:55:33 +04:00
if ( new_level < 0 ) {
printk ( KERN_INFO " debug: level `%s` is not valid \n " , str ) ;
rc = - EINVAL ;
} else {
debug_set_level ( id , new_level ) ;
rc = user_len ;
}
free_str :
kfree ( str ) ;
out :
* offset + = user_len ;
2005-04-17 02:20:36 +04:00
return rc ; /* number of input characters */
}
/*
* flushes debug areas
*/
2007-02-05 23:16:47 +03:00
static void debug_flush ( debug_info_t * id , int area )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2005-06-26 01:55:33 +04:00
int i , j ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:55:33 +04:00
if ( ! id | | ! id - > areas )
2005-04-17 02:20:36 +04:00
return ;
spin_lock_irqsave ( & id - > lock , flags ) ;
if ( area = = DEBUG_FLUSH_ALL ) {
id - > active_area = 0 ;
2005-06-26 01:55:33 +04:00
memset ( id - > active_entries , 0 , id - > nr_areas * sizeof ( int ) ) ;
for ( i = 0 ; i < id - > nr_areas ; i + + ) {
id - > active_pages [ i ] = 0 ;
for ( j = 0 ; j < id - > pages_per_area ; j + + ) {
memset ( id - > areas [ i ] [ j ] , 0 , PAGE_SIZE ) ;
}
}
2005-04-17 02:20:36 +04:00
} else if ( area > = 0 & & area < id - > nr_areas ) {
2005-06-26 01:55:33 +04:00
id - > active_entries [ area ] = 0 ;
id - > active_pages [ area ] = 0 ;
for ( i = 0 ; i < id - > pages_per_area ; i + + ) {
memset ( id - > areas [ area ] [ i ] , 0 , PAGE_SIZE ) ;
}
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & id - > lock , flags ) ;
}
/*
* view function : flushes debug areas
*/
2005-06-26 01:55:33 +04:00
static int
debug_input_flush_fn ( debug_info_t * id , struct debug_view * view ,
struct file * file , const char __user * user_buf ,
size_t user_len , loff_t * offset )
2005-04-17 02:20:36 +04:00
{
char input_buf [ 1 ] ;
2005-06-26 01:55:33 +04:00
int rc = user_len ;
if ( user_len > 0x10000 )
user_len = 0x10000 ;
if ( * offset ! = 0 ) {
rc = - EPIPE ;
2005-04-17 02:20:36 +04:00
goto out ;
2005-06-26 01:55:33 +04:00
}
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( input_buf , user_buf , 1 ) ) {
rc = - EFAULT ;
goto out ;
}
if ( input_buf [ 0 ] = = ' - ' ) {
debug_flush ( id , DEBUG_FLUSH_ALL ) ;
goto out ;
}
if ( isdigit ( input_buf [ 0 ] ) ) {
int area = ( ( int ) input_buf [ 0 ] - ( int ) ' 0 ' ) ;
debug_flush ( id , area ) ;
goto out ;
}
printk ( KERN_INFO " debug: area `%c` is not valid \n " , input_buf [ 0 ] ) ;
2005-06-26 01:55:33 +04:00
out :
* offset + = user_len ;
2005-04-17 02:20:36 +04:00
return rc ; /* number of input characters */
}
/*
* prints debug header in raw format
*/
2005-06-26 01:55:33 +04:00
static int
debug_raw_header_fn ( debug_info_t * id , struct debug_view * view ,
int area , debug_entry_t * entry , char * out_buf )
2005-04-17 02:20:36 +04:00
{
int rc ;
rc = sizeof ( debug_entry_t ) ;
memcpy ( out_buf , entry , sizeof ( debug_entry_t ) ) ;
return rc ;
}
/*
* prints debug data in raw format
*/
2005-06-26 01:55:33 +04:00
static int
debug_raw_format_fn ( debug_info_t * id , struct debug_view * view ,
2005-04-17 02:20:36 +04:00
char * out_buf , const char * in_buf )
{
int rc ;
rc = id - > buf_size ;
memcpy ( out_buf , in_buf , id - > buf_size ) ;
return rc ;
}
/*
* prints debug data in hex / ascii format
*/
2005-06-26 01:55:33 +04:00
static int
debug_hex_ascii_format_fn ( debug_info_t * id , struct debug_view * view ,
char * out_buf , const char * in_buf )
2005-04-17 02:20:36 +04:00
{
int i , rc = 0 ;
for ( i = 0 ; i < id - > buf_size ; i + + ) {
rc + = sprintf ( out_buf + rc , " %02x " ,
( ( unsigned char * ) in_buf ) [ i ] ) ;
}
rc + = sprintf ( out_buf + rc , " | " ) ;
for ( i = 0 ; i < id - > buf_size ; i + + ) {
unsigned char c = in_buf [ i ] ;
if ( ! isprint ( c ) )
rc + = sprintf ( out_buf + rc , " . " ) ;
else
rc + = sprintf ( out_buf + rc , " %c " , c ) ;
}
rc + = sprintf ( out_buf + rc , " \n " ) ;
return rc ;
}
/*
* prints header for debug entry
*/
2005-06-26 01:55:33 +04:00
int
debug_dflt_header_fn ( debug_info_t * id , struct debug_view * view ,
2005-04-17 02:20:36 +04:00
int area , debug_entry_t * entry , char * out_buf )
{
2005-09-04 02:57:58 +04:00
struct timespec time_spec ;
2005-04-17 02:20:36 +04:00
unsigned long long time ;
char * except_str ;
unsigned long caller ;
int rc = 0 ;
unsigned int level ;
level = entry - > id . fields . level ;
time = entry - > id . stck ;
/* adjust todclock to 1970 */
time - = 0x8126d60e46000000LL - ( 0x3c26700LL * 1000000 * 4096 ) ;
2005-09-04 02:57:58 +04:00
tod_to_timeval ( time , & time_spec ) ;
2005-04-17 02:20:36 +04:00
if ( entry - > id . fields . exception )
except_str = " * " ;
else
except_str = " - " ;
caller = ( ( unsigned long ) entry - > caller ) & PSW_ADDR_INSN ;
rc + = sprintf ( out_buf , " %02i %011lu:%06lu %1u %1s %02i %p " ,
2005-09-04 02:57:58 +04:00
area , time_spec . tv_sec , time_spec . tv_nsec / 1000 , level ,
2005-04-17 02:20:36 +04:00
except_str , entry - > id . fields . cpuid , ( void * ) caller ) ;
return rc ;
}
/*
* prints debug data sprintf - formated :
* debug_sprinf_event / exception calls must be used together with this view
*/
# define DEBUG_SPRINTF_MAX_ARGS 10
2005-06-26 01:55:33 +04:00
static int
debug_sprintf_format_fn ( debug_info_t * id , struct debug_view * view ,
char * out_buf , debug_sprintf_entry_t * curr_event )
2005-04-17 02:20:36 +04:00
{
int num_longs , num_used_args = 0 , i , rc = 0 ;
int index [ DEBUG_SPRINTF_MAX_ARGS ] ;
/* count of longs fit into one entry */
num_longs = id - > buf_size / sizeof ( long ) ;
if ( num_longs < 1 )
goto out ; /* bufsize of entry too small */
if ( num_longs = = 1 ) {
/* no args, we use only the string */
strcpy ( out_buf , curr_event - > string ) ;
rc = strlen ( curr_event - > string ) ;
goto out ;
}
/* number of arguments used for sprintf (without the format string) */
num_used_args = min ( DEBUG_SPRINTF_MAX_ARGS , ( num_longs - 1 ) ) ;
memset ( index , 0 , DEBUG_SPRINTF_MAX_ARGS * sizeof ( int ) ) ;
for ( i = 0 ; i < num_used_args ; i + + )
index [ i ] = i ;
rc = sprintf ( out_buf , curr_event - > string , curr_event - > args [ index [ 0 ] ] ,
curr_event - > args [ index [ 1 ] ] , curr_event - > args [ index [ 2 ] ] ,
curr_event - > args [ index [ 3 ] ] , curr_event - > args [ index [ 4 ] ] ,
curr_event - > args [ index [ 5 ] ] , curr_event - > args [ index [ 6 ] ] ,
curr_event - > args [ index [ 7 ] ] , curr_event - > args [ index [ 8 ] ] ,
curr_event - > args [ index [ 9 ] ] ) ;
out :
return rc ;
}
/*
* clean up module
*/
2007-02-05 23:16:47 +03:00
static void __exit debug_exit ( void )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:55:33 +04:00
debugfs_remove ( debug_debugfs_root_entry ) ;
2005-04-17 02:20:36 +04:00
unregister_sysctl_table ( s390dbf_sysctl_header ) ;
return ;
}
/*
* module definitions
*/
2005-06-26 01:55:33 +04:00
postcore_initcall ( debug_init ) ;
2005-04-17 02:20:36 +04:00
module_exit ( debug_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
EXPORT_SYMBOL ( debug_register ) ;
EXPORT_SYMBOL ( debug_unregister ) ;
EXPORT_SYMBOL ( debug_set_level ) ;
EXPORT_SYMBOL ( debug_stop_all ) ;
EXPORT_SYMBOL ( debug_register_view ) ;
EXPORT_SYMBOL ( debug_unregister_view ) ;
EXPORT_SYMBOL ( debug_event_common ) ;
EXPORT_SYMBOL ( debug_exception_common ) ;
EXPORT_SYMBOL ( debug_hex_ascii_view ) ;
EXPORT_SYMBOL ( debug_raw_view ) ;
EXPORT_SYMBOL ( debug_dflt_header_fn ) ;
EXPORT_SYMBOL ( debug_sprintf_view ) ;
EXPORT_SYMBOL ( debug_sprintf_exception ) ;
EXPORT_SYMBOL ( debug_sprintf_event ) ;