1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * QLogic FCoE Offload Driver |
4 | * Copyright (c) 2016-2018 QLogic Corporation |
5 | */ |
6 | #ifdef CONFIG_DEBUG_FS |
7 | |
8 | #include <linux/uaccess.h> |
9 | #include <linux/debugfs.h> |
10 | #include <linux/module.h> |
11 | #include <linux/vmalloc.h> |
12 | |
13 | #include "qedf.h" |
14 | #include "qedf_dbg.h" |
15 | |
16 | static struct dentry *qedf_dbg_root; |
17 | |
18 | /* |
19 | * qedf_dbg_host_init - setup the debugfs file for the pf |
20 | */ |
21 | void |
22 | qedf_dbg_host_init(struct qedf_dbg_ctx *qedf, |
23 | const struct qedf_debugfs_ops *dops, |
24 | const struct file_operations *fops) |
25 | { |
26 | char host_dirname[32]; |
27 | |
28 | QEDF_INFO(qedf, QEDF_LOG_DEBUGFS, "Creating debugfs host node\n" ); |
29 | /* create pf dir */ |
30 | sprintf(buf: host_dirname, fmt: "host%u" , qedf->host_no); |
31 | qedf->bdf_dentry = debugfs_create_dir(name: host_dirname, parent: qedf_dbg_root); |
32 | |
33 | /* create debugfs files */ |
34 | while (dops) { |
35 | if (!(dops->name)) |
36 | break; |
37 | |
38 | debugfs_create_file(name: dops->name, mode: 0600, parent: qedf->bdf_dentry, data: qedf, |
39 | fops); |
40 | dops++; |
41 | fops++; |
42 | } |
43 | } |
44 | |
45 | /* |
46 | * qedf_dbg_host_exit - clear out the pf's debugfs entries |
47 | */ |
48 | void |
49 | qedf_dbg_host_exit(struct qedf_dbg_ctx *qedf_dbg) |
50 | { |
51 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Destroying debugfs host " |
52 | "entry\n" ); |
53 | /* remove debugfs entries of this PF */ |
54 | debugfs_remove_recursive(dentry: qedf_dbg->bdf_dentry); |
55 | qedf_dbg->bdf_dentry = NULL; |
56 | } |
57 | |
58 | /* |
59 | * qedf_dbg_init - start up debugfs for the driver |
60 | */ |
61 | void |
62 | qedf_dbg_init(char *drv_name) |
63 | { |
64 | QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Creating debugfs root node\n" ); |
65 | |
66 | /* create qed dir in root of debugfs. NULL means debugfs root */ |
67 | qedf_dbg_root = debugfs_create_dir(name: drv_name, NULL); |
68 | } |
69 | |
70 | /* |
71 | * qedf_dbg_exit - clean out the driver's debugfs entries |
72 | */ |
73 | void |
74 | qedf_dbg_exit(void) |
75 | { |
76 | QEDF_INFO(NULL, QEDF_LOG_DEBUGFS, "Destroying debugfs root " |
77 | "entry\n" ); |
78 | |
79 | /* remove qed dir in root of debugfs */ |
80 | debugfs_remove_recursive(dentry: qedf_dbg_root); |
81 | qedf_dbg_root = NULL; |
82 | } |
83 | |
84 | const struct qedf_debugfs_ops qedf_debugfs_ops[] = { |
85 | { "fp_int" , NULL }, |
86 | { "io_trace" , NULL }, |
87 | { "debug" , NULL }, |
88 | { "stop_io_on_error" , NULL}, |
89 | { "driver_stats" , NULL}, |
90 | { "clear_stats" , NULL}, |
91 | { "offload_stats" , NULL}, |
92 | /* This must be last */ |
93 | { NULL, NULL } |
94 | }; |
95 | |
96 | DECLARE_PER_CPU(struct qedf_percpu_iothread_s, qedf_percpu_iothreads); |
97 | |
98 | static ssize_t |
99 | qedf_dbg_fp_int_cmd_read(struct file *filp, char __user *buffer, size_t count, |
100 | loff_t *ppos) |
101 | { |
102 | ssize_t ret; |
103 | size_t cnt = 0; |
104 | char *cbuf; |
105 | int id; |
106 | struct qedf_fastpath *fp = NULL; |
107 | struct qedf_dbg_ctx *qedf_dbg = |
108 | (struct qedf_dbg_ctx *)filp->private_data; |
109 | struct qedf_ctx *qedf = container_of(qedf_dbg, |
110 | struct qedf_ctx, dbg_ctx); |
111 | |
112 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n" ); |
113 | |
114 | cbuf = vmalloc(QEDF_DEBUGFS_LOG_LEN); |
115 | if (!cbuf) |
116 | return 0; |
117 | |
118 | cnt += scnprintf(buf: cbuf + cnt, QEDF_DEBUGFS_LOG_LEN - cnt, fmt: "\nFastpath I/O completions\n\n" ); |
119 | |
120 | for (id = 0; id < qedf->num_queues; id++) { |
121 | fp = &(qedf->fp_array[id]); |
122 | if (fp->sb_id == QEDF_SB_ID_NULL) |
123 | continue; |
124 | cnt += scnprintf(buf: cbuf + cnt, QEDF_DEBUGFS_LOG_LEN - cnt, |
125 | fmt: "#%d: %lu\n" , id, fp->completions); |
126 | } |
127 | |
128 | ret = simple_read_from_buffer(to: buffer, count, ppos, from: cbuf, available: cnt); |
129 | |
130 | vfree(addr: cbuf); |
131 | |
132 | return ret; |
133 | } |
134 | |
135 | static ssize_t |
136 | qedf_dbg_fp_int_cmd_write(struct file *filp, const char __user *buffer, |
137 | size_t count, loff_t *ppos) |
138 | { |
139 | if (!count || *ppos) |
140 | return 0; |
141 | |
142 | return count; |
143 | } |
144 | |
145 | static ssize_t |
146 | qedf_dbg_debug_cmd_read(struct file *filp, char __user *buffer, size_t count, |
147 | loff_t *ppos) |
148 | { |
149 | int cnt; |
150 | char cbuf[32]; |
151 | struct qedf_dbg_ctx *qedf_dbg = |
152 | (struct qedf_dbg_ctx *)filp->private_data; |
153 | |
154 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "debug mask=0x%x\n" , qedf_debug); |
155 | cnt = scnprintf(buf: cbuf, size: sizeof(cbuf), fmt: "debug mask = 0x%x\n" , qedf_debug); |
156 | |
157 | return simple_read_from_buffer(to: buffer, count, ppos, from: cbuf, available: cnt); |
158 | } |
159 | |
160 | static ssize_t |
161 | qedf_dbg_debug_cmd_write(struct file *filp, const char __user *buffer, |
162 | size_t count, loff_t *ppos) |
163 | { |
164 | uint32_t val; |
165 | void *kern_buf; |
166 | int rval; |
167 | struct qedf_dbg_ctx *qedf_dbg = |
168 | (struct qedf_dbg_ctx *)filp->private_data; |
169 | |
170 | if (!count || *ppos) |
171 | return 0; |
172 | |
173 | kern_buf = memdup_user(buffer, count); |
174 | if (IS_ERR(ptr: kern_buf)) |
175 | return PTR_ERR(ptr: kern_buf); |
176 | |
177 | rval = kstrtouint(s: kern_buf, base: 10, res: &val); |
178 | kfree(objp: kern_buf); |
179 | if (rval) |
180 | return rval; |
181 | |
182 | if (val == 1) |
183 | qedf_debug = QEDF_DEFAULT_LOG_MASK; |
184 | else |
185 | qedf_debug = val; |
186 | |
187 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Setting debug=0x%x.\n" , val); |
188 | return count; |
189 | } |
190 | |
191 | static ssize_t |
192 | qedf_dbg_stop_io_on_error_cmd_read(struct file *filp, char __user *buffer, |
193 | size_t count, loff_t *ppos) |
194 | { |
195 | int cnt; |
196 | char cbuf[7]; |
197 | struct qedf_dbg_ctx *qedf_dbg = |
198 | (struct qedf_dbg_ctx *)filp->private_data; |
199 | struct qedf_ctx *qedf = container_of(qedf_dbg, |
200 | struct qedf_ctx, dbg_ctx); |
201 | |
202 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n" ); |
203 | cnt = scnprintf(buf: cbuf, size: sizeof(cbuf), fmt: "%s\n" , |
204 | qedf->stop_io_on_error ? "true" : "false" ); |
205 | |
206 | return simple_read_from_buffer(to: buffer, count, ppos, from: cbuf, available: cnt); |
207 | } |
208 | |
209 | static ssize_t |
210 | qedf_dbg_stop_io_on_error_cmd_write(struct file *filp, |
211 | const char __user *buffer, size_t count, |
212 | loff_t *ppos) |
213 | { |
214 | void *kern_buf; |
215 | struct qedf_dbg_ctx *qedf_dbg = |
216 | (struct qedf_dbg_ctx *)filp->private_data; |
217 | struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx, |
218 | dbg_ctx); |
219 | |
220 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n" ); |
221 | |
222 | if (!count || *ppos) |
223 | return 0; |
224 | |
225 | kern_buf = memdup_user(buffer, 6); |
226 | if (IS_ERR(ptr: kern_buf)) |
227 | return PTR_ERR(ptr: kern_buf); |
228 | |
229 | if (strncmp(kern_buf, "false" , 5) == 0) |
230 | qedf->stop_io_on_error = false; |
231 | else if (strncmp(kern_buf, "true" , 4) == 0) |
232 | qedf->stop_io_on_error = true; |
233 | else if (strncmp(kern_buf, "now" , 3) == 0) |
234 | /* Trigger from user to stop all I/O on this host */ |
235 | set_bit(QEDF_DBG_STOP_IO, addr: &qedf->flags); |
236 | |
237 | kfree(objp: kern_buf); |
238 | return count; |
239 | } |
240 | |
241 | static int |
242 | qedf_io_trace_show(struct seq_file *s, void *unused) |
243 | { |
244 | int i, idx = 0; |
245 | struct qedf_ctx *qedf = s->private; |
246 | struct qedf_dbg_ctx *qedf_dbg = &qedf->dbg_ctx; |
247 | struct qedf_io_log *io_log; |
248 | unsigned long flags; |
249 | |
250 | if (!qedf_io_tracing) { |
251 | seq_puts(m: s, s: "I/O tracing not enabled.\n" ); |
252 | goto out; |
253 | } |
254 | |
255 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n" ); |
256 | |
257 | spin_lock_irqsave(&qedf->io_trace_lock, flags); |
258 | idx = qedf->io_trace_idx; |
259 | for (i = 0; i < QEDF_IO_TRACE_SIZE; i++) { |
260 | io_log = &qedf->io_trace_buf[idx]; |
261 | seq_printf(m: s, fmt: "%d:" , io_log->direction); |
262 | seq_printf(m: s, fmt: "0x%x:" , io_log->task_id); |
263 | seq_printf(m: s, fmt: "0x%06x:" , io_log->port_id); |
264 | seq_printf(m: s, fmt: "%d:" , io_log->lun); |
265 | seq_printf(m: s, fmt: "0x%02x:" , io_log->op); |
266 | seq_printf(m: s, fmt: "0x%02x%02x%02x%02x:" , io_log->lba[0], |
267 | io_log->lba[1], io_log->lba[2], io_log->lba[3]); |
268 | seq_printf(m: s, fmt: "%d:" , io_log->bufflen); |
269 | seq_printf(m: s, fmt: "%d:" , io_log->sg_count); |
270 | seq_printf(m: s, fmt: "0x%08x:" , io_log->result); |
271 | seq_printf(m: s, fmt: "%lu:" , io_log->jiffies); |
272 | seq_printf(m: s, fmt: "%d:" , io_log->refcount); |
273 | seq_printf(m: s, fmt: "%d:" , io_log->req_cpu); |
274 | seq_printf(m: s, fmt: "%d:" , io_log->int_cpu); |
275 | seq_printf(m: s, fmt: "%d:" , io_log->rsp_cpu); |
276 | seq_printf(m: s, fmt: "%d\n" , io_log->sge_type); |
277 | |
278 | idx++; |
279 | if (idx == QEDF_IO_TRACE_SIZE) |
280 | idx = 0; |
281 | } |
282 | spin_unlock_irqrestore(lock: &qedf->io_trace_lock, flags); |
283 | |
284 | out: |
285 | return 0; |
286 | } |
287 | |
288 | static int |
289 | qedf_dbg_io_trace_open(struct inode *inode, struct file *file) |
290 | { |
291 | struct qedf_dbg_ctx *qedf_dbg = inode->i_private; |
292 | struct qedf_ctx *qedf = container_of(qedf_dbg, |
293 | struct qedf_ctx, dbg_ctx); |
294 | |
295 | return single_open(file, qedf_io_trace_show, qedf); |
296 | } |
297 | |
298 | /* Based on fip_state enum from libfcoe.h */ |
299 | static char *fip_state_names[] = { |
300 | "FIP_ST_DISABLED" , |
301 | "FIP_ST_LINK_WAIT" , |
302 | "FIP_ST_AUTO" , |
303 | "FIP_ST_NON_FIP" , |
304 | "FIP_ST_ENABLED" , |
305 | "FIP_ST_VNMP_START" , |
306 | "FIP_ST_VNMP_PROBE1" , |
307 | "FIP_ST_VNMP_PROBE2" , |
308 | "FIP_ST_VNMP_CLAIM" , |
309 | "FIP_ST_VNMP_UP" , |
310 | }; |
311 | |
312 | /* Based on fc_rport_state enum from libfc.h */ |
313 | static char *fc_rport_state_names[] = { |
314 | "RPORT_ST_INIT" , |
315 | "RPORT_ST_FLOGI" , |
316 | "RPORT_ST_PLOGI_WAIT" , |
317 | "RPORT_ST_PLOGI" , |
318 | "RPORT_ST_PRLI" , |
319 | "RPORT_ST_RTV" , |
320 | "RPORT_ST_READY" , |
321 | "RPORT_ST_ADISC" , |
322 | "RPORT_ST_DELETE" , |
323 | }; |
324 | |
325 | static int |
326 | qedf_driver_stats_show(struct seq_file *s, void *unused) |
327 | { |
328 | struct qedf_ctx *qedf = s->private; |
329 | struct qedf_rport *fcport; |
330 | struct fc_rport_priv *rdata; |
331 | |
332 | seq_printf(m: s, fmt: "Host WWNN/WWPN: %016llx/%016llx\n" , |
333 | qedf->wwnn, qedf->wwpn); |
334 | seq_printf(m: s, fmt: "Host NPortID: %06x\n" , qedf->lport->port_id); |
335 | seq_printf(m: s, fmt: "Link State: %s\n" , atomic_read(v: &qedf->link_state) ? |
336 | "Up" : "Down" ); |
337 | seq_printf(m: s, fmt: "Logical Link State: %s\n" , qedf->lport->link_up ? |
338 | "Up" : "Down" ); |
339 | seq_printf(m: s, fmt: "FIP state: %s\n" , fip_state_names[qedf->ctlr.state]); |
340 | seq_printf(m: s, fmt: "FIP VLAN ID: %d\n" , qedf->vlan_id & 0xfff); |
341 | seq_printf(m: s, fmt: "FIP 802.1Q Priority: %d\n" , qedf->prio); |
342 | if (qedf->ctlr.sel_fcf) { |
343 | seq_printf(m: s, fmt: "FCF WWPN: %016llx\n" , |
344 | qedf->ctlr.sel_fcf->switch_name); |
345 | seq_printf(m: s, fmt: "FCF MAC: %pM\n" , qedf->ctlr.sel_fcf->fcf_mac); |
346 | } else { |
347 | seq_puts(m: s, s: "FCF not selected\n" ); |
348 | } |
349 | |
350 | seq_puts(m: s, s: "\nSGE stats:\n\n" ); |
351 | seq_printf(m: s, fmt: "cmg_mgr free io_reqs: %d\n" , |
352 | atomic_read(v: &qedf->cmd_mgr->free_list_cnt)); |
353 | seq_printf(m: s, fmt: "slow SGEs: %d\n" , qedf->slow_sge_ios); |
354 | seq_printf(m: s, fmt: "fast SGEs: %d\n\n" , qedf->fast_sge_ios); |
355 | |
356 | seq_puts(m: s, s: "Offloaded ports:\n\n" ); |
357 | |
358 | rcu_read_lock(); |
359 | list_for_each_entry_rcu(fcport, &qedf->fcports, peers) { |
360 | rdata = fcport->rdata; |
361 | if (rdata == NULL) |
362 | continue; |
363 | seq_printf(m: s, fmt: "%016llx/%016llx/%06x: state=%s, free_sqes=%d, num_active_ios=%d\n" , |
364 | rdata->rport->node_name, rdata->rport->port_name, |
365 | rdata->ids.port_id, |
366 | fc_rport_state_names[rdata->rp_state], |
367 | atomic_read(v: &fcport->free_sqes), |
368 | atomic_read(v: &fcport->num_active_ios)); |
369 | } |
370 | rcu_read_unlock(); |
371 | |
372 | return 0; |
373 | } |
374 | |
375 | static int |
376 | qedf_dbg_driver_stats_open(struct inode *inode, struct file *file) |
377 | { |
378 | struct qedf_dbg_ctx *qedf_dbg = inode->i_private; |
379 | struct qedf_ctx *qedf = container_of(qedf_dbg, |
380 | struct qedf_ctx, dbg_ctx); |
381 | |
382 | return single_open(file, qedf_driver_stats_show, qedf); |
383 | } |
384 | |
385 | static ssize_t |
386 | qedf_dbg_clear_stats_cmd_read(struct file *filp, char __user *buffer, |
387 | size_t count, loff_t *ppos) |
388 | { |
389 | int cnt = 0; |
390 | |
391 | /* Essentially a read stub */ |
392 | cnt = min_t(int, count, cnt - *ppos); |
393 | *ppos += cnt; |
394 | return cnt; |
395 | } |
396 | |
397 | static ssize_t |
398 | qedf_dbg_clear_stats_cmd_write(struct file *filp, |
399 | const char __user *buffer, size_t count, |
400 | loff_t *ppos) |
401 | { |
402 | struct qedf_dbg_ctx *qedf_dbg = |
403 | (struct qedf_dbg_ctx *)filp->private_data; |
404 | struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx, |
405 | dbg_ctx); |
406 | |
407 | QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "Clearing stat counters.\n" ); |
408 | |
409 | if (!count || *ppos) |
410 | return 0; |
411 | |
412 | /* Clear stat counters exposed by 'stats' node */ |
413 | qedf->slow_sge_ios = 0; |
414 | qedf->fast_sge_ios = 0; |
415 | |
416 | return count; |
417 | } |
418 | |
419 | static int |
420 | qedf_offload_stats_show(struct seq_file *s, void *unused) |
421 | { |
422 | struct qedf_ctx *qedf = s->private; |
423 | struct qed_fcoe_stats *fw_fcoe_stats; |
424 | |
425 | fw_fcoe_stats = kmalloc(size: sizeof(struct qed_fcoe_stats), GFP_KERNEL); |
426 | if (!fw_fcoe_stats) { |
427 | QEDF_ERR(&(qedf->dbg_ctx), "Could not allocate memory for " |
428 | "fw_fcoe_stats.\n" ); |
429 | goto out; |
430 | } |
431 | |
432 | /* Query firmware for offload stats */ |
433 | qed_ops->get_stats(qedf->cdev, fw_fcoe_stats); |
434 | |
435 | seq_printf(m: s, fmt: "fcoe_rx_byte_cnt=%llu\n" |
436 | "fcoe_rx_data_pkt_cnt=%llu\n" |
437 | "fcoe_rx_xfer_pkt_cnt=%llu\n" |
438 | "fcoe_rx_other_pkt_cnt=%llu\n" |
439 | "fcoe_silent_drop_pkt_cmdq_full_cnt=%u\n" |
440 | "fcoe_silent_drop_pkt_crc_error_cnt=%u\n" |
441 | "fcoe_silent_drop_pkt_task_invalid_cnt=%u\n" |
442 | "fcoe_silent_drop_total_pkt_cnt=%u\n" |
443 | "fcoe_silent_drop_pkt_rq_full_cnt=%u\n" |
444 | "fcoe_tx_byte_cnt=%llu\n" |
445 | "fcoe_tx_data_pkt_cnt=%llu\n" |
446 | "fcoe_tx_xfer_pkt_cnt=%llu\n" |
447 | "fcoe_tx_other_pkt_cnt=%llu\n" , |
448 | fw_fcoe_stats->fcoe_rx_byte_cnt, |
449 | fw_fcoe_stats->fcoe_rx_data_pkt_cnt, |
450 | fw_fcoe_stats->fcoe_rx_xfer_pkt_cnt, |
451 | fw_fcoe_stats->fcoe_rx_other_pkt_cnt, |
452 | fw_fcoe_stats->fcoe_silent_drop_pkt_cmdq_full_cnt, |
453 | fw_fcoe_stats->fcoe_silent_drop_pkt_crc_error_cnt, |
454 | fw_fcoe_stats->fcoe_silent_drop_pkt_task_invalid_cnt, |
455 | fw_fcoe_stats->fcoe_silent_drop_total_pkt_cnt, |
456 | fw_fcoe_stats->fcoe_silent_drop_pkt_rq_full_cnt, |
457 | fw_fcoe_stats->fcoe_tx_byte_cnt, |
458 | fw_fcoe_stats->fcoe_tx_data_pkt_cnt, |
459 | fw_fcoe_stats->fcoe_tx_xfer_pkt_cnt, |
460 | fw_fcoe_stats->fcoe_tx_other_pkt_cnt); |
461 | |
462 | kfree(objp: fw_fcoe_stats); |
463 | out: |
464 | return 0; |
465 | } |
466 | |
467 | static int |
468 | qedf_dbg_offload_stats_open(struct inode *inode, struct file *file) |
469 | { |
470 | struct qedf_dbg_ctx *qedf_dbg = inode->i_private; |
471 | struct qedf_ctx *qedf = container_of(qedf_dbg, |
472 | struct qedf_ctx, dbg_ctx); |
473 | |
474 | return single_open(file, qedf_offload_stats_show, qedf); |
475 | } |
476 | |
477 | const struct file_operations qedf_dbg_fops[] = { |
478 | qedf_dbg_fileops(qedf, fp_int), |
479 | qedf_dbg_fileops_seq(qedf, io_trace), |
480 | qedf_dbg_fileops(qedf, debug), |
481 | qedf_dbg_fileops(qedf, stop_io_on_error), |
482 | qedf_dbg_fileops_seq(qedf, driver_stats), |
483 | qedf_dbg_fileops(qedf, clear_stats), |
484 | qedf_dbg_fileops_seq(qedf, offload_stats), |
485 | /* This must be last */ |
486 | { }, |
487 | }; |
488 | |
489 | #else /* CONFIG_DEBUG_FS */ |
490 | void qedf_dbg_host_init(struct qedf_dbg_ctx *); |
491 | void qedf_dbg_host_exit(struct qedf_dbg_ctx *); |
492 | void qedf_dbg_init(char *); |
493 | void qedf_dbg_exit(void); |
494 | #endif /* CONFIG_DEBUG_FS */ |
495 | |