diff options
author | Thomas Maier <balagi@justmail.de> | 2006-12-08 05:36:12 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-08 11:28:38 -0500 |
commit | 32694850a91bd4fedcdd4a46292f870588be81d1 (patch) | |
tree | 370e1f97c9dff8f4feb71b8a1cf03cfe37b15d8b /drivers/block/pktcdvd.c | |
parent | 0a0fc9601dd1024ec7171993bf075a789246e1ed (diff) |
[PATCH] pktcdvd: add sysfs and debugfs interface
Add a sysfs and debugfs interface to the pktcdvd driver.
Look into the Documentation/ABI/testing/* files in the patch for more info.
Signed-off-by: Thomas Maier <balagi@justmail.de>
Signed-off-by: Peter Osterlund <petero2@telia.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/block/pktcdvd.c')
-rw-r--r-- | drivers/block/pktcdvd.c | 443 |
1 files changed, 439 insertions, 4 deletions
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 91fbe7fd6d0b..7c95c762950f 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c | |||
@@ -60,6 +60,8 @@ | |||
60 | #include <scsi/scsi_cmnd.h> | 60 | #include <scsi/scsi_cmnd.h> |
61 | #include <scsi/scsi_ioctl.h> | 61 | #include <scsi/scsi_ioctl.h> |
62 | #include <scsi/scsi.h> | 62 | #include <scsi/scsi.h> |
63 | #include <linux/debugfs.h> | ||
64 | #include <linux/device.h> | ||
63 | 65 | ||
64 | #include <asm/uaccess.h> | 66 | #include <asm/uaccess.h> |
65 | 67 | ||
@@ -89,6 +91,419 @@ static int write_congestion_off = PKT_WRITE_CONGESTION_OFF; | |||
89 | static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ | 91 | static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ |
90 | static mempool_t *psd_pool; | 92 | static mempool_t *psd_pool; |
91 | 93 | ||
94 | static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */ | ||
95 | static struct dentry *pkt_debugfs_root = NULL; /* /debug/pktcdvd */ | ||
96 | |||
97 | /* forward declaration */ | ||
98 | static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev); | ||
99 | static int pkt_remove_dev(dev_t pkt_dev); | ||
100 | static int pkt_seq_show(struct seq_file *m, void *p); | ||
101 | |||
102 | |||
103 | |||
104 | /* | ||
105 | * create and register a pktcdvd kernel object. | ||
106 | */ | ||
107 | static struct pktcdvd_kobj* pkt_kobj_create(struct pktcdvd_device *pd, | ||
108 | const char* name, | ||
109 | struct kobject* parent, | ||
110 | struct kobj_type* ktype) | ||
111 | { | ||
112 | struct pktcdvd_kobj *p; | ||
113 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
114 | if (!p) | ||
115 | return NULL; | ||
116 | kobject_set_name(&p->kobj, "%s", name); | ||
117 | p->kobj.parent = parent; | ||
118 | p->kobj.ktype = ktype; | ||
119 | p->pd = pd; | ||
120 | if (kobject_register(&p->kobj) != 0) | ||
121 | return NULL; | ||
122 | return p; | ||
123 | } | ||
124 | /* | ||
125 | * remove a pktcdvd kernel object. | ||
126 | */ | ||
127 | static void pkt_kobj_remove(struct pktcdvd_kobj *p) | ||
128 | { | ||
129 | if (p) | ||
130 | kobject_unregister(&p->kobj); | ||
131 | } | ||
132 | /* | ||
133 | * default release function for pktcdvd kernel objects. | ||
134 | */ | ||
135 | static void pkt_kobj_release(struct kobject *kobj) | ||
136 | { | ||
137 | kfree(to_pktcdvdkobj(kobj)); | ||
138 | } | ||
139 | |||
140 | |||
141 | /********************************************************** | ||
142 | * | ||
143 | * sysfs interface for pktcdvd | ||
144 | * by (C) 2006 Thomas Maier <balagi@justmail.de> | ||
145 | * | ||
146 | **********************************************************/ | ||
147 | |||
148 | #define DEF_ATTR(_obj,_name,_mode) \ | ||
149 | static struct attribute _obj = { \ | ||
150 | .name = _name, .owner = THIS_MODULE, .mode = _mode } | ||
151 | |||
152 | /********************************************************** | ||
153 | /sys/class/pktcdvd/pktcdvd[0-7]/ | ||
154 | stat/reset | ||
155 | stat/packets_started | ||
156 | stat/packets_finished | ||
157 | stat/kb_written | ||
158 | stat/kb_read | ||
159 | stat/kb_read_gather | ||
160 | write_queue/size | ||
161 | write_queue/congestion_off | ||
162 | write_queue/congestion_on | ||
163 | **********************************************************/ | ||
164 | |||
165 | DEF_ATTR(kobj_pkt_attr_st1, "reset", 0200); | ||
166 | DEF_ATTR(kobj_pkt_attr_st2, "packets_started", 0444); | ||
167 | DEF_ATTR(kobj_pkt_attr_st3, "packets_finished", 0444); | ||
168 | DEF_ATTR(kobj_pkt_attr_st4, "kb_written", 0444); | ||
169 | DEF_ATTR(kobj_pkt_attr_st5, "kb_read", 0444); | ||
170 | DEF_ATTR(kobj_pkt_attr_st6, "kb_read_gather", 0444); | ||
171 | |||
172 | static struct attribute *kobj_pkt_attrs_stat[] = { | ||
173 | &kobj_pkt_attr_st1, | ||
174 | &kobj_pkt_attr_st2, | ||
175 | &kobj_pkt_attr_st3, | ||
176 | &kobj_pkt_attr_st4, | ||
177 | &kobj_pkt_attr_st5, | ||
178 | &kobj_pkt_attr_st6, | ||
179 | NULL | ||
180 | }; | ||
181 | |||
182 | DEF_ATTR(kobj_pkt_attr_wq1, "size", 0444); | ||
183 | DEF_ATTR(kobj_pkt_attr_wq2, "congestion_off", 0644); | ||
184 | DEF_ATTR(kobj_pkt_attr_wq3, "congestion_on", 0644); | ||
185 | |||
186 | static struct attribute *kobj_pkt_attrs_wqueue[] = { | ||
187 | &kobj_pkt_attr_wq1, | ||
188 | &kobj_pkt_attr_wq2, | ||
189 | &kobj_pkt_attr_wq3, | ||
190 | NULL | ||
191 | }; | ||
192 | |||
193 | /* declares a char buffer[64] _dbuf, copies data from | ||
194 | * _b with length _l into it and ensures that _dbuf ends | ||
195 | * with a \0 character. | ||
196 | */ | ||
197 | #define DECLARE_BUF_AS_STRING(_dbuf, _b, _l) \ | ||
198 | char _dbuf[64]; int dlen = (_l) < 0 ? 0 : (_l); \ | ||
199 | if (dlen >= sizeof(_dbuf)) dlen = sizeof(_dbuf)-1; \ | ||
200 | memcpy(_dbuf, _b, dlen); _dbuf[dlen] = 0 | ||
201 | |||
202 | static ssize_t kobj_pkt_show(struct kobject *kobj, | ||
203 | struct attribute *attr, char *data) | ||
204 | { | ||
205 | struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd; | ||
206 | int n = 0; | ||
207 | int v; | ||
208 | if (strcmp(attr->name, "packets_started") == 0) { | ||
209 | n = sprintf(data, "%lu\n", pd->stats.pkt_started); | ||
210 | |||
211 | } else if (strcmp(attr->name, "packets_finished") == 0) { | ||
212 | n = sprintf(data, "%lu\n", pd->stats.pkt_ended); | ||
213 | |||
214 | } else if (strcmp(attr->name, "kb_written") == 0) { | ||
215 | n = sprintf(data, "%lu\n", pd->stats.secs_w >> 1); | ||
216 | |||
217 | } else if (strcmp(attr->name, "kb_read") == 0) { | ||
218 | n = sprintf(data, "%lu\n", pd->stats.secs_r >> 1); | ||
219 | |||
220 | } else if (strcmp(attr->name, "kb_read_gather") == 0) { | ||
221 | n = sprintf(data, "%lu\n", pd->stats.secs_rg >> 1); | ||
222 | |||
223 | } else if (strcmp(attr->name, "size") == 0) { | ||
224 | spin_lock(&pd->lock); | ||
225 | v = pd->bio_queue_size; | ||
226 | spin_unlock(&pd->lock); | ||
227 | n = sprintf(data, "%d\n", v); | ||
228 | |||
229 | } else if (strcmp(attr->name, "congestion_off") == 0) { | ||
230 | spin_lock(&pd->lock); | ||
231 | v = pd->write_congestion_off; | ||
232 | spin_unlock(&pd->lock); | ||
233 | n = sprintf(data, "%d\n", v); | ||
234 | |||
235 | } else if (strcmp(attr->name, "congestion_on") == 0) { | ||
236 | spin_lock(&pd->lock); | ||
237 | v = pd->write_congestion_on; | ||
238 | spin_unlock(&pd->lock); | ||
239 | n = sprintf(data, "%d\n", v); | ||
240 | } | ||
241 | return n; | ||
242 | } | ||
243 | |||
244 | static void init_write_congestion_marks(int* lo, int* hi) | ||
245 | { | ||
246 | if (*hi > 0) { | ||
247 | *hi = max(*hi, 500); | ||
248 | *hi = min(*hi, 1000000); | ||
249 | if (*lo <= 0) | ||
250 | *lo = *hi - 100; | ||
251 | else { | ||
252 | *lo = min(*lo, *hi - 100); | ||
253 | *lo = max(*lo, 100); | ||
254 | } | ||
255 | } else { | ||
256 | *hi = -1; | ||
257 | *lo = -1; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static ssize_t kobj_pkt_store(struct kobject *kobj, | ||
262 | struct attribute *attr, | ||
263 | const char *data, size_t len) | ||
264 | { | ||
265 | struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd; | ||
266 | int val; | ||
267 | DECLARE_BUF_AS_STRING(dbuf, data, len); /* ensure sscanf scans a string */ | ||
268 | |||
269 | if (strcmp(attr->name, "reset") == 0 && dlen > 0) { | ||
270 | pd->stats.pkt_started = 0; | ||
271 | pd->stats.pkt_ended = 0; | ||
272 | pd->stats.secs_w = 0; | ||
273 | pd->stats.secs_rg = 0; | ||
274 | pd->stats.secs_r = 0; | ||
275 | |||
276 | } else if (strcmp(attr->name, "congestion_off") == 0 | ||
277 | && sscanf(dbuf, "%d", &val) == 1) { | ||
278 | spin_lock(&pd->lock); | ||
279 | pd->write_congestion_off = val; | ||
280 | init_write_congestion_marks(&pd->write_congestion_off, | ||
281 | &pd->write_congestion_on); | ||
282 | spin_unlock(&pd->lock); | ||
283 | |||
284 | } else if (strcmp(attr->name, "congestion_on") == 0 | ||
285 | && sscanf(dbuf, "%d", &val) == 1) { | ||
286 | spin_lock(&pd->lock); | ||
287 | pd->write_congestion_on = val; | ||
288 | init_write_congestion_marks(&pd->write_congestion_off, | ||
289 | &pd->write_congestion_on); | ||
290 | spin_unlock(&pd->lock); | ||
291 | } | ||
292 | return len; | ||
293 | } | ||
294 | |||
295 | static struct sysfs_ops kobj_pkt_ops = { | ||
296 | .show = kobj_pkt_show, | ||
297 | .store = kobj_pkt_store | ||
298 | }; | ||
299 | static struct kobj_type kobj_pkt_type_stat = { | ||
300 | .release = pkt_kobj_release, | ||
301 | .sysfs_ops = &kobj_pkt_ops, | ||
302 | .default_attrs = kobj_pkt_attrs_stat | ||
303 | }; | ||
304 | static struct kobj_type kobj_pkt_type_wqueue = { | ||
305 | .release = pkt_kobj_release, | ||
306 | .sysfs_ops = &kobj_pkt_ops, | ||
307 | .default_attrs = kobj_pkt_attrs_wqueue | ||
308 | }; | ||
309 | |||
310 | static void pkt_sysfs_dev_new(struct pktcdvd_device *pd) | ||
311 | { | ||
312 | if (class_pktcdvd) { | ||
313 | pd->clsdev = class_device_create(class_pktcdvd, | ||
314 | NULL, pd->pkt_dev, | ||
315 | NULL, "%s", pd->name); | ||
316 | if (IS_ERR(pd->clsdev)) | ||
317 | pd->clsdev = NULL; | ||
318 | } | ||
319 | if (pd->clsdev) { | ||
320 | pd->kobj_stat = pkt_kobj_create(pd, "stat", | ||
321 | &pd->clsdev->kobj, | ||
322 | &kobj_pkt_type_stat); | ||
323 | pd->kobj_wqueue = pkt_kobj_create(pd, "write_queue", | ||
324 | &pd->clsdev->kobj, | ||
325 | &kobj_pkt_type_wqueue); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd) | ||
330 | { | ||
331 | pkt_kobj_remove(pd->kobj_stat); | ||
332 | pkt_kobj_remove(pd->kobj_wqueue); | ||
333 | if (class_pktcdvd) | ||
334 | class_device_destroy(class_pktcdvd, pd->pkt_dev); | ||
335 | } | ||
336 | |||
337 | |||
338 | /******************************************************************** | ||
339 | /sys/class/pktcdvd/ | ||
340 | add map block device | ||
341 | remove unmap packet dev | ||
342 | device_map show mappings | ||
343 | *******************************************************************/ | ||
344 | |||
345 | static void class_pktcdvd_release(struct class *cls) | ||
346 | { | ||
347 | kfree(cls); | ||
348 | } | ||
349 | static ssize_t class_pktcdvd_show_map(struct class *c, char *data) | ||
350 | { | ||
351 | int n = 0; | ||
352 | int idx; | ||
353 | mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); | ||
354 | for (idx = 0; idx < MAX_WRITERS; idx++) { | ||
355 | struct pktcdvd_device *pd = pkt_devs[idx]; | ||
356 | if (!pd) | ||
357 | continue; | ||
358 | n += sprintf(data+n, "%s %u:%u %u:%u\n", | ||
359 | pd->name, | ||
360 | MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev), | ||
361 | MAJOR(pd->bdev->bd_dev), | ||
362 | MINOR(pd->bdev->bd_dev)); | ||
363 | } | ||
364 | mutex_unlock(&ctl_mutex); | ||
365 | return n; | ||
366 | } | ||
367 | |||
368 | static ssize_t class_pktcdvd_store_add(struct class *c, const char *buf, | ||
369 | size_t count) | ||
370 | { | ||
371 | unsigned int major, minor; | ||
372 | DECLARE_BUF_AS_STRING(dbuf, buf, count); | ||
373 | if (sscanf(dbuf, "%u:%u", &major, &minor) == 2) { | ||
374 | pkt_setup_dev(MKDEV(major, minor), NULL); | ||
375 | return count; | ||
376 | } | ||
377 | return -EINVAL; | ||
378 | } | ||
379 | |||
380 | static ssize_t class_pktcdvd_store_remove(struct class *c, const char *buf, | ||
381 | size_t count) | ||
382 | { | ||
383 | unsigned int major, minor; | ||
384 | DECLARE_BUF_AS_STRING(dbuf, buf, count); | ||
385 | if (sscanf(dbuf, "%u:%u", &major, &minor) == 2) { | ||
386 | pkt_remove_dev(MKDEV(major, minor)); | ||
387 | return count; | ||
388 | } | ||
389 | return -EINVAL; | ||
390 | } | ||
391 | |||
392 | static struct class_attribute class_pktcdvd_attrs[] = { | ||
393 | __ATTR(add, 0200, NULL, class_pktcdvd_store_add), | ||
394 | __ATTR(remove, 0200, NULL, class_pktcdvd_store_remove), | ||
395 | __ATTR(device_map, 0444, class_pktcdvd_show_map, NULL), | ||
396 | __ATTR_NULL | ||
397 | }; | ||
398 | |||
399 | |||
400 | static int pkt_sysfs_init(void) | ||
401 | { | ||
402 | int ret = 0; | ||
403 | |||
404 | /* | ||
405 | * create control files in sysfs | ||
406 | * /sys/class/pktcdvd/... | ||
407 | */ | ||
408 | class_pktcdvd = kzalloc(sizeof(*class_pktcdvd), GFP_KERNEL); | ||
409 | if (!class_pktcdvd) | ||
410 | return -ENOMEM; | ||
411 | class_pktcdvd->name = DRIVER_NAME; | ||
412 | class_pktcdvd->owner = THIS_MODULE; | ||
413 | class_pktcdvd->class_release = class_pktcdvd_release; | ||
414 | class_pktcdvd->class_attrs = class_pktcdvd_attrs; | ||
415 | ret = class_register(class_pktcdvd); | ||
416 | if (ret) { | ||
417 | kfree(class_pktcdvd); | ||
418 | class_pktcdvd = NULL; | ||
419 | printk(DRIVER_NAME": failed to create class pktcdvd\n"); | ||
420 | return ret; | ||
421 | } | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static void pkt_sysfs_cleanup(void) | ||
426 | { | ||
427 | if (class_pktcdvd) | ||
428 | class_destroy(class_pktcdvd); | ||
429 | class_pktcdvd = NULL; | ||
430 | } | ||
431 | |||
432 | /******************************************************************** | ||
433 | entries in debugfs | ||
434 | |||
435 | /debugfs/pktcdvd[0-7]/ | ||
436 | info | ||
437 | |||
438 | *******************************************************************/ | ||
439 | |||
440 | static int pkt_debugfs_seq_show(struct seq_file *m, void *p) | ||
441 | { | ||
442 | return pkt_seq_show(m, p); | ||
443 | } | ||
444 | |||
445 | static int pkt_debugfs_fops_open(struct inode *inode, struct file *file) | ||
446 | { | ||
447 | return single_open(file, pkt_debugfs_seq_show, inode->i_private); | ||
448 | } | ||
449 | |||
450 | static struct file_operations debug_fops = { | ||
451 | .open = pkt_debugfs_fops_open, | ||
452 | .read = seq_read, | ||
453 | .llseek = seq_lseek, | ||
454 | .release = single_release, | ||
455 | .owner = THIS_MODULE, | ||
456 | }; | ||
457 | |||
458 | static void pkt_debugfs_dev_new(struct pktcdvd_device *pd) | ||
459 | { | ||
460 | if (!pkt_debugfs_root) | ||
461 | return; | ||
462 | pd->dfs_f_info = NULL; | ||
463 | pd->dfs_d_root = debugfs_create_dir(pd->name, pkt_debugfs_root); | ||
464 | if (IS_ERR(pd->dfs_d_root)) { | ||
465 | pd->dfs_d_root = NULL; | ||
466 | return; | ||
467 | } | ||
468 | pd->dfs_f_info = debugfs_create_file("info", S_IRUGO, | ||
469 | pd->dfs_d_root, pd, &debug_fops); | ||
470 | if (IS_ERR(pd->dfs_f_info)) { | ||
471 | pd->dfs_f_info = NULL; | ||
472 | return; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | static void pkt_debugfs_dev_remove(struct pktcdvd_device *pd) | ||
477 | { | ||
478 | if (!pkt_debugfs_root) | ||
479 | return; | ||
480 | if (pd->dfs_f_info) | ||
481 | debugfs_remove(pd->dfs_f_info); | ||
482 | pd->dfs_f_info = NULL; | ||
483 | if (pd->dfs_d_root) | ||
484 | debugfs_remove(pd->dfs_d_root); | ||
485 | pd->dfs_d_root = NULL; | ||
486 | } | ||
487 | |||
488 | static void pkt_debugfs_init(void) | ||
489 | { | ||
490 | pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL); | ||
491 | if (IS_ERR(pkt_debugfs_root)) { | ||
492 | pkt_debugfs_root = NULL; | ||
493 | return; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | static void pkt_debugfs_cleanup(void) | ||
498 | { | ||
499 | if (!pkt_debugfs_root) | ||
500 | return; | ||
501 | debugfs_remove(pkt_debugfs_root); | ||
502 | pkt_debugfs_root = NULL; | ||
503 | } | ||
504 | |||
505 | /* ----------------------------------------------------------*/ | ||
506 | |||
92 | 507 | ||
93 | static void pkt_bio_finished(struct pktcdvd_device *pd) | 508 | static void pkt_bio_finished(struct pktcdvd_device *pd) |
94 | { | 509 | { |
@@ -2527,6 +2942,9 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) | |||
2527 | 2942 | ||
2528 | add_disk(disk); | 2943 | add_disk(disk); |
2529 | 2944 | ||
2945 | pkt_sysfs_dev_new(pd); | ||
2946 | pkt_debugfs_dev_new(pd); | ||
2947 | |||
2530 | pkt_devs[idx] = pd; | 2948 | pkt_devs[idx] = pd; |
2531 | if (pkt_dev) | 2949 | if (pkt_dev) |
2532 | *pkt_dev = pd->pkt_dev; | 2950 | *pkt_dev = pd->pkt_dev; |
@@ -2577,6 +2995,11 @@ static int pkt_remove_dev(dev_t pkt_dev) | |||
2577 | if (!IS_ERR(pd->cdrw.thread)) | 2995 | if (!IS_ERR(pd->cdrw.thread)) |
2578 | kthread_stop(pd->cdrw.thread); | 2996 | kthread_stop(pd->cdrw.thread); |
2579 | 2997 | ||
2998 | pkt_devs[idx] = NULL; | ||
2999 | |||
3000 | pkt_debugfs_dev_remove(pd); | ||
3001 | pkt_sysfs_dev_remove(pd); | ||
3002 | |||
2580 | blkdev_put(pd->bdev); | 3003 | blkdev_put(pd->bdev); |
2581 | 3004 | ||
2582 | remove_proc_entry(pd->name, pkt_proc); | 3005 | remove_proc_entry(pd->name, pkt_proc); |
@@ -2586,7 +3009,6 @@ static int pkt_remove_dev(dev_t pkt_dev) | |||
2586 | blk_cleanup_queue(pd->disk->queue); | 3009 | blk_cleanup_queue(pd->disk->queue); |
2587 | put_disk(pd->disk); | 3010 | put_disk(pd->disk); |
2588 | 3011 | ||
2589 | pkt_devs[idx] = NULL; | ||
2590 | mempool_destroy(pd->rb_pool); | 3012 | mempool_destroy(pd->rb_pool); |
2591 | kfree(pd); | 3013 | kfree(pd); |
2592 | 3014 | ||
@@ -2670,6 +3092,8 @@ static int __init pkt_init(void) | |||
2670 | { | 3092 | { |
2671 | int ret; | 3093 | int ret; |
2672 | 3094 | ||
3095 | mutex_init(&ctl_mutex); | ||
3096 | |||
2673 | psd_pool = mempool_create_kmalloc_pool(PSD_POOL_SIZE, | 3097 | psd_pool = mempool_create_kmalloc_pool(PSD_POOL_SIZE, |
2674 | sizeof(struct packet_stacked_data)); | 3098 | sizeof(struct packet_stacked_data)); |
2675 | if (!psd_pool) | 3099 | if (!psd_pool) |
@@ -2683,18 +3107,25 @@ static int __init pkt_init(void) | |||
2683 | if (!pktdev_major) | 3107 | if (!pktdev_major) |
2684 | pktdev_major = ret; | 3108 | pktdev_major = ret; |
2685 | 3109 | ||
3110 | ret = pkt_sysfs_init(); | ||
3111 | if (ret) | ||
3112 | goto out; | ||
3113 | |||
3114 | pkt_debugfs_init(); | ||
3115 | |||
2686 | ret = misc_register(&pkt_misc); | 3116 | ret = misc_register(&pkt_misc); |
2687 | if (ret) { | 3117 | if (ret) { |
2688 | printk(DRIVER_NAME": Unable to register misc device\n"); | 3118 | printk(DRIVER_NAME": Unable to register misc device\n"); |
2689 | goto out; | 3119 | goto out_misc; |
2690 | } | 3120 | } |
2691 | 3121 | ||
2692 | mutex_init(&ctl_mutex); | ||
2693 | |||
2694 | pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver); | 3122 | pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver); |
2695 | 3123 | ||
2696 | return 0; | 3124 | return 0; |
2697 | 3125 | ||
3126 | out_misc: | ||
3127 | pkt_debugfs_cleanup(); | ||
3128 | pkt_sysfs_cleanup(); | ||
2698 | out: | 3129 | out: |
2699 | unregister_blkdev(pktdev_major, DRIVER_NAME); | 3130 | unregister_blkdev(pktdev_major, DRIVER_NAME); |
2700 | out2: | 3131 | out2: |
@@ -2706,6 +3137,10 @@ static void __exit pkt_exit(void) | |||
2706 | { | 3137 | { |
2707 | remove_proc_entry(DRIVER_NAME, proc_root_driver); | 3138 | remove_proc_entry(DRIVER_NAME, proc_root_driver); |
2708 | misc_deregister(&pkt_misc); | 3139 | misc_deregister(&pkt_misc); |
3140 | |||
3141 | pkt_debugfs_cleanup(); | ||
3142 | pkt_sysfs_cleanup(); | ||
3143 | |||
2709 | unregister_blkdev(pktdev_major, DRIVER_NAME); | 3144 | unregister_blkdev(pktdev_major, DRIVER_NAME); |
2710 | mempool_destroy(psd_pool); | 3145 | mempool_destroy(psd_pool); |
2711 | } | 3146 | } |