2013-02-13 05:01:02 +04:00
/*
* Copyright 2012 Cisco Systems , Inc . All rights reserved .
*
* This program is free software ; you may redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <linux/module.h>
# include <linux/mempool.h>
# include <linux/errno.h>
# include <linux/spinlock.h>
# include <linux/kallsyms.h>
2014-04-18 23:28:19 +04:00
# include <linux/time.h>
2015-06-02 12:01:38 +03:00
# include <linux/vmalloc.h>
2013-02-13 05:01:02 +04:00
# include "fnic_io.h"
# include "fnic.h"
unsigned int trace_max_pages ;
static int fnic_max_trace_entries ;
static unsigned long fnic_trace_buf_p ;
static DEFINE_SPINLOCK ( fnic_trace_lock ) ;
static fnic_trace_dbg_t fnic_trace_entries ;
int fnic_tracing_enabled = 1 ;
2014-04-18 23:28:19 +04:00
/* static char *fnic_fc_ctlr_trace_buf_p; */
static int fc_trace_max_entries ;
static unsigned long fnic_fc_ctlr_trace_buf_p ;
static fnic_trace_dbg_t fc_trace_entries ;
int fnic_fc_tracing_enabled = 1 ;
int fnic_fc_trace_cleared = 1 ;
static DEFINE_SPINLOCK ( fnic_fc_trace_lock ) ;
2013-02-13 05:01:02 +04:00
/*
* fnic_trace_get_buf - Give buffer pointer to user to fill up trace information
*
* Description :
* This routine gets next available trace buffer entry location @ wr_idx
* from allocated trace buffer pages and give that memory location
* to user to store the trace information .
*
* Return Value :
* This routine returns pointer to next available trace entry
* @ fnic_buf_head for user to fill trace information .
*/
fnic_trace_data_t * fnic_trace_get_buf ( void )
{
unsigned long fnic_buf_head ;
unsigned long flags ;
spin_lock_irqsave ( & fnic_trace_lock , flags ) ;
/*
* Get next available memory location for writing trace information
* at @ wr_idx and increment @ wr_idx
*/
fnic_buf_head =
fnic_trace_entries . page_offset [ fnic_trace_entries . wr_idx ] ;
fnic_trace_entries . wr_idx + + ;
/*
* Verify if trace buffer is full then change wd_idx to
* start from zero
*/
if ( fnic_trace_entries . wr_idx > = fnic_max_trace_entries )
fnic_trace_entries . wr_idx = 0 ;
/*
* Verify if write index @ wr_idx and read index @ rd_idx are same then
* increment @ rd_idx to move to next entry in trace buffer
*/
if ( fnic_trace_entries . wr_idx = = fnic_trace_entries . rd_idx ) {
fnic_trace_entries . rd_idx + + ;
if ( fnic_trace_entries . rd_idx > = fnic_max_trace_entries )
fnic_trace_entries . rd_idx = 0 ;
}
spin_unlock_irqrestore ( & fnic_trace_lock , flags ) ;
return ( fnic_trace_data_t * ) fnic_buf_head ;
}
/*
* fnic_get_trace_data - Copy trace buffer to a memory file
* @ fnic_dbgfs_t : pointer to debugfs trace buffer
*
* Description :
* This routine gathers the fnic trace debugfs data from the fnic_trace_data_t
* buffer and dumps it to fnic_dbgfs_t . It will start at the rd_idx entry in
* the log and process the log until the end of the buffer . Then it will gather
* from the beginning of the log and process until the current entry @ wr_idx .
*
* Return Value :
* This routine returns the amount of bytes that were dumped into fnic_dbgfs_t
*/
int fnic_get_trace_data ( fnic_dbgfs_t * fnic_dbgfs_prt )
{
int rd_idx ;
int wr_idx ;
int len = 0 ;
unsigned long flags ;
char str [ KSYM_SYMBOL_LEN ] ;
struct timespec val ;
fnic_trace_data_t * tbp ;
spin_lock_irqsave ( & fnic_trace_lock , flags ) ;
rd_idx = fnic_trace_entries . rd_idx ;
wr_idx = fnic_trace_entries . wr_idx ;
if ( wr_idx < rd_idx ) {
while ( 1 ) {
/* Start from read index @rd_idx */
tbp = ( fnic_trace_data_t * )
fnic_trace_entries . page_offset [ rd_idx ] ;
if ( ! tbp ) {
spin_unlock_irqrestore ( & fnic_trace_lock , flags ) ;
return 0 ;
}
/* Convert function pointer to function name */
if ( sizeof ( unsigned long ) < 8 ) {
sprint_symbol ( str , tbp - > fnaddr . low ) ;
jiffies_to_timespec ( tbp - > timestamp . low , & val ) ;
} else {
sprint_symbol ( str , tbp - > fnaddr . val ) ;
jiffies_to_timespec ( tbp - > timestamp . val , & val ) ;
}
/*
* Dump trace buffer entry to memory file
* and increment read index @ rd_idx
*/
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
( trace_max_pages * PAGE_SIZE * 3 ) - len ,
" %16lu.%16lu %-50s %8x %8x %16llx %16llx "
" %16llx %16llx %16llx \n " , val . tv_sec ,
val . tv_nsec , str , tbp - > host_no , tbp - > tag ,
tbp - > data [ 0 ] , tbp - > data [ 1 ] , tbp - > data [ 2 ] ,
tbp - > data [ 3 ] , tbp - > data [ 4 ] ) ;
rd_idx + + ;
/*
* If rd_idx is reached to maximum trace entries
* then move rd_idx to zero
*/
if ( rd_idx > ( fnic_max_trace_entries - 1 ) )
rd_idx = 0 ;
/*
* Continure dumpping trace buffer entries into
* memory file till rd_idx reaches write index
*/
if ( rd_idx = = wr_idx )
break ;
}
} else if ( wr_idx > rd_idx ) {
while ( 1 ) {
/* Start from read index @rd_idx */
tbp = ( fnic_trace_data_t * )
fnic_trace_entries . page_offset [ rd_idx ] ;
if ( ! tbp ) {
spin_unlock_irqrestore ( & fnic_trace_lock , flags ) ;
return 0 ;
}
/* Convert function pointer to function name */
if ( sizeof ( unsigned long ) < 8 ) {
sprint_symbol ( str , tbp - > fnaddr . low ) ;
jiffies_to_timespec ( tbp - > timestamp . low , & val ) ;
} else {
sprint_symbol ( str , tbp - > fnaddr . val ) ;
jiffies_to_timespec ( tbp - > timestamp . val , & val ) ;
}
/*
* Dump trace buffer entry to memory file
* and increment read index @ rd_idx
*/
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
( trace_max_pages * PAGE_SIZE * 3 ) - len ,
" %16lu.%16lu %-50s %8x %8x %16llx %16llx "
" %16llx %16llx %16llx \n " , val . tv_sec ,
val . tv_nsec , str , tbp - > host_no , tbp - > tag ,
tbp - > data [ 0 ] , tbp - > data [ 1 ] , tbp - > data [ 2 ] ,
tbp - > data [ 3 ] , tbp - > data [ 4 ] ) ;
rd_idx + + ;
/*
* Continue dumpping trace buffer entries into
* memory file till rd_idx reaches write index
*/
if ( rd_idx = = wr_idx )
break ;
}
}
spin_unlock_irqrestore ( & fnic_trace_lock , flags ) ;
return len ;
}
2013-09-13 04:45:42 +04:00
/*
* fnic_get_stats_data - Copy fnic stats buffer to a memory file
* @ fnic_dbgfs_t : pointer to debugfs fnic stats buffer
*
* Description :
* This routine gathers the fnic stats debugfs data from the fnic_stats struct
* and dumps it to stats_debug_info .
*
* Return Value :
* This routine returns the amount of bytes that were dumped into
* stats_debug_info
*/
int fnic_get_stats_data ( struct stats_debug_info * debug ,
struct fnic_stats * stats )
{
int len = 0 ;
int buf_size = debug - > buf_size ;
struct timespec val1 , val2 ;
len = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" ------------------------------------------ \n "
" \t \t IO Statistics \n "
" ------------------------------------------ \n " ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Number of Active IOs: %lld \n Maximum Active IOs: %lld \n "
" Number of IOs: %lld \n Number of IO Completions: %lld \n "
" Number of IO Failures: %lld \n Number of IO NOT Found: %lld \n "
" Number of Memory alloc Failures: %lld \n "
" Number of IOREQ Null: %lld \n "
" Number of SCSI cmd pointer Null: %lld \n " ,
( u64 ) atomic64_read ( & stats - > io_stats . active_ios ) ,
( u64 ) atomic64_read ( & stats - > io_stats . max_active_ios ) ,
( u64 ) atomic64_read ( & stats - > io_stats . num_ios ) ,
( u64 ) atomic64_read ( & stats - > io_stats . io_completions ) ,
( u64 ) atomic64_read ( & stats - > io_stats . io_failures ) ,
( u64 ) atomic64_read ( & stats - > io_stats . io_not_found ) ,
( u64 ) atomic64_read ( & stats - > io_stats . alloc_failures ) ,
( u64 ) atomic64_read ( & stats - > io_stats . ioreq_null ) ,
( u64 ) atomic64_read ( & stats - > io_stats . sc_null ) ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" \n ------------------------------------------ \n "
" \t \t Abort Statistics \n "
" ------------------------------------------ \n " ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Number of Aborts: %lld \n "
" Number of Abort Failures: %lld \n "
" Number of Abort Driver Timeouts: %lld \n "
" Number of Abort FW Timeouts: %lld \n "
" Number of Abort IO NOT Found: %lld \n " ,
( u64 ) atomic64_read ( & stats - > abts_stats . aborts ) ,
( u64 ) atomic64_read ( & stats - > abts_stats . abort_failures ) ,
( u64 ) atomic64_read ( & stats - > abts_stats . abort_drv_timeouts ) ,
( u64 ) atomic64_read ( & stats - > abts_stats . abort_fw_timeouts ) ,
( u64 ) atomic64_read ( & stats - > abts_stats . abort_io_not_found ) ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" \n ------------------------------------------ \n "
" \t \t Terminate Statistics \n "
" ------------------------------------------ \n " ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Number of Terminates: %lld \n "
" Maximum Terminates: %lld \n "
" Number of Terminate Driver Timeouts: %lld \n "
" Number of Terminate FW Timeouts: %lld \n "
" Number of Terminate IO NOT Found: %lld \n "
" Number of Terminate Failures: %lld \n " ,
( u64 ) atomic64_read ( & stats - > term_stats . terminates ) ,
( u64 ) atomic64_read ( & stats - > term_stats . max_terminates ) ,
( u64 ) atomic64_read ( & stats - > term_stats . terminate_drv_timeouts ) ,
( u64 ) atomic64_read ( & stats - > term_stats . terminate_fw_timeouts ) ,
( u64 ) atomic64_read ( & stats - > term_stats . terminate_io_not_found ) ,
( u64 ) atomic64_read ( & stats - > term_stats . terminate_failures ) ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" \n ------------------------------------------ \n "
" \t \t Reset Statistics \n "
" ------------------------------------------ \n " ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Number of Device Resets: %lld \n "
" Number of Device Reset Failures: %lld \n "
" Number of Device Reset Aborts: %lld \n "
" Number of Device Reset Timeouts: %lld \n "
" Number of Device Reset Terminates: %lld \n "
" Number of FW Resets: %lld \n "
" Number of FW Reset Completions: %lld \n "
" Number of FW Reset Failures: %lld \n "
" Number of Fnic Reset: %lld \n "
" Number of Fnic Reset Completions: %lld \n "
" Number of Fnic Reset Failures: %lld \n " ,
( u64 ) atomic64_read ( & stats - > reset_stats . device_resets ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . device_reset_failures ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . device_reset_aborts ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . device_reset_timeouts ) ,
( u64 ) atomic64_read (
& stats - > reset_stats . device_reset_terminates ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . fw_resets ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . fw_reset_completions ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . fw_reset_failures ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . fnic_resets ) ,
( u64 ) atomic64_read (
& stats - > reset_stats . fnic_reset_completions ) ,
( u64 ) atomic64_read ( & stats - > reset_stats . fnic_reset_failures ) ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" \n ------------------------------------------ \n "
" \t \t Firmware Statistics \n "
" ------------------------------------------ \n " ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Number of Active FW Requests %lld \n "
" Maximum FW Requests: %lld \n "
" Number of FW out of resources: %lld \n "
" Number of FW IO errors: %lld \n " ,
( u64 ) atomic64_read ( & stats - > fw_stats . active_fw_reqs ) ,
( u64 ) atomic64_read ( & stats - > fw_stats . max_fw_reqs ) ,
( u64 ) atomic64_read ( & stats - > fw_stats . fw_out_of_resources ) ,
( u64 ) atomic64_read ( & stats - > fw_stats . io_fw_errs ) ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" \n ------------------------------------------ \n "
" \t \t Vlan Discovery Statistics \n "
" ------------------------------------------ \n " ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Number of Vlan Discovery Requests Sent %lld \n "
" Vlan Response Received with no FCF VLAN ID: %lld \n "
" No solicitations recvd after vlan set, expiry count: %lld \n "
" Flogi rejects count: %lld \n " ,
( u64 ) atomic64_read ( & stats - > vlan_stats . vlan_disc_reqs ) ,
( u64 ) atomic64_read ( & stats - > vlan_stats . resp_withno_vlanID ) ,
( u64 ) atomic64_read ( & stats - > vlan_stats . sol_expiry_count ) ,
( u64 ) atomic64_read ( & stats - > vlan_stats . flogi_rejects ) ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" \n ------------------------------------------ \n "
" \t \t Other Important Statistics \n "
" ------------------------------------------ \n " ) ;
jiffies_to_timespec ( stats - > misc_stats . last_isr_time , & val1 ) ;
jiffies_to_timespec ( stats - > misc_stats . last_ack_time , & val2 ) ;
len + = snprintf ( debug - > debug_buffer + len , buf_size - len ,
" Last ISR time: %llu (%8lu.%8lu) \n "
" Last ACK time: %llu (%8lu.%8lu) \n "
" Number of ISRs: %lld \n "
" Maximum CQ Entries: %lld \n "
" Number of ACK index out of range: %lld \n "
" Number of data count mismatch: %lld \n "
" Number of FCPIO Timeouts: %lld \n "
" Number of FCPIO Aborted: %lld \n "
" Number of SGL Invalid: %lld \n "
" Number of Copy WQ Alloc Failures for ABTs: %lld \n "
" Number of Copy WQ Alloc Failures for Device Reset: %lld \n "
" Number of Copy WQ Alloc Failures for IOs: %lld \n "
" Number of no icmnd itmf Completions: %lld \n "
" Number of QUEUE Fulls: %lld \n "
" Number of rport not ready: %lld \n "
" Number of receive frame errors: %lld \n " ,
( u64 ) stats - > misc_stats . last_isr_time ,
val1 . tv_sec , val1 . tv_nsec ,
( u64 ) stats - > misc_stats . last_ack_time ,
val2 . tv_sec , val2 . tv_nsec ,
( u64 ) atomic64_read ( & stats - > misc_stats . isr_count ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . max_cq_entries ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . ack_index_out_of_range ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . data_count_mismatch ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . fcpio_timeout ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . fcpio_aborted ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . sgl_invalid ) ,
( u64 ) atomic64_read (
& stats - > misc_stats . abts_cpwq_alloc_failures ) ,
( u64 ) atomic64_read (
& stats - > misc_stats . devrst_cpwq_alloc_failures ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . io_cpwq_alloc_failures ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . no_icmnd_itmf_cmpls ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . queue_fulls ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . rport_not_ready ) ,
( u64 ) atomic64_read ( & stats - > misc_stats . frame_errors ) ) ;
return len ;
}
2013-02-13 05:01:02 +04:00
/*
* fnic_trace_buf_init - Initialize fnic trace buffer logging facility
*
* Description :
* Initialize trace buffer data structure by allocating required memory and
* setting page_offset information for every trace entry by adding trace entry
* length to previous page_offset value .
*/
int fnic_trace_buf_init ( void )
{
unsigned long fnic_buf_head ;
int i ;
int err = 0 ;
trace_max_pages = fnic_trace_max_pages ;
fnic_max_trace_entries = ( trace_max_pages * PAGE_SIZE ) /
FNIC_ENTRY_SIZE_BYTES ;
fnic_trace_buf_p = ( unsigned long ) vmalloc ( ( trace_max_pages * PAGE_SIZE ) ) ;
if ( ! fnic_trace_buf_p ) {
printk ( KERN_ERR PFX " Failed to allocate memory "
" for fnic_trace_buf_p \n " ) ;
err = - ENOMEM ;
goto err_fnic_trace_buf_init ;
}
memset ( ( void * ) fnic_trace_buf_p , 0 , ( trace_max_pages * PAGE_SIZE ) ) ;
fnic_trace_entries . page_offset = vmalloc ( fnic_max_trace_entries *
sizeof ( unsigned long ) ) ;
if ( ! fnic_trace_entries . page_offset ) {
printk ( KERN_ERR PFX " Failed to allocate memory for "
" page_offset \n " ) ;
if ( fnic_trace_buf_p ) {
vfree ( ( void * ) fnic_trace_buf_p ) ;
fnic_trace_buf_p = 0 ;
}
err = - ENOMEM ;
goto err_fnic_trace_buf_init ;
}
memset ( ( void * ) fnic_trace_entries . page_offset , 0 ,
( fnic_max_trace_entries * sizeof ( unsigned long ) ) ) ;
fnic_trace_entries . wr_idx = fnic_trace_entries . rd_idx = 0 ;
fnic_buf_head = fnic_trace_buf_p ;
/*
* Set page_offset field of fnic_trace_entries struct by
* calculating memory location for every trace entry using
* length of each trace entry
*/
for ( i = 0 ; i < fnic_max_trace_entries ; i + + ) {
fnic_trace_entries . page_offset [ i ] = fnic_buf_head ;
fnic_buf_head + = FNIC_ENTRY_SIZE_BYTES ;
}
err = fnic_trace_debugfs_init ( ) ;
if ( err < 0 ) {
2014-04-18 23:28:19 +04:00
pr_err ( " fnic: Failed to initialize debugfs for tracing \n " ) ;
2013-02-13 05:01:02 +04:00
goto err_fnic_trace_debugfs_init ;
}
2014-04-18 23:28:19 +04:00
pr_info ( " fnic: Successfully Initialized Trace Buffer \n " ) ;
2013-02-13 05:01:02 +04:00
return err ;
err_fnic_trace_debugfs_init :
fnic_trace_free ( ) ;
err_fnic_trace_buf_init :
return err ;
}
/*
* fnic_trace_free - Free memory of fnic trace data structures .
*/
void fnic_trace_free ( void )
{
fnic_tracing_enabled = 0 ;
fnic_trace_debugfs_terminate ( ) ;
if ( fnic_trace_entries . page_offset ) {
vfree ( ( void * ) fnic_trace_entries . page_offset ) ;
fnic_trace_entries . page_offset = NULL ;
}
if ( fnic_trace_buf_p ) {
vfree ( ( void * ) fnic_trace_buf_p ) ;
fnic_trace_buf_p = 0 ;
}
printk ( KERN_INFO PFX " Successfully Freed Trace Buffer \n " ) ;
}
2014-04-18 23:28:19 +04:00
/*
* fnic_fc_ctlr_trace_buf_init -
* Initialize trace buffer to log fnic control frames
* Description :
* Initialize trace buffer data structure by allocating
* required memory for trace data as well as for Indexes .
* Frame size is 256 bytes and
* memory is allocated for 1024 entries of 256 bytes .
* Page_offset ( Index ) is set to the address of trace entry
* and page_offset is initialized by adding frame size
* to the previous page_offset entry .
*/
int fnic_fc_trace_init ( void )
{
unsigned long fc_trace_buf_head ;
int err = 0 ;
int i ;
fc_trace_max_entries = ( fnic_fc_trace_max_pages * PAGE_SIZE ) /
FC_TRC_SIZE_BYTES ;
fnic_fc_ctlr_trace_buf_p = ( unsigned long ) vmalloc (
fnic_fc_trace_max_pages * PAGE_SIZE ) ;
if ( ! fnic_fc_ctlr_trace_buf_p ) {
pr_err ( " fnic: Failed to allocate memory for "
" FC Control Trace Buf \n " ) ;
err = - ENOMEM ;
goto err_fnic_fc_ctlr_trace_buf_init ;
}
memset ( ( void * ) fnic_fc_ctlr_trace_buf_p , 0 ,
fnic_fc_trace_max_pages * PAGE_SIZE ) ;
/* Allocate memory for page offset */
fc_trace_entries . page_offset = vmalloc ( fc_trace_max_entries *
sizeof ( unsigned long ) ) ;
if ( ! fc_trace_entries . page_offset ) {
pr_err ( " fnic:Failed to allocate memory for page_offset \n " ) ;
if ( fnic_fc_ctlr_trace_buf_p ) {
pr_err ( " fnic: Freeing FC Control Trace Buf \n " ) ;
vfree ( ( void * ) fnic_fc_ctlr_trace_buf_p ) ;
fnic_fc_ctlr_trace_buf_p = 0 ;
}
err = - ENOMEM ;
goto err_fnic_fc_ctlr_trace_buf_init ;
}
memset ( ( void * ) fc_trace_entries . page_offset , 0 ,
( fc_trace_max_entries * sizeof ( unsigned long ) ) ) ;
fc_trace_entries . rd_idx = fc_trace_entries . wr_idx = 0 ;
fc_trace_buf_head = fnic_fc_ctlr_trace_buf_p ;
/*
* Set up fc_trace_entries . page_offset field with memory location
* for every trace entry
*/
for ( i = 0 ; i < fc_trace_max_entries ; i + + ) {
fc_trace_entries . page_offset [ i ] = fc_trace_buf_head ;
fc_trace_buf_head + = FC_TRC_SIZE_BYTES ;
}
err = fnic_fc_trace_debugfs_init ( ) ;
if ( err < 0 ) {
pr_err ( " fnic: Failed to initialize FC_CTLR tracing. \n " ) ;
goto err_fnic_fc_ctlr_trace_debugfs_init ;
}
pr_info ( " fnic: Successfully Initialized FC_CTLR Trace Buffer \n " ) ;
return err ;
err_fnic_fc_ctlr_trace_debugfs_init :
fnic_fc_trace_free ( ) ;
err_fnic_fc_ctlr_trace_buf_init :
return err ;
}
/*
* Fnic_fc_ctlr_trace_free - Free memory of fnic_fc_ctlr trace data structures .
*/
void fnic_fc_trace_free ( void )
{
fnic_fc_tracing_enabled = 0 ;
fnic_fc_trace_debugfs_terminate ( ) ;
if ( fc_trace_entries . page_offset ) {
vfree ( ( void * ) fc_trace_entries . page_offset ) ;
fc_trace_entries . page_offset = NULL ;
}
if ( fnic_fc_ctlr_trace_buf_p ) {
vfree ( ( void * ) fnic_fc_ctlr_trace_buf_p ) ;
fnic_fc_ctlr_trace_buf_p = 0 ;
}
pr_info ( " fnic:Successfully FC_CTLR Freed Trace Buffer \n " ) ;
}
/*
* fnic_fc_ctlr_set_trace_data :
* Maintain rd & wr idx accordingly and set data
* Passed parameters :
* host_no : host number accociated with fnic
* frame_type : send_frame , rece_frame or link event
* fc_frame : pointer to fc_frame
* frame_len : Length of the fc_frame
* Description :
* This routine will get next available wr_idx and
* copy all passed trace data to the buffer pointed by wr_idx
* and increment wr_idx . It will also make sure that we dont
* overwrite the entry which we are reading and also
* wrap around if we reach the maximum entries .
* Returned Value :
* It will return 0 for success or - 1 for failure
*/
int fnic_fc_trace_set_data ( u32 host_no , u8 frame_type ,
char * frame , u32 fc_trc_frame_len )
{
unsigned long flags ;
struct fc_trace_hdr * fc_buf ;
unsigned long eth_fcoe_hdr_len ;
char * fc_trace ;
if ( fnic_fc_tracing_enabled = = 0 )
return 0 ;
spin_lock_irqsave ( & fnic_fc_trace_lock , flags ) ;
if ( fnic_fc_trace_cleared = = 1 ) {
fc_trace_entries . rd_idx = fc_trace_entries . wr_idx = 0 ;
2014-08-29 18:37:33 +04:00
pr_info ( " fnic: Resetting the read idx \n " ) ;
2014-04-18 23:28:19 +04:00
memset ( ( void * ) fnic_fc_ctlr_trace_buf_p , 0 ,
fnic_fc_trace_max_pages * PAGE_SIZE ) ;
fnic_fc_trace_cleared = 0 ;
}
fc_buf = ( struct fc_trace_hdr * )
fc_trace_entries . page_offset [ fc_trace_entries . wr_idx ] ;
fc_trace_entries . wr_idx + + ;
if ( fc_trace_entries . wr_idx > = fc_trace_max_entries )
fc_trace_entries . wr_idx = 0 ;
if ( fc_trace_entries . wr_idx = = fc_trace_entries . rd_idx ) {
fc_trace_entries . rd_idx + + ;
if ( fc_trace_entries . rd_idx > = fc_trace_max_entries )
fc_trace_entries . rd_idx = 0 ;
}
fc_buf - > time_stamp = CURRENT_TIME ;
fc_buf - > host_no = host_no ;
fc_buf - > frame_type = frame_type ;
fc_trace = ( char * ) FC_TRACE_ADDRESS ( fc_buf ) ;
/* During the receive path, we do not have eth hdr as well as fcoe hdr
* at trace entry point so we will stuff 0xff just to make it generic .
*/
if ( frame_type = = FNIC_FC_RECV ) {
eth_fcoe_hdr_len = sizeof ( struct ethhdr ) +
sizeof ( struct fcoe_hdr ) ;
memset ( ( char * ) fc_trace , 0xff , eth_fcoe_hdr_len ) ;
/* Copy the rest of data frame */
memcpy ( ( char * ) ( fc_trace + eth_fcoe_hdr_len ) , ( void * ) frame ,
min_t ( u8 , fc_trc_frame_len ,
2014-11-10 23:54:33 +03:00
( u8 ) ( FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE
- eth_fcoe_hdr_len ) ) ) ;
2014-04-18 23:28:19 +04:00
} else {
memcpy ( ( char * ) fc_trace , ( void * ) frame ,
min_t ( u8 , fc_trc_frame_len ,
( u8 ) ( FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE ) ) ) ;
}
/* Store the actual received length */
fc_buf - > frame_len = fc_trc_frame_len ;
spin_unlock_irqrestore ( & fnic_fc_trace_lock , flags ) ;
return 0 ;
}
/*
* fnic_fc_ctlr_get_trace_data : Copy trace buffer to a memory file
* Passed parameter :
* @ fnic_dbgfs_t : pointer to debugfs trace buffer
* rdata_flag : 1 = > Unformated file
* 0 = > formated file
* Description :
* This routine will copy the trace data to memory file with
* proper formatting and also copy to another memory
* file without formatting for further procesing .
* Retrun Value :
* Number of bytes that were dumped into fnic_dbgfs_t
*/
int fnic_fc_trace_get_data ( fnic_dbgfs_t * fnic_dbgfs_prt , u8 rdata_flag )
{
int rd_idx , wr_idx ;
unsigned long flags ;
int len = 0 , j ;
struct fc_trace_hdr * tdata ;
char * fc_trace ;
spin_lock_irqsave ( & fnic_fc_trace_lock , flags ) ;
if ( fc_trace_entries . wr_idx = = fc_trace_entries . rd_idx ) {
spin_unlock_irqrestore ( & fnic_fc_trace_lock , flags ) ;
pr_info ( " fnic: Buffer is empty \n " ) ;
return 0 ;
}
rd_idx = fc_trace_entries . rd_idx ;
wr_idx = fc_trace_entries . wr_idx ;
if ( rdata_flag = = 0 ) {
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
( fnic_fc_trace_max_pages * PAGE_SIZE * 3 ) - len ,
" Time Stamp (UTC) \t \t "
" Host No: F Type: len: FCoE_FRAME: \n " ) ;
}
while ( rd_idx ! = wr_idx ) {
tdata = ( struct fc_trace_hdr * )
fc_trace_entries . page_offset [ rd_idx ] ;
if ( ! tdata ) {
pr_info ( " fnic: Rd data is NULL \n " ) ;
spin_unlock_irqrestore ( & fnic_fc_trace_lock , flags ) ;
return 0 ;
}
if ( rdata_flag = = 0 ) {
copy_and_format_trace_data ( tdata ,
fnic_dbgfs_prt , & len , rdata_flag ) ;
} else {
fc_trace = ( char * ) tdata ;
for ( j = 0 ; j < FC_TRC_SIZE_BYTES ; j + + ) {
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
( fnic_fc_trace_max_pages * PAGE_SIZE * 3 )
- len , " %02x " , fc_trace [ j ] & 0xff ) ;
} /* for loop */
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
( fnic_fc_trace_max_pages * PAGE_SIZE * 3 ) - len ,
" \n " ) ;
}
rd_idx + + ;
if ( rd_idx > ( fc_trace_max_entries - 1 ) )
rd_idx = 0 ;
}
spin_unlock_irqrestore ( & fnic_fc_trace_lock , flags ) ;
return len ;
}
/*
* copy_and_format_trace_data : Copy formatted data to char * buffer
* Passed Parameter :
* @ fc_trace_hdr_t : pointer to trace data
* @ fnic_dbgfs_t : pointer to debugfs trace buffer
* @ orig_len : pointer to len
* rdata_flag : 0 = > Formated file , 1 = > Unformated file
* Description :
* This routine will format and copy the passed trace data
* for formated file or unformated file accordingly .
*/
void copy_and_format_trace_data ( struct fc_trace_hdr * tdata ,
fnic_dbgfs_t * fnic_dbgfs_prt , int * orig_len ,
u8 rdata_flag )
{
struct tm tm ;
int j , i = 1 , len ;
char * fc_trace , * fmt ;
int ethhdr_len = sizeof ( struct ethhdr ) - 1 ;
int fcoehdr_len = sizeof ( struct fcoe_hdr ) ;
int fchdr_len = sizeof ( struct fc_frame_header ) ;
int max_size = fnic_fc_trace_max_pages * PAGE_SIZE * 3 ;
tdata - > frame_type = tdata - > frame_type & 0x7F ;
len = * orig_len ;
time_to_tm ( tdata - > time_stamp . tv_sec , 0 , & tm ) ;
fmt = " %02d:%02d:%04ld %02d:%02d:%02d.%09lu ns%8x %c%8x \t " ;
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
2014-05-03 04:46:31 +04:00
max_size - len ,
2014-04-18 23:28:19 +04:00
fmt ,
tm . tm_mon + 1 , tm . tm_mday , tm . tm_year + 1900 ,
tm . tm_hour , tm . tm_min , tm . tm_sec ,
tdata - > time_stamp . tv_nsec , tdata - > host_no ,
tdata - > frame_type , tdata - > frame_len ) ;
fc_trace = ( char * ) FC_TRACE_ADDRESS ( tdata ) ;
for ( j = 0 ; j < min_t ( u8 , tdata - > frame_len ,
( u8 ) ( FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE ) ) ; j + + ) {
if ( tdata - > frame_type = = FNIC_FC_LE ) {
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
max_size - len , " %c " , fc_trace [ j ] ) ;
} else {
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
max_size - len , " %02x " , fc_trace [ j ] & 0xff ) ;
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
max_size - len , " " ) ;
if ( j = = ethhdr_len | |
j = = ethhdr_len + fcoehdr_len | |
j = = ethhdr_len + fcoehdr_len + fchdr_len | |
( i > 3 & & j % fchdr_len = = 0 ) ) {
len + = snprintf ( fnic_dbgfs_prt - > buffer
2014-05-03 04:46:31 +04:00
+ len , max_size - len ,
2014-04-18 23:28:19 +04:00
" \n \t \t \t \t \t \t \t \t " ) ;
i + + ;
}
} /* end of else*/
} /* End of for loop*/
len + = snprintf ( fnic_dbgfs_prt - > buffer + len ,
max_size - len , " \n " ) ;
* orig_len = len ;
}