diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dma-debug.c | 102 |
1 files changed, 101 insertions, 1 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index c01f64780caf..c6330b1a7be7 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -23,9 +23,11 @@ | |||
23 | #include <linux/dma-debug.h> | 23 | #include <linux/dma-debug.h> |
24 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
25 | #include <linux/debugfs.h> | 25 | #include <linux/debugfs.h> |
26 | #include <linux/uaccess.h> | ||
26 | #include <linux/device.h> | 27 | #include <linux/device.h> |
27 | #include <linux/types.h> | 28 | #include <linux/types.h> |
28 | #include <linux/sched.h> | 29 | #include <linux/sched.h> |
30 | #include <linux/ctype.h> | ||
29 | #include <linux/list.h> | 31 | #include <linux/list.h> |
30 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
31 | 33 | ||
@@ -98,6 +100,7 @@ static struct dentry *show_all_errors_dent __read_mostly; | |||
98 | static struct dentry *show_num_errors_dent __read_mostly; | 100 | static struct dentry *show_num_errors_dent __read_mostly; |
99 | static struct dentry *num_free_entries_dent __read_mostly; | 101 | static struct dentry *num_free_entries_dent __read_mostly; |
100 | static struct dentry *min_free_entries_dent __read_mostly; | 102 | static struct dentry *min_free_entries_dent __read_mostly; |
103 | static struct dentry *filter_dent __read_mostly; | ||
101 | 104 | ||
102 | /* per-driver filter related state */ | 105 | /* per-driver filter related state */ |
103 | 106 | ||
@@ -160,7 +163,8 @@ static bool driver_filter(struct device *dev) | |||
160 | read_lock_irqsave(&driver_name_lock, flags); | 163 | read_lock_irqsave(&driver_name_lock, flags); |
161 | 164 | ||
162 | if (drv->name && | 165 | if (drv->name && |
163 | strncmp(current_driver_name, drv->name, 63) == 0) { | 166 | strncmp(current_driver_name, drv->name, |
167 | NAME_MAX_LEN-1) == 0) { | ||
164 | current_driver = drv; | 168 | current_driver = drv; |
165 | ret = true; | 169 | ret = true; |
166 | } | 170 | } |
@@ -454,6 +458,97 @@ out_err: | |||
454 | return -ENOMEM; | 458 | return -ENOMEM; |
455 | } | 459 | } |
456 | 460 | ||
461 | static ssize_t filter_read(struct file *file, char __user *user_buf, | ||
462 | size_t count, loff_t *ppos) | ||
463 | { | ||
464 | unsigned long flags; | ||
465 | char buf[NAME_MAX_LEN + 1]; | ||
466 | int len; | ||
467 | |||
468 | if (!current_driver_name[0]) | ||
469 | return 0; | ||
470 | |||
471 | /* | ||
472 | * We can't copy to userspace directly because current_driver_name can | ||
473 | * only be read under the driver_name_lock with irqs disabled. So | ||
474 | * create a temporary copy first. | ||
475 | */ | ||
476 | read_lock_irqsave(&driver_name_lock, flags); | ||
477 | len = scnprintf(buf, NAME_MAX_LEN + 1, "%s\n", current_driver_name); | ||
478 | read_unlock_irqrestore(&driver_name_lock, flags); | ||
479 | |||
480 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
481 | } | ||
482 | |||
483 | static ssize_t filter_write(struct file *file, const char __user *userbuf, | ||
484 | size_t count, loff_t *ppos) | ||
485 | { | ||
486 | unsigned long flags; | ||
487 | char buf[NAME_MAX_LEN]; | ||
488 | size_t len = NAME_MAX_LEN - 1; | ||
489 | int i; | ||
490 | |||
491 | /* | ||
492 | * We can't copy from userspace directly. Access to | ||
493 | * current_driver_name is protected with a write_lock with irqs | ||
494 | * disabled. Since copy_from_user can fault and may sleep we | ||
495 | * need to copy to temporary buffer first | ||
496 | */ | ||
497 | len = min(count, len); | ||
498 | if (copy_from_user(buf, userbuf, len)) | ||
499 | return -EFAULT; | ||
500 | |||
501 | buf[len] = 0; | ||
502 | |||
503 | write_lock_irqsave(&driver_name_lock, flags); | ||
504 | |||
505 | /* Now handle the string we got from userspace very carefully. | ||
506 | * The rules are: | ||
507 | * - only use the first token we got | ||
508 | * - token delimiter is everything looking like a space | ||
509 | * character (' ', '\n', '\t' ...) | ||
510 | * | ||
511 | */ | ||
512 | if (!isalnum(buf[0])) { | ||
513 | /* | ||
514 | If the first character userspace gave us is not | ||
515 | * alphanumerical then assume the filter should be | ||
516 | * switched off. | ||
517 | */ | ||
518 | if (current_driver_name[0]) | ||
519 | printk(KERN_INFO "DMA-API: switching off dma-debug " | ||
520 | "driver filter\n"); | ||
521 | current_driver_name[0] = 0; | ||
522 | current_driver = NULL; | ||
523 | goto out_unlock; | ||
524 | } | ||
525 | |||
526 | /* | ||
527 | * Now parse out the first token and use it as the name for the | ||
528 | * driver to filter for. | ||
529 | */ | ||
530 | for (i = 0; i < NAME_MAX_LEN; ++i) { | ||
531 | current_driver_name[i] = buf[i]; | ||
532 | if (isspace(buf[i]) || buf[i] == ' ' || buf[i] == 0) | ||
533 | break; | ||
534 | } | ||
535 | current_driver_name[i] = 0; | ||
536 | current_driver = NULL; | ||
537 | |||
538 | printk(KERN_INFO "DMA-API: enable driver filter for driver [%s]\n", | ||
539 | current_driver_name); | ||
540 | |||
541 | out_unlock: | ||
542 | write_unlock_irqrestore(&driver_name_lock, flags); | ||
543 | |||
544 | return count; | ||
545 | } | ||
546 | |||
547 | const struct file_operations filter_fops = { | ||
548 | .read = filter_read, | ||
549 | .write = filter_write, | ||
550 | }; | ||
551 | |||
457 | static int dma_debug_fs_init(void) | 552 | static int dma_debug_fs_init(void) |
458 | { | 553 | { |
459 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); | 554 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); |
@@ -497,6 +592,11 @@ static int dma_debug_fs_init(void) | |||
497 | if (!min_free_entries_dent) | 592 | if (!min_free_entries_dent) |
498 | goto out_err; | 593 | goto out_err; |
499 | 594 | ||
595 | filter_dent = debugfs_create_file("driver_filter", 0644, | ||
596 | dma_debug_dent, NULL, &filter_fops); | ||
597 | if (!filter_dent) | ||
598 | goto out_err; | ||
599 | |||
500 | return 0; | 600 | return 0; |
501 | 601 | ||
502 | out_err: | 602 | out_err: |