diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_debugfs.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_debugfs.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c new file mode 100644 index 000000000000..673cfe11cc2b --- /dev/null +++ b/drivers/scsi/lpfc/lpfc_debugfs.c | |||
@@ -0,0 +1,508 @@ | |||
1 | /******************************************************************* | ||
2 | * This file is part of the Emulex Linux Device Driver for * | ||
3 | * Fibre Channel Host Bus Adapters. * | ||
4 | * Copyright (C) 2007 Emulex. All rights reserved. * | ||
5 | * EMULEX and SLI are trademarks of Emulex. * | ||
6 | * www.emulex.com * | ||
7 | * * | ||
8 | * This program is free software; you can redistribute it and/or * | ||
9 | * modify it under the terms of version 2 of the GNU General * | ||
10 | * Public License as published by the Free Software Foundation. * | ||
11 | * This program is distributed in the hope that it will be useful. * | ||
12 | * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * | ||
13 | * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * | ||
15 | * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * | ||
16 | * TO BE LEGALLY INVALID. See the GNU General Public License for * | ||
17 | * more details, a copy of which can be found in the file COPYING * | ||
18 | * included with this package. * | ||
19 | *******************************************************************/ | ||
20 | |||
21 | #include <linux/blkdev.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/dma-mapping.h> | ||
24 | #include <linux/idr.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/kthread.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/spinlock.h> | ||
29 | #include <linux/ctype.h> | ||
30 | #include <linux/version.h> | ||
31 | |||
32 | #include <scsi/scsi.h> | ||
33 | #include <scsi/scsi_device.h> | ||
34 | #include <scsi/scsi_host.h> | ||
35 | #include <scsi/scsi_transport_fc.h> | ||
36 | |||
37 | #include "lpfc_hw.h" | ||
38 | #include "lpfc_sli.h" | ||
39 | #include "lpfc_disc.h" | ||
40 | #include "lpfc_scsi.h" | ||
41 | #include "lpfc.h" | ||
42 | #include "lpfc_logmsg.h" | ||
43 | #include "lpfc_crtn.h" | ||
44 | #include "lpfc_vport.h" | ||
45 | #include "lpfc_version.h" | ||
46 | #include "lpfc_vport.h" | ||
47 | #include "lpfc_debugfs.h" | ||
48 | |||
49 | #ifdef CONFIG_LPFC_DEBUG_FS | ||
50 | /* debugfs interface | ||
51 | * | ||
52 | * To access this interface the user should: | ||
53 | * # mkdir /debug | ||
54 | * # mount -t debugfs none /debug | ||
55 | * | ||
56 | * The lpfc debugfs directory hierachy is: | ||
57 | * lpfc/lpfcX/vportY | ||
58 | * where X is the lpfc hba unique_id | ||
59 | * where Y is the vport VPI on that hba | ||
60 | * | ||
61 | * Debugging services available per vport: | ||
62 | * discovery_trace | ||
63 | * This is an ACSII readable file that contains a trace of the last | ||
64 | * lpfc_debugfs_max_disc_trc events that happened on a specific vport. | ||
65 | * See lpfc_debugfs.h for different categories of | ||
66 | * discovery events. To enable the discovery trace, the following | ||
67 | * module parameters must be set: | ||
68 | * lpfc_debugfs_enable=1 Turns on lpfc debugfs filesystem support | ||
69 | * lpfc_debugfs_max_disc_trc=X Where X is the event trace depth for | ||
70 | * EACH vport. X MUST also be a power of 2. | ||
71 | * lpfc_debugfs_mask_disc_trc=Y Where Y is an event mask as defined in | ||
72 | * lpfc_debugfs.h . | ||
73 | */ | ||
74 | static int lpfc_debugfs_enable = 0; | ||
75 | module_param(lpfc_debugfs_enable, int, 0); | ||
76 | MODULE_PARM_DESC(lpfc_debugfs_enable, "Enable debugfs services"); | ||
77 | |||
78 | static int lpfc_debugfs_max_disc_trc = 0; /* This MUST be a power of 2 */ | ||
79 | module_param(lpfc_debugfs_max_disc_trc, int, 0); | ||
80 | MODULE_PARM_DESC(lpfc_debugfs_max_disc_trc, | ||
81 | "Set debugfs discovery trace depth"); | ||
82 | |||
83 | static int lpfc_debugfs_mask_disc_trc = 0; | ||
84 | module_param(lpfc_debugfs_mask_disc_trc, int, 0); | ||
85 | MODULE_PARM_DESC(lpfc_debugfs_mask_disc_trc, | ||
86 | "Set debugfs discovery trace mask"); | ||
87 | |||
88 | #include <linux/debugfs.h> | ||
89 | |||
90 | /* size of discovery_trace output line */ | ||
91 | #define LPFC_DISC_TRC_ENTRY_SIZE 80 | ||
92 | |||
93 | /* nodelist output buffer size */ | ||
94 | #define LPFC_NODELIST_SIZE 8192 | ||
95 | #define LPFC_NODELIST_ENTRY_SIZE 120 | ||
96 | |||
97 | struct lpfc_debug { | ||
98 | char *buffer; | ||
99 | int len; | ||
100 | }; | ||
101 | |||
102 | atomic_t lpfc_debugfs_disc_trc_cnt = ATOMIC_INIT(0); | ||
103 | unsigned long lpfc_debugfs_start_time = 0L; | ||
104 | |||
105 | static int | ||
106 | lpfc_debugfs_disc_trc_data(struct lpfc_vport *vport, char *buf, int size) | ||
107 | { | ||
108 | int i, index, len, enable; | ||
109 | uint32_t ms; | ||
110 | struct lpfc_disc_trc *dtp; | ||
111 | char buffer[80]; | ||
112 | |||
113 | |||
114 | enable = lpfc_debugfs_enable; | ||
115 | lpfc_debugfs_enable = 0; | ||
116 | |||
117 | len = 0; | ||
118 | index = (atomic_read(&vport->disc_trc_cnt) + 1) & | ||
119 | (lpfc_debugfs_max_disc_trc - 1); | ||
120 | for (i = index; i < lpfc_debugfs_max_disc_trc; i++) { | ||
121 | dtp = vport->disc_trc + i; | ||
122 | if (!dtp->fmt) | ||
123 | continue; | ||
124 | ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time); | ||
125 | snprintf(buffer, 80, "%010d:%010d ms:%s\n", | ||
126 | dtp->seq_cnt, ms, dtp->fmt); | ||
127 | len += snprintf(buf+len, size-len, buffer, | ||
128 | dtp->data1, dtp->data2, dtp->data3); | ||
129 | } | ||
130 | for (i = 0; i < index; i++) { | ||
131 | dtp = vport->disc_trc + i; | ||
132 | if (!dtp->fmt) | ||
133 | continue; | ||
134 | ms = jiffies_to_msecs(dtp->jif - lpfc_debugfs_start_time); | ||
135 | snprintf(buffer, 80, "%010d:%010d ms:%s\n", | ||
136 | dtp->seq_cnt, ms, dtp->fmt); | ||
137 | len += snprintf(buf+len, size-len, buffer, | ||
138 | dtp->data1, dtp->data2, dtp->data3); | ||
139 | } | ||
140 | |||
141 | lpfc_debugfs_enable = enable; | ||
142 | return len; | ||
143 | } | ||
144 | |||
145 | static int | ||
146 | lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) | ||
147 | { | ||
148 | int len = 0; | ||
149 | int cnt; | ||
150 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | ||
151 | struct lpfc_nodelist *ndlp; | ||
152 | unsigned char *statep, *name; | ||
153 | |||
154 | cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE); | ||
155 | |||
156 | spin_lock_irq(shost->host_lock); | ||
157 | list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { | ||
158 | if (!cnt) { | ||
159 | len += snprintf(buf+len, size-len, | ||
160 | "Missing Nodelist Entries\n"); | ||
161 | break; | ||
162 | } | ||
163 | cnt--; | ||
164 | switch (ndlp->nlp_state) { | ||
165 | case NLP_STE_UNUSED_NODE: | ||
166 | statep = "UNUSED"; | ||
167 | break; | ||
168 | case NLP_STE_PLOGI_ISSUE: | ||
169 | statep = "PLOGI "; | ||
170 | break; | ||
171 | case NLP_STE_ADISC_ISSUE: | ||
172 | statep = "ADISC "; | ||
173 | break; | ||
174 | case NLP_STE_REG_LOGIN_ISSUE: | ||
175 | statep = "REGLOG"; | ||
176 | break; | ||
177 | case NLP_STE_PRLI_ISSUE: | ||
178 | statep = "PRLI "; | ||
179 | break; | ||
180 | case NLP_STE_UNMAPPED_NODE: | ||
181 | statep = "UNMAP "; | ||
182 | break; | ||
183 | case NLP_STE_MAPPED_NODE: | ||
184 | statep = "MAPPED"; | ||
185 | break; | ||
186 | case NLP_STE_NPR_NODE: | ||
187 | statep = "NPR "; | ||
188 | break; | ||
189 | default: | ||
190 | statep = "UNKNOWN"; | ||
191 | } | ||
192 | len += snprintf(buf+len, size-len, "%s DID:x%06x ", | ||
193 | statep, ndlp->nlp_DID); | ||
194 | name = (unsigned char *)&ndlp->nlp_portname; | ||
195 | len += snprintf(buf+len, size-len, | ||
196 | "WWPN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ", | ||
197 | *name, *(name+1), *(name+2), *(name+3), | ||
198 | *(name+4), *(name+5), *(name+6), *(name+7)); | ||
199 | name = (unsigned char *)&ndlp->nlp_nodename; | ||
200 | len += snprintf(buf+len, size-len, | ||
201 | "WWNN %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ", | ||
202 | *name, *(name+1), *(name+2), *(name+3), | ||
203 | *(name+4), *(name+5), *(name+6), *(name+7)); | ||
204 | len += snprintf(buf+len, size-len, "RPI:%03d flag:x%08x ", | ||
205 | ndlp->nlp_rpi, ndlp->nlp_flag); | ||
206 | if (!ndlp->nlp_type) | ||
207 | len += snprintf(buf+len, size-len, "UNKNOWN_TYPE"); | ||
208 | if (ndlp->nlp_type & NLP_FC_NODE) | ||
209 | len += snprintf(buf+len, size-len, "FC_NODE "); | ||
210 | if (ndlp->nlp_type & NLP_FABRIC) | ||
211 | len += snprintf(buf+len, size-len, "FABRIC "); | ||
212 | if (ndlp->nlp_type & NLP_FCP_TARGET) | ||
213 | len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ", | ||
214 | ndlp->nlp_sid); | ||
215 | if (ndlp->nlp_type & NLP_FCP_INITIATOR) | ||
216 | len += snprintf(buf+len, size-len, "FCP_INITIATOR"); | ||
217 | len += snprintf(buf+len, size-len, "\n"); | ||
218 | } | ||
219 | spin_unlock_irq(shost->host_lock); | ||
220 | return len; | ||
221 | } | ||
222 | #endif | ||
223 | |||
224 | |||
225 | inline void | ||
226 | lpfc_debugfs_disc_trc(struct lpfc_vport *vport, int mask, char *fmt, | ||
227 | uint32_t data1, uint32_t data2, uint32_t data3) | ||
228 | { | ||
229 | #ifdef CONFIG_LPFC_DEBUG_FS | ||
230 | struct lpfc_disc_trc *dtp; | ||
231 | int index; | ||
232 | |||
233 | if (!(lpfc_debugfs_mask_disc_trc & mask)) | ||
234 | return; | ||
235 | |||
236 | if (!lpfc_debugfs_enable || !lpfc_debugfs_max_disc_trc || | ||
237 | !vport || !vport->disc_trc) | ||
238 | return; | ||
239 | |||
240 | index = atomic_inc_return(&vport->disc_trc_cnt) & | ||
241 | (lpfc_debugfs_max_disc_trc - 1); | ||
242 | dtp = vport->disc_trc + index; | ||
243 | dtp->fmt = fmt; | ||
244 | dtp->data1 = data1; | ||
245 | dtp->data2 = data2; | ||
246 | dtp->data3 = data3; | ||
247 | dtp->seq_cnt = atomic_inc_return(&lpfc_debugfs_disc_trc_cnt); | ||
248 | dtp->jif = jiffies; | ||
249 | #endif | ||
250 | return; | ||
251 | } | ||
252 | |||
253 | #ifdef CONFIG_LPFC_DEBUG_FS | ||
254 | static int | ||
255 | lpfc_debugfs_disc_trc_open(struct inode *inode, struct file *file) | ||
256 | { | ||
257 | struct lpfc_vport *vport = inode->i_private; | ||
258 | struct lpfc_debug *debug; | ||
259 | int size; | ||
260 | int rc = -ENOMEM; | ||
261 | |||
262 | if (!lpfc_debugfs_max_disc_trc) { | ||
263 | rc = -ENOSPC; | ||
264 | goto out; | ||
265 | } | ||
266 | |||
267 | debug = kmalloc(sizeof(*debug), GFP_KERNEL); | ||
268 | if (!debug) | ||
269 | goto out; | ||
270 | |||
271 | /* Round to page boundry */ | ||
272 | size = (lpfc_debugfs_max_disc_trc * LPFC_DISC_TRC_ENTRY_SIZE); | ||
273 | size = PAGE_ALIGN(size); | ||
274 | |||
275 | debug->buffer = kmalloc(size, GFP_KERNEL); | ||
276 | if (!debug->buffer) { | ||
277 | kfree(debug); | ||
278 | goto out; | ||
279 | } | ||
280 | |||
281 | debug->len = lpfc_debugfs_disc_trc_data(vport, debug->buffer, size); | ||
282 | file->private_data = debug; | ||
283 | |||
284 | rc = 0; | ||
285 | out: | ||
286 | return rc; | ||
287 | } | ||
288 | |||
289 | static int | ||
290 | lpfc_debugfs_nodelist_open(struct inode *inode, struct file *file) | ||
291 | { | ||
292 | struct lpfc_vport *vport = inode->i_private; | ||
293 | struct lpfc_debug *debug; | ||
294 | int rc = -ENOMEM; | ||
295 | |||
296 | debug = kmalloc(sizeof(*debug), GFP_KERNEL); | ||
297 | if (!debug) | ||
298 | goto out; | ||
299 | |||
300 | /* Round to page boundry */ | ||
301 | debug->buffer = kmalloc(LPFC_NODELIST_SIZE, GFP_KERNEL); | ||
302 | if (!debug->buffer) { | ||
303 | kfree(debug); | ||
304 | goto out; | ||
305 | } | ||
306 | |||
307 | debug->len = lpfc_debugfs_nodelist_data(vport, debug->buffer, | ||
308 | LPFC_NODELIST_SIZE); | ||
309 | file->private_data = debug; | ||
310 | |||
311 | rc = 0; | ||
312 | out: | ||
313 | return rc; | ||
314 | } | ||
315 | |||
316 | static loff_t | ||
317 | lpfc_debugfs_lseek(struct file *file, loff_t off, int whence) | ||
318 | { | ||
319 | struct lpfc_debug *debug; | ||
320 | loff_t pos = -1; | ||
321 | |||
322 | debug = file->private_data; | ||
323 | |||
324 | switch (whence) { | ||
325 | case 0: | ||
326 | pos = off; | ||
327 | break; | ||
328 | case 1: | ||
329 | pos = file->f_pos + off; | ||
330 | break; | ||
331 | case 2: | ||
332 | pos = debug->len - off; | ||
333 | } | ||
334 | return (pos < 0 || pos > debug->len) ? -EINVAL : (file->f_pos = pos); | ||
335 | } | ||
336 | |||
337 | static ssize_t | ||
338 | lpfc_debugfs_read(struct file *file, char __user *buf, | ||
339 | size_t nbytes, loff_t *ppos) | ||
340 | { | ||
341 | struct lpfc_debug *debug = file->private_data; | ||
342 | return simple_read_from_buffer(buf, nbytes, ppos, debug->buffer, | ||
343 | debug->len); | ||
344 | } | ||
345 | |||
346 | static int | ||
347 | lpfc_debugfs_release(struct inode *inode, struct file *file) | ||
348 | { | ||
349 | struct lpfc_debug *debug = file->private_data; | ||
350 | |||
351 | kfree(debug->buffer); | ||
352 | kfree(debug); | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | #undef lpfc_debugfs_op_disc_trc | ||
358 | static struct file_operations lpfc_debugfs_op_disc_trc = { | ||
359 | .owner = THIS_MODULE, | ||
360 | .open = lpfc_debugfs_disc_trc_open, | ||
361 | .llseek = lpfc_debugfs_lseek, | ||
362 | .read = lpfc_debugfs_read, | ||
363 | .release = lpfc_debugfs_release, | ||
364 | }; | ||
365 | |||
366 | #undef lpfc_debugfs_op_nodelist | ||
367 | static struct file_operations lpfc_debugfs_op_nodelist = { | ||
368 | .owner = THIS_MODULE, | ||
369 | .open = lpfc_debugfs_nodelist_open, | ||
370 | .llseek = lpfc_debugfs_lseek, | ||
371 | .read = lpfc_debugfs_read, | ||
372 | .release = lpfc_debugfs_release, | ||
373 | }; | ||
374 | |||
375 | static struct dentry *lpfc_debugfs_root = NULL; | ||
376 | static atomic_t lpfc_debugfs_hba_count; | ||
377 | #endif | ||
378 | |||
379 | inline void | ||
380 | lpfc_debugfs_initialize(struct lpfc_vport *vport) | ||
381 | { | ||
382 | #ifdef CONFIG_LPFC_DEBUG_FS | ||
383 | struct lpfc_hba *phba = vport->phba; | ||
384 | char name[64]; | ||
385 | uint32_t num, i; | ||
386 | |||
387 | if (!lpfc_debugfs_enable) | ||
388 | return; | ||
389 | |||
390 | if (lpfc_debugfs_max_disc_trc) { | ||
391 | num = lpfc_debugfs_max_disc_trc - 1; | ||
392 | if (num & lpfc_debugfs_max_disc_trc) { | ||
393 | /* Change to be a power of 2 */ | ||
394 | num = lpfc_debugfs_max_disc_trc; | ||
395 | i = 0; | ||
396 | while (num > 1) { | ||
397 | num = num >> 1; | ||
398 | i++; | ||
399 | } | ||
400 | lpfc_debugfs_max_disc_trc = (1 << i); | ||
401 | printk(KERN_ERR | ||
402 | "lpfc_debugfs_max_disc_trc changed to %d\n", | ||
403 | lpfc_debugfs_max_disc_trc); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | if (!lpfc_debugfs_root) { | ||
408 | lpfc_debugfs_root = debugfs_create_dir("lpfc", NULL); | ||
409 | atomic_set(&lpfc_debugfs_hba_count, 0); | ||
410 | if (!lpfc_debugfs_root) | ||
411 | goto debug_failed; | ||
412 | } | ||
413 | |||
414 | snprintf(name, sizeof(name), "lpfc%d", phba->brd_no); | ||
415 | if (!phba->hba_debugfs_root) { | ||
416 | phba->hba_debugfs_root = | ||
417 | debugfs_create_dir(name, lpfc_debugfs_root); | ||
418 | if (!phba->hba_debugfs_root) | ||
419 | goto debug_failed; | ||
420 | atomic_inc(&lpfc_debugfs_hba_count); | ||
421 | atomic_set(&phba->debugfs_vport_count, 0); | ||
422 | } | ||
423 | |||
424 | snprintf(name, sizeof(name), "vport%d", vport->vpi); | ||
425 | if (!vport->vport_debugfs_root) { | ||
426 | vport->vport_debugfs_root = | ||
427 | debugfs_create_dir(name, phba->hba_debugfs_root); | ||
428 | if (!vport->vport_debugfs_root) | ||
429 | goto debug_failed; | ||
430 | atomic_inc(&phba->debugfs_vport_count); | ||
431 | } | ||
432 | |||
433 | if (!lpfc_debugfs_start_time) | ||
434 | lpfc_debugfs_start_time = jiffies; | ||
435 | |||
436 | vport->disc_trc = kmalloc( | ||
437 | (sizeof(struct lpfc_disc_trc) * lpfc_debugfs_max_disc_trc), | ||
438 | GFP_KERNEL); | ||
439 | |||
440 | if (!vport->disc_trc) | ||
441 | goto debug_failed; | ||
442 | memset(vport->disc_trc, 0, | ||
443 | (sizeof(struct lpfc_disc_trc) * lpfc_debugfs_max_disc_trc)); | ||
444 | |||
445 | snprintf(name, sizeof(name), "discovery_trace"); | ||
446 | vport->debug_disc_trc = | ||
447 | debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, | ||
448 | vport->vport_debugfs_root, | ||
449 | vport, &lpfc_debugfs_op_disc_trc); | ||
450 | if (!vport->debug_disc_trc) { | ||
451 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
452 | "%d:0409 Cannot create debugfs", | ||
453 | phba->brd_no); | ||
454 | goto debug_failed; | ||
455 | } | ||
456 | snprintf(name, sizeof(name), "nodelist"); | ||
457 | vport->debug_nodelist = | ||
458 | debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR, | ||
459 | vport->vport_debugfs_root, | ||
460 | vport, &lpfc_debugfs_op_nodelist); | ||
461 | if (!vport->debug_nodelist) { | ||
462 | lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | ||
463 | "%d:0409 Cannot create debugfs", | ||
464 | phba->brd_no); | ||
465 | goto debug_failed; | ||
466 | } | ||
467 | debug_failed: | ||
468 | return; | ||
469 | #endif | ||
470 | } | ||
471 | |||
472 | |||
473 | inline void | ||
474 | lpfc_debugfs_terminate(struct lpfc_vport *vport) | ||
475 | { | ||
476 | #ifdef CONFIG_LPFC_DEBUG_FS | ||
477 | struct lpfc_hba *phba = vport->phba; | ||
478 | |||
479 | if (vport->disc_trc) { | ||
480 | kfree(vport->disc_trc); | ||
481 | vport->disc_trc = NULL; | ||
482 | } | ||
483 | if (vport->debug_disc_trc) { | ||
484 | debugfs_remove(vport->debug_disc_trc); /* discovery_trace */ | ||
485 | vport->debug_disc_trc = NULL; | ||
486 | } | ||
487 | if (vport->debug_nodelist) { | ||
488 | debugfs_remove(vport->debug_nodelist); /* nodelist */ | ||
489 | vport->debug_nodelist = NULL; | ||
490 | } | ||
491 | if (vport->vport_debugfs_root) { | ||
492 | debugfs_remove(vport->vport_debugfs_root); /* vportX */ | ||
493 | vport->vport_debugfs_root = NULL; | ||
494 | atomic_dec(&phba->debugfs_vport_count); | ||
495 | } | ||
496 | if (atomic_read(&phba->debugfs_vport_count) == 0) { | ||
497 | debugfs_remove(vport->phba->hba_debugfs_root); /* lpfcX */ | ||
498 | vport->phba->hba_debugfs_root = NULL; | ||
499 | atomic_dec(&lpfc_debugfs_hba_count); | ||
500 | if (atomic_read(&lpfc_debugfs_hba_count) == 0) { | ||
501 | debugfs_remove(lpfc_debugfs_root); /* lpfc */ | ||
502 | lpfc_debugfs_root = NULL; | ||
503 | } | ||
504 | } | ||
505 | #endif | ||
506 | } | ||
507 | |||
508 | |||