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