aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Haverkamp <haver@linux.vnet.ibm.com>2013-12-09 07:30:42 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-18 19:51:15 -0500
commitc59330cd7c15d550c4edbb8cd49d48e6d65ed47f (patch)
treee7447d7a2b6434f647ccc8c2b5429640463a49b3
parent67f4addbb8fe68b9de8e8042c5367bd17db36520 (diff)
GenWQE Debugfs interfaces
Debugfs interfaces for the GenWQE card. Help to debug potential problems. Dump internal chip state for debugging and failure determination. Signed-off-by: Frank Haverkamp <haver@linux.vnet.ibm.com> Co-authors: Joerg-Stephan Vogt <jsvogt@de.ibm.com>, Michael Jung <MIJUNG@de.ibm.com>, Michael Ruettger <michael@ibmra.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/ABI/testing/debugfs-driver-genwqe91
-rw-r--r--drivers/misc/genwqe/card_debugfs.c500
2 files changed, 591 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/debugfs-driver-genwqe b/Documentation/ABI/testing/debugfs-driver-genwqe
new file mode 100644
index 000000000000..1c2f25674e8c
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-driver-genwqe
@@ -0,0 +1,91 @@
1What: /sys/kernel/debug/genwqe/genwqe<n>_card/ddcb_info
2Date: Oct 2013
3Contact: haver@linux.vnet.ibm.com
4Description: DDCB queue dump used for debugging queueing problems.
5
6What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_regs
7Date: Oct 2013
8Contact: haver@linux.vnet.ibm.com
9Description: Dump of the current error registers.
10 Only available for PF.
11
12What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_dbg_uid0
13Date: Oct 2013
14Contact: haver@linux.vnet.ibm.com
15Description: Internal chip state of UID0 (unit id 0).
16 Only available for PF.
17
18What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_dbg_uid1
19Date: Oct 2013
20Contact: haver@linux.vnet.ibm.com
21Description: Internal chip state of UID1.
22 Only available for PF.
23
24What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_dbg_uid2
25Date: Oct 2013
26Contact: haver@linux.vnet.ibm.com
27Description: Internal chip state of UID2.
28 Only available for PF.
29
30What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_regs
31Date: Oct 2013
32Contact: haver@linux.vnet.ibm.com
33Description: Dump of the error registers before the last reset of
34 the card occured.
35 Only available for PF.
36
37What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_dbg_uid0
38Date: Oct 2013
39Contact: haver@linux.vnet.ibm.com
40Description: Internal chip state of UID0 before card was reset.
41 Only available for PF.
42
43What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_dbg_uid1
44Date: Oct 2013
45Contact: haver@linux.vnet.ibm.com
46Description: Internal chip state of UID1 before card was reset.
47 Only available for PF.
48
49What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_dbg_uid2
50Date: Oct 2013
51Contact: haver@linux.vnet.ibm.com
52Description: Internal chip state of UID2 before card was reset.
53 Only available for PF.
54
55What: /sys/kernel/debug/genwqe/genwqe<n>_card/info
56Date: Oct 2013
57Contact: haver@linux.vnet.ibm.com
58Description: Comprehensive summary of bitstream version and software
59 version. Used bitstream and bitstream clocking information.
60
61What: /sys/kernel/debug/genwqe/genwqe<n>_card/err_inject
62Date: Oct 2013
63Contact: haver@linux.vnet.ibm.com
64Description: Possibility to inject error cases to ensure that the drivers
65 error handling code works well.
66
67What: /sys/kernel/debug/genwqe/genwqe<n>_card/vf<0..14>_jobtimeout_msec
68Date: Oct 2013
69Contact: haver@linux.vnet.ibm.com
70Description: Default VF timeout 250ms. Testing might require 1000ms.
71 Using 0 will use the cards default value (whatever that is).
72
73 The timeout depends on the max number of available cards
74 in the system and the maximum allowed queue size.
75
76 The driver ensures that the settings are done just before
77 the VFs get enabled. Changing the timeouts in flight is not
78 possible.
79 Only available for PF.
80
81What: /sys/kernel/debug/genwqe/genwqe<n>_card/jobtimer
82Date: Oct 2013
83Contact: haver@linux.vnet.ibm.com
84Description: Dump job timeout register values for PF and VFs.
85 Only available for PF.
86
87What: /sys/kernel/debug/genwqe/genwqe<n>_card/queue_working_time
88Date: Dec 2013
89Contact: haver@linux.vnet.ibm.com
90Description: Dump queue working time register values for PF and VFs.
91 Only available for PF.
diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c
new file mode 100644
index 000000000000..3bfdc07a7248
--- /dev/null
+++ b/drivers/misc/genwqe/card_debugfs.c
@@ -0,0 +1,500 @@
1/**
2 * IBM Accelerator Family 'GenWQE'
3 *
4 * (C) Copyright IBM Corp. 2013
5 *
6 * Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
7 * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
8 * Author: Michael Jung <mijung@de.ibm.com>
9 * Author: Michael Ruettger <michael@ibmra.de>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License (version 2 only)
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 */
20
21/*
22 * Debugfs interfaces for the GenWQE card. Help to debug potential
23 * problems. Dump internal chip state for debugging and failure
24 * determination.
25 */
26
27#include <linux/module.h>
28#include <linux/kernel.h>
29#include <linux/init.h>
30#include <linux/debugfs.h>
31#include <linux/seq_file.h>
32#include <linux/uaccess.h>
33
34#include "card_base.h"
35#include "card_ddcb.h"
36
37#define GENWQE_DEBUGFS_RO(_name, _showfn) \
38 static int genwqe_debugfs_##_name##_open(struct inode *inode, \
39 struct file *file) \
40 { \
41 return single_open(file, _showfn, inode->i_private); \
42 } \
43 static const struct file_operations genwqe_##_name##_fops = { \
44 .open = genwqe_debugfs_##_name##_open, \
45 .read = seq_read, \
46 .llseek = seq_lseek, \
47 .release = single_release, \
48 }
49
50static void dbg_uidn_show(struct seq_file *s, struct genwqe_reg *regs,
51 int entries)
52{
53 unsigned int i;
54 u32 v_hi, v_lo;
55
56 for (i = 0; i < entries; i++) {
57 v_hi = (regs[i].val >> 32) & 0xffffffff;
58 v_lo = (regs[i].val) & 0xffffffff;
59
60 seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x EXT_ERR_REC\n",
61 regs[i].addr, regs[i].idx, v_hi, v_lo);
62 }
63}
64
65static int curr_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
66{
67 struct genwqe_dev *cd = s->private;
68 int entries;
69 struct genwqe_reg *regs;
70
71 entries = genwqe_ffdc_buff_size(cd, uid);
72 if (entries < 0)
73 return -EINVAL;
74
75 if (entries == 0)
76 return 0;
77
78 regs = kcalloc(entries, sizeof(*regs), GFP_KERNEL);
79 if (regs == NULL)
80 return -ENOMEM;
81
82 genwqe_stop_traps(cd); /* halt the traps while dumping data */
83 genwqe_ffdc_buff_read(cd, uid, regs, entries);
84 genwqe_start_traps(cd);
85
86 dbg_uidn_show(s, regs, entries);
87 kfree(regs);
88 return 0;
89}
90
91static int genwqe_curr_dbg_uid0_show(struct seq_file *s, void *unused)
92{
93 return curr_dbg_uidn_show(s, unused, 0);
94}
95
96GENWQE_DEBUGFS_RO(curr_dbg_uid0, genwqe_curr_dbg_uid0_show);
97
98static int genwqe_curr_dbg_uid1_show(struct seq_file *s, void *unused)
99{
100 return curr_dbg_uidn_show(s, unused, 1);
101}
102
103GENWQE_DEBUGFS_RO(curr_dbg_uid1, genwqe_curr_dbg_uid1_show);
104
105static int genwqe_curr_dbg_uid2_show(struct seq_file *s, void *unused)
106{
107 return curr_dbg_uidn_show(s, unused, 2);
108}
109
110GENWQE_DEBUGFS_RO(curr_dbg_uid2, genwqe_curr_dbg_uid2_show);
111
112static int prev_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
113{
114 struct genwqe_dev *cd = s->private;
115
116 dbg_uidn_show(s, cd->ffdc[uid].regs, cd->ffdc[uid].entries);
117 return 0;
118}
119
120static int genwqe_prev_dbg_uid0_show(struct seq_file *s, void *unused)
121{
122 return prev_dbg_uidn_show(s, unused, 0);
123}
124
125GENWQE_DEBUGFS_RO(prev_dbg_uid0, genwqe_prev_dbg_uid0_show);
126
127static int genwqe_prev_dbg_uid1_show(struct seq_file *s, void *unused)
128{
129 return prev_dbg_uidn_show(s, unused, 1);
130}
131
132GENWQE_DEBUGFS_RO(prev_dbg_uid1, genwqe_prev_dbg_uid1_show);
133
134static int genwqe_prev_dbg_uid2_show(struct seq_file *s, void *unused)
135{
136 return prev_dbg_uidn_show(s, unused, 2);
137}
138
139GENWQE_DEBUGFS_RO(prev_dbg_uid2, genwqe_prev_dbg_uid2_show);
140
141static int genwqe_curr_regs_show(struct seq_file *s, void *unused)
142{
143 struct genwqe_dev *cd = s->private;
144 unsigned int i;
145 struct genwqe_reg *regs;
146
147 regs = kcalloc(GENWQE_FFDC_REGS, sizeof(*regs), GFP_KERNEL);
148 if (regs == NULL)
149 return -ENOMEM;
150
151 genwqe_stop_traps(cd);
152 genwqe_read_ffdc_regs(cd, regs, GENWQE_FFDC_REGS, 1);
153 genwqe_start_traps(cd);
154
155 for (i = 0; i < GENWQE_FFDC_REGS; i++) {
156 if (regs[i].addr == 0xffffffff)
157 break; /* invalid entries */
158
159 if (regs[i].val == 0x0ull)
160 continue; /* do not print 0x0 FIRs */
161
162 seq_printf(s, " 0x%08x 0x%016llx\n",
163 regs[i].addr, regs[i].val);
164 }
165 return 0;
166}
167
168GENWQE_DEBUGFS_RO(curr_regs, genwqe_curr_regs_show);
169
170static int genwqe_prev_regs_show(struct seq_file *s, void *unused)
171{
172 struct genwqe_dev *cd = s->private;
173 unsigned int i;
174 struct genwqe_reg *regs = cd->ffdc[GENWQE_DBG_REGS].regs;
175
176 if (regs == NULL)
177 return -EINVAL;
178
179 for (i = 0; i < GENWQE_FFDC_REGS; i++) {
180 if (regs[i].addr == 0xffffffff)
181 break; /* invalid entries */
182
183 if (regs[i].val == 0x0ull)
184 continue; /* do not print 0x0 FIRs */
185
186 seq_printf(s, " 0x%08x 0x%016llx\n",
187 regs[i].addr, regs[i].val);
188 }
189 return 0;
190}
191
192GENWQE_DEBUGFS_RO(prev_regs, genwqe_prev_regs_show);
193
194static int genwqe_jtimer_show(struct seq_file *s, void *unused)
195{
196 struct genwqe_dev *cd = s->private;
197 unsigned int vf_num;
198 u64 jtimer;
199
200 jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT, 0);
201 seq_printf(s, " PF 0x%016llx %d msec\n", jtimer,
202 genwqe_pf_jobtimeout_msec);
203
204 for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) {
205 jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT,
206 vf_num + 1);
207 seq_printf(s, " VF%-2d 0x%016llx %d msec\n", vf_num, jtimer,
208 cd->vf_jobtimeout_msec[vf_num]);
209 }
210 return 0;
211}
212
213GENWQE_DEBUGFS_RO(jtimer, genwqe_jtimer_show);
214
215static int genwqe_queue_working_time_show(struct seq_file *s, void *unused)
216{
217 struct genwqe_dev *cd = s->private;
218 unsigned int vf_num;
219 u64 t;
220
221 t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, 0);
222 seq_printf(s, " PF 0x%016llx\n", t);
223
224 for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) {
225 t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, vf_num + 1);
226 seq_printf(s, " VF%-2d 0x%016llx\n", vf_num, t);
227 }
228 return 0;
229}
230
231GENWQE_DEBUGFS_RO(queue_working_time, genwqe_queue_working_time_show);
232
233static int genwqe_ddcb_info_show(struct seq_file *s, void *unused)
234{
235 struct genwqe_dev *cd = s->private;
236 unsigned int i;
237 struct ddcb_queue *queue;
238 struct ddcb *pddcb;
239
240 queue = &cd->queue;
241 seq_puts(s, "DDCB QUEUE:\n");
242 seq_printf(s, " ddcb_max: %d\n"
243 " ddcb_daddr: %016llx - %016llx\n"
244 " ddcb_vaddr: %016llx\n"
245 " ddcbs_in_flight: %u\n"
246 " ddcbs_max_in_flight: %u\n"
247 " ddcbs_completed: %u\n"
248 " busy: %u\n"
249 " irqs_processed: %u\n",
250 queue->ddcb_max, (long long)queue->ddcb_daddr,
251 (long long)queue->ddcb_daddr +
252 (queue->ddcb_max * DDCB_LENGTH),
253 (long long)queue->ddcb_vaddr, queue->ddcbs_in_flight,
254 queue->ddcbs_max_in_flight, queue->ddcbs_completed,
255 queue->busy, cd->irqs_processed);
256
257 /* Hardware State */
258 seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n"
259 " 0x%08x 0x%016llx IO_QUEUE_STATUS\n"
260 " 0x%08x 0x%016llx IO_QUEUE_SEGMENT\n"
261 " 0x%08x 0x%016llx IO_QUEUE_INITSQN\n"
262 " 0x%08x 0x%016llx IO_QUEUE_WRAP\n"
263 " 0x%08x 0x%016llx IO_QUEUE_OFFSET\n"
264 " 0x%08x 0x%016llx IO_QUEUE_WTIME\n"
265 " 0x%08x 0x%016llx IO_QUEUE_ERRCNTS\n"
266 " 0x%08x 0x%016llx IO_QUEUE_LRW\n",
267 queue->IO_QUEUE_CONFIG,
268 __genwqe_readq(cd, queue->IO_QUEUE_CONFIG),
269 queue->IO_QUEUE_STATUS,
270 __genwqe_readq(cd, queue->IO_QUEUE_STATUS),
271 queue->IO_QUEUE_SEGMENT,
272 __genwqe_readq(cd, queue->IO_QUEUE_SEGMENT),
273 queue->IO_QUEUE_INITSQN,
274 __genwqe_readq(cd, queue->IO_QUEUE_INITSQN),
275 queue->IO_QUEUE_WRAP,
276 __genwqe_readq(cd, queue->IO_QUEUE_WRAP),
277 queue->IO_QUEUE_OFFSET,
278 __genwqe_readq(cd, queue->IO_QUEUE_OFFSET),
279 queue->IO_QUEUE_WTIME,
280 __genwqe_readq(cd, queue->IO_QUEUE_WTIME),
281 queue->IO_QUEUE_ERRCNTS,
282 __genwqe_readq(cd, queue->IO_QUEUE_ERRCNTS),
283 queue->IO_QUEUE_LRW,
284 __genwqe_readq(cd, queue->IO_QUEUE_LRW));
285
286 seq_printf(s, "DDCB list (ddcb_act=%d/ddcb_next=%d):\n",
287 queue->ddcb_act, queue->ddcb_next);
288
289 pddcb = queue->ddcb_vaddr;
290 for (i = 0; i < queue->ddcb_max; i++) {
291 seq_printf(s, " %-3d: RETC=%03x SEQ=%04x HSI/SHI=%02x/%02x ",
292 i, be16_to_cpu(pddcb->retc_16),
293 be16_to_cpu(pddcb->seqnum_16),
294 pddcb->hsi, pddcb->shi);
295 seq_printf(s, "PRIV=%06llx CMD=%02x\n",
296 be64_to_cpu(pddcb->priv_64), pddcb->cmd);
297 pddcb++;
298 }
299 return 0;
300}
301
302GENWQE_DEBUGFS_RO(ddcb_info, genwqe_ddcb_info_show);
303
304static int genwqe_info_show(struct seq_file *s, void *unused)
305{
306 struct genwqe_dev *cd = s->private;
307 u16 val16, type;
308 u64 app_id, slu_id, bitstream = -1;
309 struct pci_dev *pci_dev = cd->pci_dev;
310
311 slu_id = __genwqe_readq(cd, IO_SLU_UNITCFG);
312 app_id = __genwqe_readq(cd, IO_APP_UNITCFG);
313
314 if (genwqe_is_privileged(cd))
315 bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM);
316
317 val16 = (u16)(slu_id & 0x0fLLU);
318 type = (u16)((slu_id >> 20) & 0xffLLU);
319
320 seq_printf(s, "%s driver version: %s\n"
321 " Device Name/Type: %s %s CardIdx: %d\n"
322 " SLU/APP Config : 0x%016llx/0x%016llx\n"
323 " Build Date : %u/%x/%u\n"
324 " Base Clock : %u MHz\n"
325 " Arch/SVN Release: %u/%llx\n"
326 " Bitstream : %llx\n",
327 GENWQE_DEVNAME, DRV_VERS_STRING, dev_name(&pci_dev->dev),
328 genwqe_is_privileged(cd) ?
329 "Physical" : "Virtual or no SR-IOV",
330 cd->card_idx, slu_id, app_id,
331 (u16)((slu_id >> 12) & 0x0fLLU), /* month */
332 (u16)((slu_id >> 4) & 0xffLLU), /* day */
333 (u16)((slu_id >> 16) & 0x0fLLU) + 2010, /* year */
334 genwqe_base_clock_frequency(cd),
335 (u16)((slu_id >> 32) & 0xffLLU), slu_id >> 40,
336 bitstream);
337
338 return 0;
339}
340
341GENWQE_DEBUGFS_RO(info, genwqe_info_show);
342
343int genwqe_init_debugfs(struct genwqe_dev *cd)
344{
345 struct dentry *root;
346 struct dentry *file;
347 int ret;
348 char card_name[64];
349 char name[64];
350 unsigned int i;
351
352 sprintf(card_name, "%s%u_card", GENWQE_DEVNAME, cd->card_idx);
353
354 root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
355 if (!root) {
356 ret = -ENOMEM;
357 goto err0;
358 }
359
360 /* non privileged interfaces are done here */
361 file = debugfs_create_file("ddcb_info", S_IRUGO, root, cd,
362 &genwqe_ddcb_info_fops);
363 if (!file) {
364 ret = -ENOMEM;
365 goto err1;
366 }
367
368 file = debugfs_create_file("info", S_IRUGO, root, cd,
369 &genwqe_info_fops);
370 if (!file) {
371 ret = -ENOMEM;
372 goto err1;
373 }
374
375 file = debugfs_create_x64("err_inject", 0666, root, &cd->err_inject);
376 if (!file) {
377 ret = -ENOMEM;
378 goto err1;
379 }
380
381 file = debugfs_create_u32("ddcb_software_timeout", 0666, root,
382 &cd->ddcb_software_timeout);
383 if (!file) {
384 ret = -ENOMEM;
385 goto err1;
386 }
387
388 file = debugfs_create_u32("kill_timeout", 0666, root,
389 &cd->kill_timeout);
390 if (!file) {
391 ret = -ENOMEM;
392 goto err1;
393 }
394
395 /* privileged interfaces follow here */
396 if (!genwqe_is_privileged(cd)) {
397 cd->debugfs_root = root;
398 return 0;
399 }
400
401 file = debugfs_create_file("curr_regs", S_IRUGO, root, cd,
402 &genwqe_curr_regs_fops);
403 if (!file) {
404 ret = -ENOMEM;
405 goto err1;
406 }
407
408 file = debugfs_create_file("curr_dbg_uid0", S_IRUGO, root, cd,
409 &genwqe_curr_dbg_uid0_fops);
410 if (!file) {
411 ret = -ENOMEM;
412 goto err1;
413 }
414
415 file = debugfs_create_file("curr_dbg_uid1", S_IRUGO, root, cd,
416 &genwqe_curr_dbg_uid1_fops);
417 if (!file) {
418 ret = -ENOMEM;
419 goto err1;
420 }
421
422 file = debugfs_create_file("curr_dbg_uid2", S_IRUGO, root, cd,
423 &genwqe_curr_dbg_uid2_fops);
424 if (!file) {
425 ret = -ENOMEM;
426 goto err1;
427 }
428
429 file = debugfs_create_file("prev_regs", S_IRUGO, root, cd,
430 &genwqe_prev_regs_fops);
431 if (!file) {
432 ret = -ENOMEM;
433 goto err1;
434 }
435
436 file = debugfs_create_file("prev_dbg_uid0", S_IRUGO, root, cd,
437 &genwqe_prev_dbg_uid0_fops);
438 if (!file) {
439 ret = -ENOMEM;
440 goto err1;
441 }
442
443 file = debugfs_create_file("prev_dbg_uid1", S_IRUGO, root, cd,
444 &genwqe_prev_dbg_uid1_fops);
445 if (!file) {
446 ret = -ENOMEM;
447 goto err1;
448 }
449
450 file = debugfs_create_file("prev_dbg_uid2", S_IRUGO, root, cd,
451 &genwqe_prev_dbg_uid2_fops);
452 if (!file) {
453 ret = -ENOMEM;
454 goto err1;
455 }
456
457 for (i = 0; i < GENWQE_MAX_VFS; i++) {
458 sprintf(name, "vf%d_jobtimeout_msec", i);
459
460 file = debugfs_create_u32(name, 0666, root,
461 &cd->vf_jobtimeout_msec[i]);
462 if (!file) {
463 ret = -ENOMEM;
464 goto err1;
465 }
466 }
467
468 file = debugfs_create_file("jobtimer", S_IRUGO, root, cd,
469 &genwqe_jtimer_fops);
470 if (!file) {
471 ret = -ENOMEM;
472 goto err1;
473 }
474
475 file = debugfs_create_file("queue_working_time", S_IRUGO, root, cd,
476 &genwqe_queue_working_time_fops);
477 if (!file) {
478 ret = -ENOMEM;
479 goto err1;
480 }
481
482 file = debugfs_create_u32("skip_recovery", 0666, root,
483 &cd->skip_recovery);
484 if (!file) {
485 ret = -ENOMEM;
486 goto err1;
487 }
488
489 cd->debugfs_root = root;
490 return 0;
491err1:
492 debugfs_remove_recursive(root);
493err0:
494 return ret;
495}
496
497void genqwe_exit_debugfs(struct genwqe_dev *cd)
498{
499 debugfs_remove_recursive(cd->debugfs_root);
500}