/* * 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)
{ unsignedlong fnic_buf_head; unsignedlong 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; unsignedlong flags; char str[KSYM_SYMBOL_LEN]; struct timespec64 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(unsignedlong) < 8) {
sprint_symbol(str, tbp->fnaddr.low);
jiffies_to_timespec64(tbp->timestamp.low, &val);
} else {
sprint_symbol(str, tbp->fnaddr.val);
jiffies_to_timespec64(tbp->timestamp.val, &val);
} /* * Dump trace buffer entry to memory file * and increment read index @rd_idx
*/
len += scnprintf(fnic_dbgfs_prt->buffer + len,
(trace_max_pages * PAGE_SIZE * 3) - len, "%16llu.%09lu %-50s %8x %8x %16llx %16llx " "%16llx %16llx %16llx\n", (u64)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; /* * Continue dumping trace buffer entries into * memory file till rd_idx reaches write index
*/ if (rd_idx == wr_idx) break;
}
} elseif (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(unsignedlong) < 8) {
sprint_symbol(str, tbp->fnaddr.low);
jiffies_to_timespec64(tbp->timestamp.low, &val);
} else {
sprint_symbol(str, tbp->fnaddr.val);
jiffies_to_timespec64(tbp->timestamp.val, &val);
} /* * Dump trace buffer entry to memory file * and increment read index @rd_idx
*/
len += scnprintf(fnic_dbgfs_prt->buffer + len,
(trace_max_pages * PAGE_SIZE * 3) - len, "%16llu.%09lu %-50s %8x %8x %16llx %16llx " "%16llx %16llx %16llx\n", (u64)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 dumping 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;
}
/* * 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 timespec64 val1, val2; int i = 0;
len += scnprintf(debug->debug_buffer + len, buf_size - len, "Current time : [%lld:%ld]\n" "Last stats reset time: [%lld:%09ld]\n" "Last stats read time: [%lld:%ld]\n" "delta since last reset: [%lld:%ld]\n" "delta since last read: [%lld:%ld]\n",
(s64)val1.tv_sec, val1.tv_nsec,
(s64)stats->stats_timestamps.last_reset_time.tv_sec,
stats->stats_timestamps.last_reset_time.tv_nsec,
(s64)stats->stats_timestamps.last_read_time.tv_sec,
stats->stats_timestamps.last_read_time.tv_nsec,
(s64)timespec64_sub(val1, stats->stats_timestamps.last_reset_time).tv_sec,
timespec64_sub(val1, stats->stats_timestamps.last_reset_time).tv_nsec,
(s64)timespec64_sub(val1, stats->stats_timestamps.last_read_time).tv_sec,
timespec64_sub(val1, stats->stats_timestamps.last_read_time).tv_nsec);
stats->stats_timestamps.last_read_time = val1;
len += scnprintf(debug->debug_buffer + len, buf_size - len, "------------------------------------------\n" "\t\tIO Statistics\n" "------------------------------------------\n");
len += scnprintf(debug->debug_buffer + len, buf_size - len, "Number of Active IOs: %lld\nMaximum Active IOs: %lld\n" "Number of IOs: %lld\nNumber of IO Completions: %lld\n" "Number of IO Failures: %lld\nNumber 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"
len += scnprintf(debug->debug_buffer + len, buf_size - len, "------------------------------------------\n" "\t\tIO Queues and cumulative IOs\n" "------------------------------------------\n");
for (i = 0; i < FNIC_MQ_MAX_QUEUES; i++) {
len += scnprintf(debug->debug_buffer + len, buf_size - len, "Q:%d -> %lld\n", i, (u64)atomic64_read(&stats->io_stats.ios[i]));
}
len += scnprintf(debug->debug_buffer + len, buf_size - len, "\nCurrent Max IO time : %lld\n",
(u64)atomic64_read(&stats->io_stats.current_max_io_time));
len += scnprintf(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));
/* * 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)
{ unsignedlong fnic_buf_head; int i; int err = 0;
/* * 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;
}
fnic_trace_debugfs_init();
pr_info("fnic: Successfully Initialized Trace Buffer\n"); return err;
/* * 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)
{ unsignedlong 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 =
(unsignedlong)vcalloc(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;
}
/* Allocate memory for page offset */
fc_trace_entries.page_offset =
vcalloc(fc_trace_max_entries, sizeof(unsignedlong)); 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;
}
/* * 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;
}
fnic_fc_trace_debugfs_init();
pr_info("fnic: Successfully Initialized FC_CTLR Trace Buffer\n"); return err;
/* * fnic_fc_ctlr_set_trace_data: * Maintain rd & wr idx accordingly and set data * Passed parameters: * host_no: host number associated 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)
{ unsignedlong flags; struct fc_trace_hdr *fc_buf; unsignedlong eth_fcoe_hdr_len; char *fc_trace;
/* 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,
(u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE
- eth_fcoe_hdr_len)));
} 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;
/* * 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 => Unformatted file * 0 => formatted 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 processing. * Return 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; unsignedlong 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 += scnprintf(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 += scnprintf(fnic_dbgfs_prt->buffer + len,
(fnic_fc_trace_max_pages * PAGE_SIZE * 3)
- len, "%02x", fc_trace[j] & 0xff);
} /* for loop */
len += scnprintf(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;
}
/* * 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 => Formatted file, 1 => Unformatted file * Description: * This routine will format and copy the passed trace data * for formatted file or unformatted 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)
{ int j, i = 1, len; 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; char *fc_trace;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.