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 | int i = 0; |
208 | |
209 | ktime_get_real_ts64(tv: &val1); |
210 | len = scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
211 | fmt: "------------------------------------------\n" |
212 | "\t\tTime\n" |
213 | "------------------------------------------\n" ); |
214 | |
215 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
216 | fmt: "Current time : [%lld:%ld]\n" |
217 | "Last stats reset time: [%lld:%09ld]\n" |
218 | "Last stats read time: [%lld:%ld]\n" |
219 | "delta since last reset: [%lld:%ld]\n" |
220 | "delta since last read: [%lld:%ld]\n" , |
221 | (s64)val1.tv_sec, val1.tv_nsec, |
222 | (s64)stats->stats_timestamps.last_reset_time.tv_sec, |
223 | stats->stats_timestamps.last_reset_time.tv_nsec, |
224 | (s64)stats->stats_timestamps.last_read_time.tv_sec, |
225 | stats->stats_timestamps.last_read_time.tv_nsec, |
226 | (s64)timespec64_sub(lhs: val1, rhs: stats->stats_timestamps.last_reset_time).tv_sec, |
227 | timespec64_sub(lhs: val1, rhs: stats->stats_timestamps.last_reset_time).tv_nsec, |
228 | (s64)timespec64_sub(lhs: val1, rhs: stats->stats_timestamps.last_read_time).tv_sec, |
229 | timespec64_sub(lhs: val1, rhs: stats->stats_timestamps.last_read_time).tv_nsec); |
230 | |
231 | stats->stats_timestamps.last_read_time = val1; |
232 | |
233 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
234 | fmt: "------------------------------------------\n" |
235 | "\t\tIO Statistics\n" |
236 | "------------------------------------------\n" ); |
237 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
238 | fmt: "Number of Active IOs: %lld\nMaximum Active IOs: %lld\n" |
239 | "Number of IOs: %lld\nNumber of IO Completions: %lld\n" |
240 | "Number of IO Failures: %lld\nNumber of IO NOT Found: %lld\n" |
241 | "Number of Memory alloc Failures: %lld\n" |
242 | "Number of IOREQ Null: %lld\n" |
243 | "Number of SCSI cmd pointer Null: %lld\n" |
244 | |
245 | "\nIO completion times: \n" |
246 | " < 10 ms : %lld\n" |
247 | " 10 ms - 100 ms : %lld\n" |
248 | " 100 ms - 500 ms : %lld\n" |
249 | " 500 ms - 5 sec: %lld\n" |
250 | " 5 sec - 10 sec: %lld\n" |
251 | " 10 sec - 30 sec: %lld\n" |
252 | " > 30 sec: %lld\n" , |
253 | (u64)atomic64_read(v: &stats->io_stats.active_ios), |
254 | (u64)atomic64_read(v: &stats->io_stats.max_active_ios), |
255 | (u64)atomic64_read(v: &stats->io_stats.num_ios), |
256 | (u64)atomic64_read(v: &stats->io_stats.io_completions), |
257 | (u64)atomic64_read(v: &stats->io_stats.io_failures), |
258 | (u64)atomic64_read(v: &stats->io_stats.io_not_found), |
259 | (u64)atomic64_read(v: &stats->io_stats.alloc_failures), |
260 | (u64)atomic64_read(v: &stats->io_stats.ioreq_null), |
261 | (u64)atomic64_read(v: &stats->io_stats.sc_null), |
262 | (u64)atomic64_read(v: &stats->io_stats.io_btw_0_to_10_msec), |
263 | (u64)atomic64_read(v: &stats->io_stats.io_btw_10_to_100_msec), |
264 | (u64)atomic64_read(v: &stats->io_stats.io_btw_100_to_500_msec), |
265 | (u64)atomic64_read(v: &stats->io_stats.io_btw_500_to_5000_msec), |
266 | (u64)atomic64_read(v: &stats->io_stats.io_btw_5000_to_10000_msec), |
267 | (u64)atomic64_read(v: &stats->io_stats.io_btw_10000_to_30000_msec), |
268 | (u64)atomic64_read(v: &stats->io_stats.io_greater_than_30000_msec)); |
269 | |
270 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
271 | fmt: "------------------------------------------\n" |
272 | "\t\tIO Queues and cumulative IOs\n" |
273 | "------------------------------------------\n" ); |
274 | |
275 | for (i = 0; i < FNIC_MQ_MAX_QUEUES; i++) { |
276 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
277 | fmt: "Q:%d -> %lld\n" , i, (u64)atomic64_read(v: &stats->io_stats.ios[i])); |
278 | } |
279 | |
280 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
281 | fmt: "\nCurrent Max IO time : %lld\n" , |
282 | (u64)atomic64_read(v: &stats->io_stats.current_max_io_time)); |
283 | |
284 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
285 | fmt: "\n------------------------------------------\n" |
286 | "\t\tAbort Statistics\n" |
287 | "------------------------------------------\n" ); |
288 | |
289 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
290 | fmt: "Number of Aborts: %lld\n" |
291 | "Number of Abort Failures: %lld\n" |
292 | "Number of Abort Driver Timeouts: %lld\n" |
293 | "Number of Abort FW Timeouts: %lld\n" |
294 | "Number of Abort IO NOT Found: %lld\n" |
295 | |
296 | "Abort issued times: \n" |
297 | " < 6 sec : %lld\n" |
298 | " 6 sec - 20 sec : %lld\n" |
299 | " 20 sec - 30 sec : %lld\n" |
300 | " 30 sec - 40 sec : %lld\n" |
301 | " 40 sec - 50 sec : %lld\n" |
302 | " 50 sec - 60 sec : %lld\n" |
303 | " > 60 sec: %lld\n" , |
304 | |
305 | (u64)atomic64_read(v: &stats->abts_stats.aborts), |
306 | (u64)atomic64_read(v: &stats->abts_stats.abort_failures), |
307 | (u64)atomic64_read(v: &stats->abts_stats.abort_drv_timeouts), |
308 | (u64)atomic64_read(v: &stats->abts_stats.abort_fw_timeouts), |
309 | (u64)atomic64_read(v: &stats->abts_stats.abort_io_not_found), |
310 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_btw_0_to_6_sec), |
311 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_btw_6_to_20_sec), |
312 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_btw_20_to_30_sec), |
313 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_btw_30_to_40_sec), |
314 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_btw_40_to_50_sec), |
315 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_btw_50_to_60_sec), |
316 | (u64)atomic64_read(v: &stats->abts_stats.abort_issued_greater_than_60_sec)); |
317 | |
318 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
319 | fmt: "\n------------------------------------------\n" |
320 | "\t\tTerminate Statistics\n" |
321 | "------------------------------------------\n" ); |
322 | |
323 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
324 | fmt: "Number of Terminates: %lld\n" |
325 | "Maximum Terminates: %lld\n" |
326 | "Number of Terminate Driver Timeouts: %lld\n" |
327 | "Number of Terminate FW Timeouts: %lld\n" |
328 | "Number of Terminate IO NOT Found: %lld\n" |
329 | "Number of Terminate Failures: %lld\n" , |
330 | (u64)atomic64_read(v: &stats->term_stats.terminates), |
331 | (u64)atomic64_read(v: &stats->term_stats.max_terminates), |
332 | (u64)atomic64_read(v: &stats->term_stats.terminate_drv_timeouts), |
333 | (u64)atomic64_read(v: &stats->term_stats.terminate_fw_timeouts), |
334 | (u64)atomic64_read(v: &stats->term_stats.terminate_io_not_found), |
335 | (u64)atomic64_read(v: &stats->term_stats.terminate_failures)); |
336 | |
337 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
338 | fmt: "\n------------------------------------------\n" |
339 | "\t\tReset Statistics\n" |
340 | "------------------------------------------\n" ); |
341 | |
342 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
343 | fmt: "Number of Device Resets: %lld\n" |
344 | "Number of Device Reset Failures: %lld\n" |
345 | "Number of Device Reset Aborts: %lld\n" |
346 | "Number of Device Reset Timeouts: %lld\n" |
347 | "Number of Device Reset Terminates: %lld\n" |
348 | "Number of FW Resets: %lld\n" |
349 | "Number of FW Reset Completions: %lld\n" |
350 | "Number of FW Reset Failures: %lld\n" |
351 | "Number of Fnic Reset: %lld\n" |
352 | "Number of Fnic Reset Completions: %lld\n" |
353 | "Number of Fnic Reset Failures: %lld\n" , |
354 | (u64)atomic64_read(v: &stats->reset_stats.device_resets), |
355 | (u64)atomic64_read(v: &stats->reset_stats.device_reset_failures), |
356 | (u64)atomic64_read(v: &stats->reset_stats.device_reset_aborts), |
357 | (u64)atomic64_read(v: &stats->reset_stats.device_reset_timeouts), |
358 | (u64)atomic64_read( |
359 | v: &stats->reset_stats.device_reset_terminates), |
360 | (u64)atomic64_read(v: &stats->reset_stats.fw_resets), |
361 | (u64)atomic64_read(v: &stats->reset_stats.fw_reset_completions), |
362 | (u64)atomic64_read(v: &stats->reset_stats.fw_reset_failures), |
363 | (u64)atomic64_read(v: &stats->reset_stats.fnic_resets), |
364 | (u64)atomic64_read( |
365 | v: &stats->reset_stats.fnic_reset_completions), |
366 | (u64)atomic64_read(v: &stats->reset_stats.fnic_reset_failures)); |
367 | |
368 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
369 | fmt: "\n------------------------------------------\n" |
370 | "\t\tFirmware Statistics\n" |
371 | "------------------------------------------\n" ); |
372 | |
373 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
374 | fmt: "Number of Active FW Requests %lld\n" |
375 | "Maximum FW Requests: %lld\n" |
376 | "Number of FW out of resources: %lld\n" |
377 | "Number of FW IO errors: %lld\n" , |
378 | (u64)atomic64_read(v: &stats->fw_stats.active_fw_reqs), |
379 | (u64)atomic64_read(v: &stats->fw_stats.max_fw_reqs), |
380 | (u64)atomic64_read(v: &stats->fw_stats.fw_out_of_resources), |
381 | (u64)atomic64_read(v: &stats->fw_stats.io_fw_errs)); |
382 | |
383 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
384 | fmt: "\n------------------------------------------\n" |
385 | "\t\tVlan Discovery Statistics\n" |
386 | "------------------------------------------\n" ); |
387 | |
388 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
389 | fmt: "Number of Vlan Discovery Requests Sent %lld\n" |
390 | "Vlan Response Received with no FCF VLAN ID: %lld\n" |
391 | "No solicitations recvd after vlan set, expiry count: %lld\n" |
392 | "Flogi rejects count: %lld\n" , |
393 | (u64)atomic64_read(v: &stats->vlan_stats.vlan_disc_reqs), |
394 | (u64)atomic64_read(v: &stats->vlan_stats.resp_withno_vlanID), |
395 | (u64)atomic64_read(v: &stats->vlan_stats.sol_expiry_count), |
396 | (u64)atomic64_read(v: &stats->vlan_stats.flogi_rejects)); |
397 | |
398 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
399 | fmt: "\n------------------------------------------\n" |
400 | "\t\tOther Important Statistics\n" |
401 | "------------------------------------------\n" ); |
402 | |
403 | jiffies_to_timespec64(jiffies: stats->misc_stats.last_isr_time, value: &val1); |
404 | jiffies_to_timespec64(jiffies: stats->misc_stats.last_ack_time, value: &val2); |
405 | |
406 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
407 | fmt: "Last ISR time: %llu (%8llu.%09lu)\n" |
408 | "Last ACK time: %llu (%8llu.%09lu)\n" |
409 | "Max ISR jiffies: %llu\n" |
410 | "Max ISR time (ms) (0 denotes < 1 ms): %llu\n" |
411 | "Corr. work done: %llu\n" |
412 | "Number of ISRs: %lld\n" |
413 | "Maximum CQ Entries: %lld\n" |
414 | "Number of ACK index out of range: %lld\n" |
415 | "Number of data count mismatch: %lld\n" |
416 | "Number of FCPIO Timeouts: %lld\n" |
417 | "Number of FCPIO Aborted: %lld\n" |
418 | "Number of SGL Invalid: %lld\n" |
419 | "Number of Copy WQ Alloc Failures for ABTs: %lld\n" |
420 | "Number of Copy WQ Alloc Failures for Device Reset: %lld\n" |
421 | "Number of Copy WQ Alloc Failures for IOs: %lld\n" |
422 | "Number of no icmnd itmf Completions: %lld\n" |
423 | "Number of Check Conditions encountered: %lld\n" |
424 | "Number of QUEUE Fulls: %lld\n" |
425 | "Number of rport not ready: %lld\n" |
426 | "Number of receive frame errors: %lld\n" , |
427 | (u64)stats->misc_stats.last_isr_time, |
428 | (s64)val1.tv_sec, val1.tv_nsec, |
429 | (u64)stats->misc_stats.last_ack_time, |
430 | (s64)val2.tv_sec, val2.tv_nsec, |
431 | (u64)atomic64_read(v: &stats->misc_stats.max_isr_jiffies), |
432 | (u64)atomic64_read(v: &stats->misc_stats.max_isr_time_ms), |
433 | (u64)atomic64_read(v: &stats->misc_stats.corr_work_done), |
434 | (u64)atomic64_read(v: &stats->misc_stats.isr_count), |
435 | (u64)atomic64_read(v: &stats->misc_stats.max_cq_entries), |
436 | (u64)atomic64_read(v: &stats->misc_stats.ack_index_out_of_range), |
437 | (u64)atomic64_read(v: &stats->misc_stats.data_count_mismatch), |
438 | (u64)atomic64_read(v: &stats->misc_stats.fcpio_timeout), |
439 | (u64)atomic64_read(v: &stats->misc_stats.fcpio_aborted), |
440 | (u64)atomic64_read(v: &stats->misc_stats.sgl_invalid), |
441 | (u64)atomic64_read( |
442 | v: &stats->misc_stats.abts_cpwq_alloc_failures), |
443 | (u64)atomic64_read( |
444 | v: &stats->misc_stats.devrst_cpwq_alloc_failures), |
445 | (u64)atomic64_read(v: &stats->misc_stats.io_cpwq_alloc_failures), |
446 | (u64)atomic64_read(v: &stats->misc_stats.no_icmnd_itmf_cmpls), |
447 | (u64)atomic64_read(v: &stats->misc_stats.check_condition), |
448 | (u64)atomic64_read(v: &stats->misc_stats.queue_fulls), |
449 | (u64)atomic64_read(v: &stats->misc_stats.rport_not_ready), |
450 | (u64)atomic64_read(v: &stats->misc_stats.frame_errors)); |
451 | |
452 | len += scnprintf(buf: debug->debug_buffer + len, size: buf_size - len, |
453 | fmt: "Firmware reported port speed: %llu\n" , |
454 | (u64)atomic64_read( |
455 | v: &stats->misc_stats.current_port_speed)); |
456 | |
457 | return len; |
458 | |
459 | } |
460 | |
461 | /* |
462 | * fnic_trace_buf_init - Initialize fnic trace buffer logging facility |
463 | * |
464 | * Description: |
465 | * Initialize trace buffer data structure by allocating required memory and |
466 | * setting page_offset information for every trace entry by adding trace entry |
467 | * length to previous page_offset value. |
468 | */ |
469 | int fnic_trace_buf_init(void) |
470 | { |
471 | unsigned long fnic_buf_head; |
472 | int i; |
473 | int err = 0; |
474 | |
475 | trace_max_pages = fnic_trace_max_pages; |
476 | fnic_max_trace_entries = (trace_max_pages * PAGE_SIZE)/ |
477 | FNIC_ENTRY_SIZE_BYTES; |
478 | |
479 | fnic_trace_buf_p = (unsigned long)vcalloc(n: trace_max_pages, PAGE_SIZE); |
480 | if (!fnic_trace_buf_p) { |
481 | printk(KERN_ERR PFX "Failed to allocate memory " |
482 | "for fnic_trace_buf_p\n" ); |
483 | err = -ENOMEM; |
484 | goto err_fnic_trace_buf_init; |
485 | } |
486 | |
487 | fnic_trace_entries.page_offset = |
488 | vmalloc(array_size(fnic_max_trace_entries, |
489 | sizeof(unsigned long))); |
490 | if (!fnic_trace_entries.page_offset) { |
491 | printk(KERN_ERR PFX "Failed to allocate memory for" |
492 | " page_offset\n" ); |
493 | if (fnic_trace_buf_p) { |
494 | vfree(addr: (void *)fnic_trace_buf_p); |
495 | fnic_trace_buf_p = 0; |
496 | } |
497 | err = -ENOMEM; |
498 | goto err_fnic_trace_buf_init; |
499 | } |
500 | memset((void *)fnic_trace_entries.page_offset, 0, |
501 | (fnic_max_trace_entries * sizeof(unsigned long))); |
502 | fnic_trace_entries.wr_idx = fnic_trace_entries.rd_idx = 0; |
503 | fnic_buf_head = fnic_trace_buf_p; |
504 | |
505 | /* |
506 | * Set page_offset field of fnic_trace_entries struct by |
507 | * calculating memory location for every trace entry using |
508 | * length of each trace entry |
509 | */ |
510 | for (i = 0; i < fnic_max_trace_entries; i++) { |
511 | fnic_trace_entries.page_offset[i] = fnic_buf_head; |
512 | fnic_buf_head += FNIC_ENTRY_SIZE_BYTES; |
513 | } |
514 | fnic_trace_debugfs_init(); |
515 | pr_info("fnic: Successfully Initialized Trace Buffer\n" ); |
516 | return err; |
517 | |
518 | err_fnic_trace_buf_init: |
519 | return err; |
520 | } |
521 | |
522 | /* |
523 | * fnic_trace_free - Free memory of fnic trace data structures. |
524 | */ |
525 | void fnic_trace_free(void) |
526 | { |
527 | fnic_tracing_enabled = 0; |
528 | fnic_trace_debugfs_terminate(); |
529 | if (fnic_trace_entries.page_offset) { |
530 | vfree(addr: (void *)fnic_trace_entries.page_offset); |
531 | fnic_trace_entries.page_offset = NULL; |
532 | } |
533 | if (fnic_trace_buf_p) { |
534 | vfree(addr: (void *)fnic_trace_buf_p); |
535 | fnic_trace_buf_p = 0; |
536 | } |
537 | printk(KERN_INFO PFX "Successfully Freed Trace Buffer\n" ); |
538 | } |
539 | |
540 | /* |
541 | * fnic_fc_ctlr_trace_buf_init - |
542 | * Initialize trace buffer to log fnic control frames |
543 | * Description: |
544 | * Initialize trace buffer data structure by allocating |
545 | * required memory for trace data as well as for Indexes. |
546 | * Frame size is 256 bytes and |
547 | * memory is allocated for 1024 entries of 256 bytes. |
548 | * Page_offset(Index) is set to the address of trace entry |
549 | * and page_offset is initialized by adding frame size |
550 | * to the previous page_offset entry. |
551 | */ |
552 | |
553 | int fnic_fc_trace_init(void) |
554 | { |
555 | unsigned long fc_trace_buf_head; |
556 | int err = 0; |
557 | int i; |
558 | |
559 | fc_trace_max_entries = (fnic_fc_trace_max_pages * PAGE_SIZE)/ |
560 | FC_TRC_SIZE_BYTES; |
561 | fnic_fc_ctlr_trace_buf_p = |
562 | (unsigned long)vmalloc(array_size(PAGE_SIZE, |
563 | fnic_fc_trace_max_pages)); |
564 | if (!fnic_fc_ctlr_trace_buf_p) { |
565 | pr_err("fnic: Failed to allocate memory for " |
566 | "FC Control Trace Buf\n" ); |
567 | err = -ENOMEM; |
568 | goto err_fnic_fc_ctlr_trace_buf_init; |
569 | } |
570 | |
571 | memset((void *)fnic_fc_ctlr_trace_buf_p, 0, |
572 | fnic_fc_trace_max_pages * PAGE_SIZE); |
573 | |
574 | /* Allocate memory for page offset */ |
575 | fc_trace_entries.page_offset = |
576 | vmalloc(array_size(fc_trace_max_entries, |
577 | sizeof(unsigned long))); |
578 | if (!fc_trace_entries.page_offset) { |
579 | pr_err("fnic:Failed to allocate memory for page_offset\n" ); |
580 | if (fnic_fc_ctlr_trace_buf_p) { |
581 | pr_err("fnic: Freeing FC Control Trace Buf\n" ); |
582 | vfree(addr: (void *)fnic_fc_ctlr_trace_buf_p); |
583 | fnic_fc_ctlr_trace_buf_p = 0; |
584 | } |
585 | err = -ENOMEM; |
586 | goto err_fnic_fc_ctlr_trace_buf_init; |
587 | } |
588 | memset((void *)fc_trace_entries.page_offset, 0, |
589 | (fc_trace_max_entries * sizeof(unsigned long))); |
590 | |
591 | fc_trace_entries.rd_idx = fc_trace_entries.wr_idx = 0; |
592 | fc_trace_buf_head = fnic_fc_ctlr_trace_buf_p; |
593 | |
594 | /* |
595 | * Set up fc_trace_entries.page_offset field with memory location |
596 | * for every trace entry |
597 | */ |
598 | for (i = 0; i < fc_trace_max_entries; i++) { |
599 | fc_trace_entries.page_offset[i] = fc_trace_buf_head; |
600 | fc_trace_buf_head += FC_TRC_SIZE_BYTES; |
601 | } |
602 | fnic_fc_trace_debugfs_init(); |
603 | pr_info("fnic: Successfully Initialized FC_CTLR Trace Buffer\n" ); |
604 | return err; |
605 | |
606 | err_fnic_fc_ctlr_trace_buf_init: |
607 | return err; |
608 | } |
609 | |
610 | /* |
611 | * Fnic_fc_ctlr_trace_free - Free memory of fnic_fc_ctlr trace data structures. |
612 | */ |
613 | void fnic_fc_trace_free(void) |
614 | { |
615 | fnic_fc_tracing_enabled = 0; |
616 | fnic_fc_trace_debugfs_terminate(); |
617 | if (fc_trace_entries.page_offset) { |
618 | vfree(addr: (void *)fc_trace_entries.page_offset); |
619 | fc_trace_entries.page_offset = NULL; |
620 | } |
621 | if (fnic_fc_ctlr_trace_buf_p) { |
622 | vfree(addr: (void *)fnic_fc_ctlr_trace_buf_p); |
623 | fnic_fc_ctlr_trace_buf_p = 0; |
624 | } |
625 | pr_info("fnic:Successfully FC_CTLR Freed Trace Buffer\n" ); |
626 | } |
627 | |
628 | /* |
629 | * fnic_fc_ctlr_set_trace_data: |
630 | * Maintain rd & wr idx accordingly and set data |
631 | * Passed parameters: |
632 | * host_no: host number associated with fnic |
633 | * frame_type: send_frame, rece_frame or link event |
634 | * fc_frame: pointer to fc_frame |
635 | * frame_len: Length of the fc_frame |
636 | * Description: |
637 | * This routine will get next available wr_idx and |
638 | * copy all passed trace data to the buffer pointed by wr_idx |
639 | * and increment wr_idx. It will also make sure that we dont |
640 | * overwrite the entry which we are reading and also |
641 | * wrap around if we reach the maximum entries. |
642 | * Returned Value: |
643 | * It will return 0 for success or -1 for failure |
644 | */ |
645 | int fnic_fc_trace_set_data(u32 host_no, u8 frame_type, |
646 | char *frame, u32 fc_trc_frame_len) |
647 | { |
648 | unsigned long flags; |
649 | struct fc_trace_hdr *fc_buf; |
650 | unsigned long eth_fcoe_hdr_len; |
651 | char *fc_trace; |
652 | |
653 | if (fnic_fc_tracing_enabled == 0) |
654 | return 0; |
655 | |
656 | spin_lock_irqsave(&fnic_fc_trace_lock, flags); |
657 | |
658 | if (fnic_fc_trace_cleared == 1) { |
659 | fc_trace_entries.rd_idx = fc_trace_entries.wr_idx = 0; |
660 | pr_info("fnic: Resetting the read idx\n" ); |
661 | memset((void *)fnic_fc_ctlr_trace_buf_p, 0, |
662 | fnic_fc_trace_max_pages * PAGE_SIZE); |
663 | fnic_fc_trace_cleared = 0; |
664 | } |
665 | |
666 | fc_buf = (struct fc_trace_hdr *) |
667 | fc_trace_entries.page_offset[fc_trace_entries.wr_idx]; |
668 | |
669 | fc_trace_entries.wr_idx++; |
670 | |
671 | if (fc_trace_entries.wr_idx >= fc_trace_max_entries) |
672 | fc_trace_entries.wr_idx = 0; |
673 | |
674 | if (fc_trace_entries.wr_idx == fc_trace_entries.rd_idx) { |
675 | fc_trace_entries.rd_idx++; |
676 | if (fc_trace_entries.rd_idx >= fc_trace_max_entries) |
677 | fc_trace_entries.rd_idx = 0; |
678 | } |
679 | |
680 | ktime_get_real_ts64(tv: &fc_buf->time_stamp); |
681 | fc_buf->host_no = host_no; |
682 | fc_buf->frame_type = frame_type; |
683 | |
684 | fc_trace = (char *)FC_TRACE_ADDRESS(fc_buf); |
685 | |
686 | /* During the receive path, we do not have eth hdr as well as fcoe hdr |
687 | * at trace entry point so we will stuff 0xff just to make it generic. |
688 | */ |
689 | if (frame_type == FNIC_FC_RECV) { |
690 | eth_fcoe_hdr_len = sizeof(struct ethhdr) + |
691 | sizeof(struct fcoe_hdr); |
692 | memset((char *)fc_trace, 0xff, eth_fcoe_hdr_len); |
693 | /* Copy the rest of data frame */ |
694 | memcpy((char *)(fc_trace + eth_fcoe_hdr_len), (void *)frame, |
695 | min_t(u8, fc_trc_frame_len, |
696 | (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE |
697 | - eth_fcoe_hdr_len))); |
698 | } else { |
699 | memcpy((char *)fc_trace, (void *)frame, |
700 | min_t(u8, fc_trc_frame_len, |
701 | (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE))); |
702 | } |
703 | |
704 | /* Store the actual received length */ |
705 | fc_buf->frame_len = fc_trc_frame_len; |
706 | |
707 | spin_unlock_irqrestore(lock: &fnic_fc_trace_lock, flags); |
708 | return 0; |
709 | } |
710 | |
711 | /* |
712 | * fnic_fc_ctlr_get_trace_data: Copy trace buffer to a memory file |
713 | * Passed parameter: |
714 | * @fnic_dbgfs_t: pointer to debugfs trace buffer |
715 | * rdata_flag: 1 => Unformatted file |
716 | * 0 => formatted file |
717 | * Description: |
718 | * This routine will copy the trace data to memory file with |
719 | * proper formatting and also copy to another memory |
720 | * file without formatting for further processing. |
721 | * Return Value: |
722 | * Number of bytes that were dumped into fnic_dbgfs_t |
723 | */ |
724 | |
725 | int fnic_fc_trace_get_data(fnic_dbgfs_t *fnic_dbgfs_prt, u8 rdata_flag) |
726 | { |
727 | int rd_idx, wr_idx; |
728 | unsigned long flags; |
729 | int len = 0, j; |
730 | struct fc_trace_hdr *tdata; |
731 | char *fc_trace; |
732 | |
733 | spin_lock_irqsave(&fnic_fc_trace_lock, flags); |
734 | if (fc_trace_entries.wr_idx == fc_trace_entries.rd_idx) { |
735 | spin_unlock_irqrestore(lock: &fnic_fc_trace_lock, flags); |
736 | pr_info("fnic: Buffer is empty\n" ); |
737 | return 0; |
738 | } |
739 | rd_idx = fc_trace_entries.rd_idx; |
740 | wr_idx = fc_trace_entries.wr_idx; |
741 | if (rdata_flag == 0) { |
742 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
743 | size: (fnic_fc_trace_max_pages * PAGE_SIZE * 3) - len, |
744 | fmt: "Time Stamp (UTC)\t\t" |
745 | "Host No: F Type: len: FCoE_FRAME:\n" ); |
746 | } |
747 | |
748 | while (rd_idx != wr_idx) { |
749 | tdata = (struct fc_trace_hdr *) |
750 | fc_trace_entries.page_offset[rd_idx]; |
751 | if (!tdata) { |
752 | pr_info("fnic: Rd data is NULL\n" ); |
753 | spin_unlock_irqrestore(lock: &fnic_fc_trace_lock, flags); |
754 | return 0; |
755 | } |
756 | if (rdata_flag == 0) { |
757 | copy_and_format_trace_data(tdata, |
758 | fnic_dbgfs_prt, len: &len, rdata_flag); |
759 | } else { |
760 | fc_trace = (char *)tdata; |
761 | for (j = 0; j < FC_TRC_SIZE_BYTES; j++) { |
762 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
763 | size: (fnic_fc_trace_max_pages * PAGE_SIZE * 3) |
764 | - len, fmt: "%02x" , fc_trace[j] & 0xff); |
765 | } /* for loop */ |
766 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
767 | size: (fnic_fc_trace_max_pages * PAGE_SIZE * 3) - len, |
768 | fmt: "\n" ); |
769 | } |
770 | rd_idx++; |
771 | if (rd_idx > (fc_trace_max_entries - 1)) |
772 | rd_idx = 0; |
773 | } |
774 | |
775 | spin_unlock_irqrestore(lock: &fnic_fc_trace_lock, flags); |
776 | return len; |
777 | } |
778 | |
779 | /* |
780 | * copy_and_format_trace_data: Copy formatted data to char * buffer |
781 | * Passed Parameter: |
782 | * @fc_trace_hdr_t: pointer to trace data |
783 | * @fnic_dbgfs_t: pointer to debugfs trace buffer |
784 | * @orig_len: pointer to len |
785 | * rdata_flag: 0 => Formatted file, 1 => Unformatted file |
786 | * Description: |
787 | * This routine will format and copy the passed trace data |
788 | * for formatted file or unformatted file accordingly. |
789 | */ |
790 | |
791 | void copy_and_format_trace_data(struct fc_trace_hdr *tdata, |
792 | fnic_dbgfs_t *fnic_dbgfs_prt, int *orig_len, |
793 | u8 rdata_flag) |
794 | { |
795 | int j, i = 1, len; |
796 | int ethhdr_len = sizeof(struct ethhdr) - 1; |
797 | int fcoehdr_len = sizeof(struct fcoe_hdr); |
798 | int fchdr_len = sizeof(struct fc_frame_header); |
799 | int max_size = fnic_fc_trace_max_pages * PAGE_SIZE * 3; |
800 | char *fc_trace; |
801 | |
802 | tdata->frame_type = tdata->frame_type & 0x7F; |
803 | |
804 | len = *orig_len; |
805 | |
806 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, size: max_size - len, |
807 | fmt: "%ptTs.%09lu ns%8x %c%8x\t" , |
808 | &tdata->time_stamp.tv_sec, tdata->time_stamp.tv_nsec, |
809 | tdata->host_no, tdata->frame_type, tdata->frame_len); |
810 | |
811 | fc_trace = (char *)FC_TRACE_ADDRESS(tdata); |
812 | |
813 | for (j = 0; j < min_t(u8, tdata->frame_len, |
814 | (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE)); j++) { |
815 | if (tdata->frame_type == FNIC_FC_LE) { |
816 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
817 | size: max_size - len, fmt: "%c" , fc_trace[j]); |
818 | } else { |
819 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
820 | size: max_size - len, fmt: "%02x" , fc_trace[j] & 0xff); |
821 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
822 | size: max_size - len, fmt: " " ); |
823 | if (j == ethhdr_len || |
824 | j == ethhdr_len + fcoehdr_len || |
825 | j == ethhdr_len + fcoehdr_len + fchdr_len || |
826 | (i > 3 && j%fchdr_len == 0)) { |
827 | len += scnprintf(buf: fnic_dbgfs_prt->buffer |
828 | + len, size: max_size - len, |
829 | fmt: "\n\t\t\t\t\t\t\t\t" ); |
830 | i++; |
831 | } |
832 | } /* end of else*/ |
833 | } /* End of for loop*/ |
834 | len += scnprintf(buf: fnic_dbgfs_prt->buffer + len, |
835 | size: max_size - len, fmt: "\n" ); |
836 | *orig_len = len; |
837 | } |
838 | |