aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorThomas Maier <balagi@justmail.de>2006-12-08 05:36:12 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-12-08 11:28:38 -0500
commit32694850a91bd4fedcdd4a46292f870588be81d1 (patch)
tree370e1f97c9dff8f4feb71b8a1cf03cfe37b15d8b /drivers
parent0a0fc9601dd1024ec7171993bf075a789246e1ed (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')
-rw-r--r--drivers/block/pktcdvd.c443
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;
89static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */ 91static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */
90static mempool_t *psd_pool; 92static mempool_t *psd_pool;
91 93
94static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */
95static struct dentry *pkt_debugfs_root = NULL; /* /debug/pktcdvd */
96
97/* forward declaration */
98static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
99static int pkt_remove_dev(dev_t pkt_dev);
100static int pkt_seq_show(struct seq_file *m, void *p);
101
102
103
104/*
105 * create and register a pktcdvd kernel object.
106 */
107static 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 */
127static 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 */
135static 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
165DEF_ATTR(kobj_pkt_attr_st1, "reset", 0200);
166DEF_ATTR(kobj_pkt_attr_st2, "packets_started", 0444);
167DEF_ATTR(kobj_pkt_attr_st3, "packets_finished", 0444);
168DEF_ATTR(kobj_pkt_attr_st4, "kb_written", 0444);
169DEF_ATTR(kobj_pkt_attr_st5, "kb_read", 0444);
170DEF_ATTR(kobj_pkt_attr_st6, "kb_read_gather", 0444);
171
172static 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
182DEF_ATTR(kobj_pkt_attr_wq1, "size", 0444);
183DEF_ATTR(kobj_pkt_attr_wq2, "congestion_off", 0644);
184DEF_ATTR(kobj_pkt_attr_wq3, "congestion_on", 0644);
185
186static 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
202static 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
244static 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
261static 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
295static struct sysfs_ops kobj_pkt_ops = {
296 .show = kobj_pkt_show,
297 .store = kobj_pkt_store
298};
299static 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};
304static 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
310static 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
329static 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
345static void class_pktcdvd_release(struct class *cls)
346{
347 kfree(cls);
348}
349static 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
368static 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
380static 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
392static 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
400static 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
425static 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
440static int pkt_debugfs_seq_show(struct seq_file *m, void *p)
441{
442 return pkt_seq_show(m, p);
443}
444
445static 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
450static 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
458static 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
476static 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
488static 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
497static 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
93static void pkt_bio_finished(struct pktcdvd_device *pd) 508static 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
3126out_misc:
3127 pkt_debugfs_cleanup();
3128 pkt_sysfs_cleanup();
2698out: 3129out:
2699 unregister_blkdev(pktdev_major, DRIVER_NAME); 3130 unregister_blkdev(pktdev_major, DRIVER_NAME);
2700out2: 3131out2:
@@ -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}