Commit | Line | Data |
---|---|---|
4d7007b4 HP |
1 | /* |
2 | * Copyright 2012 Cisco Systems, Inc. All rights reserved. | |
3 | * | |
4 | * This program is free software; you may redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; version 2 of the License. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
9 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
10 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
11 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
12 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
15 | * SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/mempool.h> | |
20 | #include <linux/errno.h> | |
21 | #include <linux/spinlock.h> | |
22 | #include <linux/kallsyms.h> | |
abb14148 | 23 | #include <linux/time.h> |
d6472302 | 24 | #include <linux/vmalloc.h> |
4d7007b4 HP |
25 | #include "fnic_io.h" |
26 | #include "fnic.h" | |
27 | ||
28 | unsigned int trace_max_pages; | |
29 | static int fnic_max_trace_entries; | |
30 | ||
31 | static unsigned long fnic_trace_buf_p; | |
32 | static DEFINE_SPINLOCK(fnic_trace_lock); | |
33 | ||
34 | static fnic_trace_dbg_t fnic_trace_entries; | |
35 | int fnic_tracing_enabled = 1; | |
36 | ||
abb14148 HS |
37 | /* static char *fnic_fc_ctlr_trace_buf_p; */ |
38 | ||
39 | static int fc_trace_max_entries; | |
40 | static unsigned long fnic_fc_ctlr_trace_buf_p; | |
41 | static fnic_trace_dbg_t fc_trace_entries; | |
42 | int fnic_fc_tracing_enabled = 1; | |
43 | int fnic_fc_trace_cleared = 1; | |
44 | static DEFINE_SPINLOCK(fnic_fc_trace_lock); | |
45 | ||
46 | ||
4d7007b4 HP |
47 | /* |
48 | * fnic_trace_get_buf - Give buffer pointer to user to fill up trace information | |
49 | * | |
50 | * Description: | |
51 | * This routine gets next available trace buffer entry location @wr_idx | |
52 | * from allocated trace buffer pages and give that memory location | |
53 | * to user to store the trace information. | |
54 | * | |
55 | * Return Value: | |
56 | * This routine returns pointer to next available trace entry | |
57 | * @fnic_buf_head for user to fill trace information. | |
58 | */ | |
59 | fnic_trace_data_t *fnic_trace_get_buf(void) | |
60 | { | |
61 | unsigned long fnic_buf_head; | |
62 | unsigned long flags; | |
63 | ||
64 | spin_lock_irqsave(&fnic_trace_lock, flags); | |
65 | ||
66 | /* | |
67 | * Get next available memory location for writing trace information | |
68 | * at @wr_idx and increment @wr_idx | |
69 | */ | |
70 | fnic_buf_head = | |
71 | fnic_trace_entries.page_offset[fnic_trace_entries.wr_idx]; | |
72 | fnic_trace_entries.wr_idx++; | |
73 | ||
74 | /* | |
75 | * Verify if trace buffer is full then change wd_idx to | |
76 | * start from zero | |
77 | */ | |
78 | if (fnic_trace_entries.wr_idx >= fnic_max_trace_entries) | |
79 | fnic_trace_entries.wr_idx = 0; | |
80 | ||
81 | /* | |
82 | * Verify if write index @wr_idx and read index @rd_idx are same then | |
83 | * increment @rd_idx to move to next entry in trace buffer | |
84 | */ | |
85 | if (fnic_trace_entries.wr_idx == fnic_trace_entries.rd_idx) { | |
86 | fnic_trace_entries.rd_idx++; | |
87 | if (fnic_trace_entries.rd_idx >= fnic_max_trace_entries) | |
88 | fnic_trace_entries.rd_idx = 0; | |
89 | } | |
90 | spin_unlock_irqrestore(&fnic_trace_lock, flags); | |
91 | return (fnic_trace_data_t *)fnic_buf_head; | |
92 | } | |
93 | ||
94 | /* | |
95 | * fnic_get_trace_data - Copy trace buffer to a memory file | |
96 | * @fnic_dbgfs_t: pointer to debugfs trace buffer | |
97 | * | |
98 | * Description: | |
99 | * This routine gathers the fnic trace debugfs data from the fnic_trace_data_t | |
100 | * buffer and dumps it to fnic_dbgfs_t. It will start at the rd_idx entry in | |
101 | * the log and process the log until the end of the buffer. Then it will gather | |
102 | * from the beginning of the log and process until the current entry @wr_idx. | |
103 | * | |
104 | * Return Value: | |
105 | * This routine returns the amount of bytes that were dumped into fnic_dbgfs_t | |
106 | */ | |
107 | int fnic_get_trace_data(fnic_dbgfs_t *fnic_dbgfs_prt) | |
108 | { | |
109 | int rd_idx; | |
110 | int wr_idx; | |
111 | int len = 0; | |
112 | unsigned long flags; | |
113 | char str[KSYM_SYMBOL_LEN]; | |
114 | struct timespec val; | |
115 | fnic_trace_data_t *tbp; | |
116 | ||
117 | spin_lock_irqsave(&fnic_trace_lock, flags); | |
118 | rd_idx = fnic_trace_entries.rd_idx; | |
119 | wr_idx = fnic_trace_entries.wr_idx; | |
120 | if (wr_idx < rd_idx) { | |
121 | while (1) { | |
122 | /* Start from read index @rd_idx */ | |
123 | tbp = (fnic_trace_data_t *) | |
124 | fnic_trace_entries.page_offset[rd_idx]; | |
125 | if (!tbp) { | |
126 | spin_unlock_irqrestore(&fnic_trace_lock, flags); | |
127 | return 0; | |
128 | } | |
129 | /* Convert function pointer to function name */ | |
130 | if (sizeof(unsigned long) < 8) { | |
131 | sprint_symbol(str, tbp->fnaddr.low); | |
132 | jiffies_to_timespec(tbp->timestamp.low, &val); | |
133 | } else { | |
134 | sprint_symbol(str, tbp->fnaddr.val); | |
135 | jiffies_to_timespec(tbp->timestamp.val, &val); | |
136 | } | |
137 | /* | |
138 | * Dump trace buffer entry to memory file | |
139 | * and increment read index @rd_idx | |
140 | */ | |
141 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
142 | (trace_max_pages * PAGE_SIZE * 3) - len, | |
143 | "%16lu.%16lu %-50s %8x %8x %16llx %16llx " | |
144 | "%16llx %16llx %16llx\n", val.tv_sec, | |
145 | val.tv_nsec, str, tbp->host_no, tbp->tag, | |
146 | tbp->data[0], tbp->data[1], tbp->data[2], | |
147 | tbp->data[3], tbp->data[4]); | |
148 | rd_idx++; | |
149 | /* | |
150 | * If rd_idx is reached to maximum trace entries | |
151 | * then move rd_idx to zero | |
152 | */ | |
153 | if (rd_idx > (fnic_max_trace_entries-1)) | |
154 | rd_idx = 0; | |
155 | /* | |
156 | * Continure dumpping trace buffer entries into | |
157 | * memory file till rd_idx reaches write index | |
158 | */ | |
159 | if (rd_idx == wr_idx) | |
160 | break; | |
161 | } | |
162 | } else if (wr_idx > rd_idx) { | |
163 | while (1) { | |
164 | /* Start from read index @rd_idx */ | |
165 | tbp = (fnic_trace_data_t *) | |
166 | fnic_trace_entries.page_offset[rd_idx]; | |
167 | if (!tbp) { | |
168 | spin_unlock_irqrestore(&fnic_trace_lock, flags); | |
169 | return 0; | |
170 | } | |
171 | /* Convert function pointer to function name */ | |
172 | if (sizeof(unsigned long) < 8) { | |
173 | sprint_symbol(str, tbp->fnaddr.low); | |
174 | jiffies_to_timespec(tbp->timestamp.low, &val); | |
175 | } else { | |
176 | sprint_symbol(str, tbp->fnaddr.val); | |
177 | jiffies_to_timespec(tbp->timestamp.val, &val); | |
178 | } | |
179 | /* | |
180 | * Dump trace buffer entry to memory file | |
181 | * and increment read index @rd_idx | |
182 | */ | |
183 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
184 | (trace_max_pages * PAGE_SIZE * 3) - len, | |
185 | "%16lu.%16lu %-50s %8x %8x %16llx %16llx " | |
186 | "%16llx %16llx %16llx\n", val.tv_sec, | |
187 | val.tv_nsec, str, tbp->host_no, tbp->tag, | |
188 | tbp->data[0], tbp->data[1], tbp->data[2], | |
189 | tbp->data[3], tbp->data[4]); | |
190 | rd_idx++; | |
191 | /* | |
192 | * Continue dumpping trace buffer entries into | |
193 | * memory file till rd_idx reaches write index | |
194 | */ | |
195 | if (rd_idx == wr_idx) | |
196 | break; | |
197 | } | |
198 | } | |
199 | spin_unlock_irqrestore(&fnic_trace_lock, flags); | |
200 | return len; | |
201 | } | |
202 | ||
67125b02 HP |
203 | /* |
204 | * fnic_get_stats_data - Copy fnic stats buffer to a memory file | |
205 | * @fnic_dbgfs_t: pointer to debugfs fnic stats buffer | |
206 | * | |
207 | * Description: | |
208 | * This routine gathers the fnic stats debugfs data from the fnic_stats struct | |
209 | * and dumps it to stats_debug_info. | |
210 | * | |
211 | * Return Value: | |
212 | * This routine returns the amount of bytes that were dumped into | |
213 | * stats_debug_info | |
214 | */ | |
215 | int fnic_get_stats_data(struct stats_debug_info *debug, | |
216 | struct fnic_stats *stats) | |
217 | { | |
218 | int len = 0; | |
219 | int buf_size = debug->buf_size; | |
220 | struct timespec val1, val2; | |
221 | ||
222 | len = snprintf(debug->debug_buffer + len, buf_size - len, | |
223 | "------------------------------------------\n" | |
224 | "\t\tIO Statistics\n" | |
225 | "------------------------------------------\n"); | |
226 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
227 | "Number of Active IOs: %lld\nMaximum Active IOs: %lld\n" | |
228 | "Number of IOs: %lld\nNumber of IO Completions: %lld\n" | |
229 | "Number of IO Failures: %lld\nNumber of IO NOT Found: %lld\n" | |
230 | "Number of Memory alloc Failures: %lld\n" | |
231 | "Number of IOREQ Null: %lld\n" | |
445d2960 SK |
232 | "Number of SCSI cmd pointer Null: %lld\n" |
233 | ||
234 | "\nIO completion times: \n" | |
235 | " < 10 ms : %lld\n" | |
236 | " 10 ms - 100 ms : %lld\n" | |
237 | " 100 ms - 500 ms : %lld\n" | |
238 | " 500 ms - 5 sec: %lld\n" | |
239 | " 5 sec - 10 sec: %lld\n" | |
240 | " 10 sec - 30 sec: %lld\n" | |
241 | " > 30 sec: %lld\n", | |
67125b02 HP |
242 | (u64)atomic64_read(&stats->io_stats.active_ios), |
243 | (u64)atomic64_read(&stats->io_stats.max_active_ios), | |
244 | (u64)atomic64_read(&stats->io_stats.num_ios), | |
245 | (u64)atomic64_read(&stats->io_stats.io_completions), | |
246 | (u64)atomic64_read(&stats->io_stats.io_failures), | |
247 | (u64)atomic64_read(&stats->io_stats.io_not_found), | |
248 | (u64)atomic64_read(&stats->io_stats.alloc_failures), | |
249 | (u64)atomic64_read(&stats->io_stats.ioreq_null), | |
445d2960 SK |
250 | (u64)atomic64_read(&stats->io_stats.sc_null), |
251 | (u64)atomic64_read(&stats->io_stats.io_btw_0_to_10_msec), | |
252 | (u64)atomic64_read(&stats->io_stats.io_btw_10_to_100_msec), | |
253 | (u64)atomic64_read(&stats->io_stats.io_btw_100_to_500_msec), | |
254 | (u64)atomic64_read(&stats->io_stats.io_btw_500_to_5000_msec), | |
255 | (u64)atomic64_read(&stats->io_stats.io_btw_5000_to_10000_msec), | |
256 | (u64)atomic64_read(&stats->io_stats.io_btw_10000_to_30000_msec), | |
257 | (u64)atomic64_read(&stats->io_stats.io_greater_than_30000_msec)); | |
258 | ||
259 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
260 | "\nCurrent Max IO time : %lld\n", | |
261 | (u64)atomic64_read(&stats->io_stats.current_max_io_time)); | |
67125b02 HP |
262 | |
263 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
264 | "\n------------------------------------------\n" | |
265 | "\t\tAbort Statistics\n" | |
266 | "------------------------------------------\n"); | |
445d2960 | 267 | |
67125b02 HP |
268 | len += snprintf(debug->debug_buffer + len, buf_size - len, |
269 | "Number of Aborts: %lld\n" | |
270 | "Number of Abort Failures: %lld\n" | |
271 | "Number of Abort Driver Timeouts: %lld\n" | |
272 | "Number of Abort FW Timeouts: %lld\n" | |
445d2960 SK |
273 | "Number of Abort IO NOT Found: %lld\n" |
274 | ||
275 | "Abord issued times: \n" | |
276 | " < 6 sec : %lld\n" | |
277 | " 6 sec - 20 sec : %lld\n" | |
278 | " 20 sec - 30 sec : %lld\n" | |
279 | " 30 sec - 40 sec : %lld\n" | |
280 | " 40 sec - 50 sec : %lld\n" | |
281 | " 50 sec - 60 sec : %lld\n" | |
282 | " > 60 sec: %lld\n", | |
283 | ||
67125b02 HP |
284 | (u64)atomic64_read(&stats->abts_stats.aborts), |
285 | (u64)atomic64_read(&stats->abts_stats.abort_failures), | |
286 | (u64)atomic64_read(&stats->abts_stats.abort_drv_timeouts), | |
287 | (u64)atomic64_read(&stats->abts_stats.abort_fw_timeouts), | |
445d2960 SK |
288 | (u64)atomic64_read(&stats->abts_stats.abort_io_not_found), |
289 | (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_0_to_6_sec), | |
290 | (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_6_to_20_sec), | |
291 | (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_20_to_30_sec), | |
292 | (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_30_to_40_sec), | |
293 | (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_40_to_50_sec), | |
294 | (u64)atomic64_read(&stats->abts_stats.abort_issued_btw_50_to_60_sec), | |
295 | (u64)atomic64_read(&stats->abts_stats.abort_issued_greater_than_60_sec)); | |
67125b02 HP |
296 | |
297 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
298 | "\n------------------------------------------\n" | |
299 | "\t\tTerminate Statistics\n" | |
300 | "------------------------------------------\n"); | |
445d2960 | 301 | |
67125b02 HP |
302 | len += snprintf(debug->debug_buffer + len, buf_size - len, |
303 | "Number of Terminates: %lld\n" | |
304 | "Maximum Terminates: %lld\n" | |
305 | "Number of Terminate Driver Timeouts: %lld\n" | |
306 | "Number of Terminate FW Timeouts: %lld\n" | |
307 | "Number of Terminate IO NOT Found: %lld\n" | |
308 | "Number of Terminate Failures: %lld\n", | |
309 | (u64)atomic64_read(&stats->term_stats.terminates), | |
310 | (u64)atomic64_read(&stats->term_stats.max_terminates), | |
311 | (u64)atomic64_read(&stats->term_stats.terminate_drv_timeouts), | |
312 | (u64)atomic64_read(&stats->term_stats.terminate_fw_timeouts), | |
313 | (u64)atomic64_read(&stats->term_stats.terminate_io_not_found), | |
314 | (u64)atomic64_read(&stats->term_stats.terminate_failures)); | |
315 | ||
316 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
317 | "\n------------------------------------------\n" | |
318 | "\t\tReset Statistics\n" | |
319 | "------------------------------------------\n"); | |
320 | ||
321 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
322 | "Number of Device Resets: %lld\n" | |
323 | "Number of Device Reset Failures: %lld\n" | |
324 | "Number of Device Reset Aborts: %lld\n" | |
325 | "Number of Device Reset Timeouts: %lld\n" | |
326 | "Number of Device Reset Terminates: %lld\n" | |
327 | "Number of FW Resets: %lld\n" | |
328 | "Number of FW Reset Completions: %lld\n" | |
329 | "Number of FW Reset Failures: %lld\n" | |
330 | "Number of Fnic Reset: %lld\n" | |
331 | "Number of Fnic Reset Completions: %lld\n" | |
332 | "Number of Fnic Reset Failures: %lld\n", | |
333 | (u64)atomic64_read(&stats->reset_stats.device_resets), | |
334 | (u64)atomic64_read(&stats->reset_stats.device_reset_failures), | |
335 | (u64)atomic64_read(&stats->reset_stats.device_reset_aborts), | |
336 | (u64)atomic64_read(&stats->reset_stats.device_reset_timeouts), | |
337 | (u64)atomic64_read( | |
338 | &stats->reset_stats.device_reset_terminates), | |
339 | (u64)atomic64_read(&stats->reset_stats.fw_resets), | |
340 | (u64)atomic64_read(&stats->reset_stats.fw_reset_completions), | |
341 | (u64)atomic64_read(&stats->reset_stats.fw_reset_failures), | |
342 | (u64)atomic64_read(&stats->reset_stats.fnic_resets), | |
343 | (u64)atomic64_read( | |
344 | &stats->reset_stats.fnic_reset_completions), | |
345 | (u64)atomic64_read(&stats->reset_stats.fnic_reset_failures)); | |
346 | ||
347 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
348 | "\n------------------------------------------\n" | |
349 | "\t\tFirmware Statistics\n" | |
350 | "------------------------------------------\n"); | |
351 | ||
352 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
353 | "Number of Active FW Requests %lld\n" | |
354 | "Maximum FW Requests: %lld\n" | |
355 | "Number of FW out of resources: %lld\n" | |
356 | "Number of FW IO errors: %lld\n", | |
357 | (u64)atomic64_read(&stats->fw_stats.active_fw_reqs), | |
358 | (u64)atomic64_read(&stats->fw_stats.max_fw_reqs), | |
359 | (u64)atomic64_read(&stats->fw_stats.fw_out_of_resources), | |
360 | (u64)atomic64_read(&stats->fw_stats.io_fw_errs)); | |
361 | ||
362 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
363 | "\n------------------------------------------\n" | |
364 | "\t\tVlan Discovery Statistics\n" | |
365 | "------------------------------------------\n"); | |
366 | ||
367 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
368 | "Number of Vlan Discovery Requests Sent %lld\n" | |
369 | "Vlan Response Received with no FCF VLAN ID: %lld\n" | |
370 | "No solicitations recvd after vlan set, expiry count: %lld\n" | |
371 | "Flogi rejects count: %lld\n", | |
372 | (u64)atomic64_read(&stats->vlan_stats.vlan_disc_reqs), | |
373 | (u64)atomic64_read(&stats->vlan_stats.resp_withno_vlanID), | |
374 | (u64)atomic64_read(&stats->vlan_stats.sol_expiry_count), | |
375 | (u64)atomic64_read(&stats->vlan_stats.flogi_rejects)); | |
376 | ||
377 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
378 | "\n------------------------------------------\n" | |
379 | "\t\tOther Important Statistics\n" | |
380 | "------------------------------------------\n"); | |
381 | ||
382 | jiffies_to_timespec(stats->misc_stats.last_isr_time, &val1); | |
383 | jiffies_to_timespec(stats->misc_stats.last_ack_time, &val2); | |
384 | ||
385 | len += snprintf(debug->debug_buffer + len, buf_size - len, | |
386 | "Last ISR time: %llu (%8lu.%8lu)\n" | |
387 | "Last ACK time: %llu (%8lu.%8lu)\n" | |
388 | "Number of ISRs: %lld\n" | |
389 | "Maximum CQ Entries: %lld\n" | |
390 | "Number of ACK index out of range: %lld\n" | |
391 | "Number of data count mismatch: %lld\n" | |
392 | "Number of FCPIO Timeouts: %lld\n" | |
393 | "Number of FCPIO Aborted: %lld\n" | |
394 | "Number of SGL Invalid: %lld\n" | |
395 | "Number of Copy WQ Alloc Failures for ABTs: %lld\n" | |
396 | "Number of Copy WQ Alloc Failures for Device Reset: %lld\n" | |
397 | "Number of Copy WQ Alloc Failures for IOs: %lld\n" | |
398 | "Number of no icmnd itmf Completions: %lld\n" | |
39fcbbc0 | 399 | "Number of Check Conditions encountered: %lld\n" |
67125b02 HP |
400 | "Number of QUEUE Fulls: %lld\n" |
401 | "Number of rport not ready: %lld\n" | |
402 | "Number of receive frame errors: %lld\n", | |
403 | (u64)stats->misc_stats.last_isr_time, | |
404 | val1.tv_sec, val1.tv_nsec, | |
405 | (u64)stats->misc_stats.last_ack_time, | |
406 | val2.tv_sec, val2.tv_nsec, | |
407 | (u64)atomic64_read(&stats->misc_stats.isr_count), | |
408 | (u64)atomic64_read(&stats->misc_stats.max_cq_entries), | |
409 | (u64)atomic64_read(&stats->misc_stats.ack_index_out_of_range), | |
410 | (u64)atomic64_read(&stats->misc_stats.data_count_mismatch), | |
411 | (u64)atomic64_read(&stats->misc_stats.fcpio_timeout), | |
412 | (u64)atomic64_read(&stats->misc_stats.fcpio_aborted), | |
413 | (u64)atomic64_read(&stats->misc_stats.sgl_invalid), | |
414 | (u64)atomic64_read( | |
415 | &stats->misc_stats.abts_cpwq_alloc_failures), | |
416 | (u64)atomic64_read( | |
417 | &stats->misc_stats.devrst_cpwq_alloc_failures), | |
418 | (u64)atomic64_read(&stats->misc_stats.io_cpwq_alloc_failures), | |
419 | (u64)atomic64_read(&stats->misc_stats.no_icmnd_itmf_cmpls), | |
39fcbbc0 | 420 | (u64)atomic64_read(&stats->misc_stats.check_condition), |
67125b02 HP |
421 | (u64)atomic64_read(&stats->misc_stats.queue_fulls), |
422 | (u64)atomic64_read(&stats->misc_stats.rport_not_ready), | |
423 | (u64)atomic64_read(&stats->misc_stats.frame_errors)); | |
424 | ||
425 | return len; | |
426 | ||
427 | } | |
428 | ||
4d7007b4 HP |
429 | /* |
430 | * fnic_trace_buf_init - Initialize fnic trace buffer logging facility | |
431 | * | |
432 | * Description: | |
433 | * Initialize trace buffer data structure by allocating required memory and | |
434 | * setting page_offset information for every trace entry by adding trace entry | |
435 | * length to previous page_offset value. | |
436 | */ | |
437 | int fnic_trace_buf_init(void) | |
438 | { | |
439 | unsigned long fnic_buf_head; | |
440 | int i; | |
441 | int err = 0; | |
442 | ||
443 | trace_max_pages = fnic_trace_max_pages; | |
444 | fnic_max_trace_entries = (trace_max_pages * PAGE_SIZE)/ | |
445 | FNIC_ENTRY_SIZE_BYTES; | |
446 | ||
447 | fnic_trace_buf_p = (unsigned long)vmalloc((trace_max_pages * PAGE_SIZE)); | |
448 | if (!fnic_trace_buf_p) { | |
449 | printk(KERN_ERR PFX "Failed to allocate memory " | |
450 | "for fnic_trace_buf_p\n"); | |
451 | err = -ENOMEM; | |
452 | goto err_fnic_trace_buf_init; | |
453 | } | |
454 | memset((void *)fnic_trace_buf_p, 0, (trace_max_pages * PAGE_SIZE)); | |
455 | ||
456 | fnic_trace_entries.page_offset = vmalloc(fnic_max_trace_entries * | |
457 | sizeof(unsigned long)); | |
458 | if (!fnic_trace_entries.page_offset) { | |
459 | printk(KERN_ERR PFX "Failed to allocate memory for" | |
460 | " page_offset\n"); | |
461 | if (fnic_trace_buf_p) { | |
462 | vfree((void *)fnic_trace_buf_p); | |
463 | fnic_trace_buf_p = 0; | |
464 | } | |
465 | err = -ENOMEM; | |
466 | goto err_fnic_trace_buf_init; | |
467 | } | |
468 | memset((void *)fnic_trace_entries.page_offset, 0, | |
469 | (fnic_max_trace_entries * sizeof(unsigned long))); | |
470 | fnic_trace_entries.wr_idx = fnic_trace_entries.rd_idx = 0; | |
471 | fnic_buf_head = fnic_trace_buf_p; | |
472 | ||
473 | /* | |
474 | * Set page_offset field of fnic_trace_entries struct by | |
475 | * calculating memory location for every trace entry using | |
476 | * length of each trace entry | |
477 | */ | |
478 | for (i = 0; i < fnic_max_trace_entries; i++) { | |
479 | fnic_trace_entries.page_offset[i] = fnic_buf_head; | |
480 | fnic_buf_head += FNIC_ENTRY_SIZE_BYTES; | |
481 | } | |
482 | err = fnic_trace_debugfs_init(); | |
483 | if (err < 0) { | |
abb14148 | 484 | pr_err("fnic: Failed to initialize debugfs for tracing\n"); |
4d7007b4 HP |
485 | goto err_fnic_trace_debugfs_init; |
486 | } | |
abb14148 | 487 | pr_info("fnic: Successfully Initialized Trace Buffer\n"); |
4d7007b4 HP |
488 | return err; |
489 | err_fnic_trace_debugfs_init: | |
490 | fnic_trace_free(); | |
491 | err_fnic_trace_buf_init: | |
492 | return err; | |
493 | } | |
494 | ||
495 | /* | |
496 | * fnic_trace_free - Free memory of fnic trace data structures. | |
497 | */ | |
498 | void fnic_trace_free(void) | |
499 | { | |
500 | fnic_tracing_enabled = 0; | |
501 | fnic_trace_debugfs_terminate(); | |
502 | if (fnic_trace_entries.page_offset) { | |
503 | vfree((void *)fnic_trace_entries.page_offset); | |
504 | fnic_trace_entries.page_offset = NULL; | |
505 | } | |
506 | if (fnic_trace_buf_p) { | |
507 | vfree((void *)fnic_trace_buf_p); | |
508 | fnic_trace_buf_p = 0; | |
509 | } | |
510 | printk(KERN_INFO PFX "Successfully Freed Trace Buffer\n"); | |
511 | } | |
abb14148 HS |
512 | |
513 | /* | |
514 | * fnic_fc_ctlr_trace_buf_init - | |
515 | * Initialize trace buffer to log fnic control frames | |
516 | * Description: | |
517 | * Initialize trace buffer data structure by allocating | |
518 | * required memory for trace data as well as for Indexes. | |
519 | * Frame size is 256 bytes and | |
520 | * memory is allocated for 1024 entries of 256 bytes. | |
521 | * Page_offset(Index) is set to the address of trace entry | |
522 | * and page_offset is initialized by adding frame size | |
523 | * to the previous page_offset entry. | |
524 | */ | |
525 | ||
526 | int fnic_fc_trace_init(void) | |
527 | { | |
528 | unsigned long fc_trace_buf_head; | |
529 | int err = 0; | |
530 | int i; | |
531 | ||
532 | fc_trace_max_entries = (fnic_fc_trace_max_pages * PAGE_SIZE)/ | |
533 | FC_TRC_SIZE_BYTES; | |
534 | fnic_fc_ctlr_trace_buf_p = (unsigned long)vmalloc( | |
535 | fnic_fc_trace_max_pages * PAGE_SIZE); | |
536 | if (!fnic_fc_ctlr_trace_buf_p) { | |
537 | pr_err("fnic: Failed to allocate memory for " | |
538 | "FC Control Trace Buf\n"); | |
539 | err = -ENOMEM; | |
540 | goto err_fnic_fc_ctlr_trace_buf_init; | |
541 | } | |
542 | ||
543 | memset((void *)fnic_fc_ctlr_trace_buf_p, 0, | |
544 | fnic_fc_trace_max_pages * PAGE_SIZE); | |
545 | ||
546 | /* Allocate memory for page offset */ | |
547 | fc_trace_entries.page_offset = vmalloc(fc_trace_max_entries * | |
548 | sizeof(unsigned long)); | |
549 | if (!fc_trace_entries.page_offset) { | |
550 | pr_err("fnic:Failed to allocate memory for page_offset\n"); | |
551 | if (fnic_fc_ctlr_trace_buf_p) { | |
552 | pr_err("fnic: Freeing FC Control Trace Buf\n"); | |
553 | vfree((void *)fnic_fc_ctlr_trace_buf_p); | |
554 | fnic_fc_ctlr_trace_buf_p = 0; | |
555 | } | |
556 | err = -ENOMEM; | |
557 | goto err_fnic_fc_ctlr_trace_buf_init; | |
558 | } | |
559 | memset((void *)fc_trace_entries.page_offset, 0, | |
560 | (fc_trace_max_entries * sizeof(unsigned long))); | |
561 | ||
562 | fc_trace_entries.rd_idx = fc_trace_entries.wr_idx = 0; | |
563 | fc_trace_buf_head = fnic_fc_ctlr_trace_buf_p; | |
564 | ||
565 | /* | |
566 | * Set up fc_trace_entries.page_offset field with memory location | |
567 | * for every trace entry | |
568 | */ | |
569 | for (i = 0; i < fc_trace_max_entries; i++) { | |
570 | fc_trace_entries.page_offset[i] = fc_trace_buf_head; | |
571 | fc_trace_buf_head += FC_TRC_SIZE_BYTES; | |
572 | } | |
573 | err = fnic_fc_trace_debugfs_init(); | |
574 | if (err < 0) { | |
575 | pr_err("fnic: Failed to initialize FC_CTLR tracing.\n"); | |
576 | goto err_fnic_fc_ctlr_trace_debugfs_init; | |
577 | } | |
578 | pr_info("fnic: Successfully Initialized FC_CTLR Trace Buffer\n"); | |
579 | return err; | |
580 | ||
581 | err_fnic_fc_ctlr_trace_debugfs_init: | |
582 | fnic_fc_trace_free(); | |
583 | err_fnic_fc_ctlr_trace_buf_init: | |
584 | return err; | |
585 | } | |
586 | ||
587 | /* | |
588 | * Fnic_fc_ctlr_trace_free - Free memory of fnic_fc_ctlr trace data structures. | |
589 | */ | |
590 | void fnic_fc_trace_free(void) | |
591 | { | |
592 | fnic_fc_tracing_enabled = 0; | |
593 | fnic_fc_trace_debugfs_terminate(); | |
594 | if (fc_trace_entries.page_offset) { | |
595 | vfree((void *)fc_trace_entries.page_offset); | |
596 | fc_trace_entries.page_offset = NULL; | |
597 | } | |
598 | if (fnic_fc_ctlr_trace_buf_p) { | |
599 | vfree((void *)fnic_fc_ctlr_trace_buf_p); | |
600 | fnic_fc_ctlr_trace_buf_p = 0; | |
601 | } | |
602 | pr_info("fnic:Successfully FC_CTLR Freed Trace Buffer\n"); | |
603 | } | |
604 | ||
605 | /* | |
606 | * fnic_fc_ctlr_set_trace_data: | |
607 | * Maintain rd & wr idx accordingly and set data | |
608 | * Passed parameters: | |
609 | * host_no: host number accociated with fnic | |
610 | * frame_type: send_frame, rece_frame or link event | |
611 | * fc_frame: pointer to fc_frame | |
612 | * frame_len: Length of the fc_frame | |
613 | * Description: | |
614 | * This routine will get next available wr_idx and | |
615 | * copy all passed trace data to the buffer pointed by wr_idx | |
616 | * and increment wr_idx. It will also make sure that we dont | |
617 | * overwrite the entry which we are reading and also | |
618 | * wrap around if we reach the maximum entries. | |
619 | * Returned Value: | |
620 | * It will return 0 for success or -1 for failure | |
621 | */ | |
622 | int fnic_fc_trace_set_data(u32 host_no, u8 frame_type, | |
623 | char *frame, u32 fc_trc_frame_len) | |
624 | { | |
625 | unsigned long flags; | |
626 | struct fc_trace_hdr *fc_buf; | |
627 | unsigned long eth_fcoe_hdr_len; | |
628 | char *fc_trace; | |
629 | ||
630 | if (fnic_fc_tracing_enabled == 0) | |
631 | return 0; | |
632 | ||
633 | spin_lock_irqsave(&fnic_fc_trace_lock, flags); | |
634 | ||
635 | if (fnic_fc_trace_cleared == 1) { | |
636 | fc_trace_entries.rd_idx = fc_trace_entries.wr_idx = 0; | |
1a84db56 | 637 | pr_info("fnic: Resetting the read idx\n"); |
abb14148 HS |
638 | memset((void *)fnic_fc_ctlr_trace_buf_p, 0, |
639 | fnic_fc_trace_max_pages * PAGE_SIZE); | |
640 | fnic_fc_trace_cleared = 0; | |
641 | } | |
642 | ||
643 | fc_buf = (struct fc_trace_hdr *) | |
644 | fc_trace_entries.page_offset[fc_trace_entries.wr_idx]; | |
645 | ||
646 | fc_trace_entries.wr_idx++; | |
647 | ||
648 | if (fc_trace_entries.wr_idx >= fc_trace_max_entries) | |
649 | fc_trace_entries.wr_idx = 0; | |
650 | ||
651 | if (fc_trace_entries.wr_idx == fc_trace_entries.rd_idx) { | |
652 | fc_trace_entries.rd_idx++; | |
653 | if (fc_trace_entries.rd_idx >= fc_trace_max_entries) | |
654 | fc_trace_entries.rd_idx = 0; | |
655 | } | |
656 | ||
48c4676d | 657 | ktime_get_real_ts64(&fc_buf->time_stamp); |
abb14148 HS |
658 | fc_buf->host_no = host_no; |
659 | fc_buf->frame_type = frame_type; | |
660 | ||
661 | fc_trace = (char *)FC_TRACE_ADDRESS(fc_buf); | |
662 | ||
663 | /* During the receive path, we do not have eth hdr as well as fcoe hdr | |
664 | * at trace entry point so we will stuff 0xff just to make it generic. | |
665 | */ | |
666 | if (frame_type == FNIC_FC_RECV) { | |
667 | eth_fcoe_hdr_len = sizeof(struct ethhdr) + | |
668 | sizeof(struct fcoe_hdr); | |
abb14148 HS |
669 | memset((char *)fc_trace, 0xff, eth_fcoe_hdr_len); |
670 | /* Copy the rest of data frame */ | |
671 | memcpy((char *)(fc_trace + eth_fcoe_hdr_len), (void *)frame, | |
672 | min_t(u8, fc_trc_frame_len, | |
042b356a HS |
673 | (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE |
674 | - eth_fcoe_hdr_len))); | |
abb14148 HS |
675 | } else { |
676 | memcpy((char *)fc_trace, (void *)frame, | |
677 | min_t(u8, fc_trc_frame_len, | |
678 | (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE))); | |
679 | } | |
680 | ||
681 | /* Store the actual received length */ | |
682 | fc_buf->frame_len = fc_trc_frame_len; | |
683 | ||
684 | spin_unlock_irqrestore(&fnic_fc_trace_lock, flags); | |
685 | return 0; | |
686 | } | |
687 | ||
688 | /* | |
689 | * fnic_fc_ctlr_get_trace_data: Copy trace buffer to a memory file | |
690 | * Passed parameter: | |
691 | * @fnic_dbgfs_t: pointer to debugfs trace buffer | |
692 | * rdata_flag: 1 => Unformated file | |
693 | * 0 => formated file | |
694 | * Description: | |
695 | * This routine will copy the trace data to memory file with | |
696 | * proper formatting and also copy to another memory | |
697 | * file without formatting for further procesing. | |
698 | * Retrun Value: | |
699 | * Number of bytes that were dumped into fnic_dbgfs_t | |
700 | */ | |
701 | ||
702 | int fnic_fc_trace_get_data(fnic_dbgfs_t *fnic_dbgfs_prt, u8 rdata_flag) | |
703 | { | |
704 | int rd_idx, wr_idx; | |
705 | unsigned long flags; | |
706 | int len = 0, j; | |
707 | struct fc_trace_hdr *tdata; | |
708 | char *fc_trace; | |
709 | ||
710 | spin_lock_irqsave(&fnic_fc_trace_lock, flags); | |
711 | if (fc_trace_entries.wr_idx == fc_trace_entries.rd_idx) { | |
712 | spin_unlock_irqrestore(&fnic_fc_trace_lock, flags); | |
713 | pr_info("fnic: Buffer is empty\n"); | |
714 | return 0; | |
715 | } | |
716 | rd_idx = fc_trace_entries.rd_idx; | |
717 | wr_idx = fc_trace_entries.wr_idx; | |
718 | if (rdata_flag == 0) { | |
719 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
720 | (fnic_fc_trace_max_pages * PAGE_SIZE * 3) - len, | |
721 | "Time Stamp (UTC)\t\t" | |
722 | "Host No: F Type: len: FCoE_FRAME:\n"); | |
723 | } | |
724 | ||
725 | while (rd_idx != wr_idx) { | |
726 | tdata = (struct fc_trace_hdr *) | |
727 | fc_trace_entries.page_offset[rd_idx]; | |
728 | if (!tdata) { | |
729 | pr_info("fnic: Rd data is NULL\n"); | |
730 | spin_unlock_irqrestore(&fnic_fc_trace_lock, flags); | |
731 | return 0; | |
732 | } | |
733 | if (rdata_flag == 0) { | |
734 | copy_and_format_trace_data(tdata, | |
735 | fnic_dbgfs_prt, &len, rdata_flag); | |
736 | } else { | |
737 | fc_trace = (char *)tdata; | |
738 | for (j = 0; j < FC_TRC_SIZE_BYTES; j++) { | |
739 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
740 | (fnic_fc_trace_max_pages * PAGE_SIZE * 3) | |
741 | - len, "%02x", fc_trace[j] & 0xff); | |
742 | } /* for loop */ | |
743 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
744 | (fnic_fc_trace_max_pages * PAGE_SIZE * 3) - len, | |
745 | "\n"); | |
746 | } | |
747 | rd_idx++; | |
748 | if (rd_idx > (fc_trace_max_entries - 1)) | |
749 | rd_idx = 0; | |
750 | } | |
751 | ||
752 | spin_unlock_irqrestore(&fnic_fc_trace_lock, flags); | |
753 | return len; | |
754 | } | |
755 | ||
756 | /* | |
757 | * copy_and_format_trace_data: Copy formatted data to char * buffer | |
758 | * Passed Parameter: | |
759 | * @fc_trace_hdr_t: pointer to trace data | |
760 | * @fnic_dbgfs_t: pointer to debugfs trace buffer | |
761 | * @orig_len: pointer to len | |
762 | * rdata_flag: 0 => Formated file, 1 => Unformated file | |
763 | * Description: | |
764 | * This routine will format and copy the passed trace data | |
765 | * for formated file or unformated file accordingly. | |
766 | */ | |
767 | ||
768 | void copy_and_format_trace_data(struct fc_trace_hdr *tdata, | |
769 | fnic_dbgfs_t *fnic_dbgfs_prt, int *orig_len, | |
770 | u8 rdata_flag) | |
771 | { | |
772 | struct tm tm; | |
773 | int j, i = 1, len; | |
774 | char *fc_trace, *fmt; | |
775 | int ethhdr_len = sizeof(struct ethhdr) - 1; | |
776 | int fcoehdr_len = sizeof(struct fcoe_hdr); | |
777 | int fchdr_len = sizeof(struct fc_frame_header); | |
778 | int max_size = fnic_fc_trace_max_pages * PAGE_SIZE * 3; | |
779 | ||
780 | tdata->frame_type = tdata->frame_type & 0x7F; | |
781 | ||
782 | len = *orig_len; | |
783 | ||
48c4676d | 784 | time64_to_tm(tdata->time_stamp.tv_sec, 0, &tm); |
abb14148 HS |
785 | |
786 | fmt = "%02d:%02d:%04ld %02d:%02d:%02d.%09lu ns%8x %c%8x\t"; | |
787 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
86001f24 | 788 | max_size - len, |
abb14148 HS |
789 | fmt, |
790 | tm.tm_mon + 1, tm.tm_mday, tm.tm_year + 1900, | |
791 | tm.tm_hour, tm.tm_min, tm.tm_sec, | |
792 | tdata->time_stamp.tv_nsec, tdata->host_no, | |
793 | tdata->frame_type, tdata->frame_len); | |
794 | ||
795 | fc_trace = (char *)FC_TRACE_ADDRESS(tdata); | |
796 | ||
797 | for (j = 0; j < min_t(u8, tdata->frame_len, | |
798 | (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE)); j++) { | |
799 | if (tdata->frame_type == FNIC_FC_LE) { | |
800 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
801 | max_size - len, "%c", fc_trace[j]); | |
802 | } else { | |
803 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
804 | max_size - len, "%02x", fc_trace[j] & 0xff); | |
805 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
806 | max_size - len, " "); | |
807 | if (j == ethhdr_len || | |
808 | j == ethhdr_len + fcoehdr_len || | |
809 | j == ethhdr_len + fcoehdr_len + fchdr_len || | |
810 | (i > 3 && j%fchdr_len == 0)) { | |
811 | len += snprintf(fnic_dbgfs_prt->buffer | |
86001f24 | 812 | + len, max_size - len, |
abb14148 HS |
813 | "\n\t\t\t\t\t\t\t\t"); |
814 | i++; | |
815 | } | |
816 | } /* end of else*/ | |
817 | } /* End of for loop*/ | |
818 | len += snprintf(fnic_dbgfs_prt->buffer + len, | |
819 | max_size - len, "\n"); | |
820 | *orig_len = len; | |
821 | } |