diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dma-debug.c | 167 |
1 files changed, 166 insertions, 1 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 1abed176d35a..f49ab22643b7 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,16 @@ 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; | ||
104 | |||
105 | /* per-driver filter related state */ | ||
106 | |||
107 | #define NAME_MAX_LEN 64 | ||
108 | |||
109 | static char current_driver_name[NAME_MAX_LEN] __read_mostly; | ||
110 | static struct device_driver *current_driver __read_mostly; | ||
111 | |||
112 | static DEFINE_RWLOCK(driver_name_lock); | ||
101 | 113 | ||
102 | static const char *type2name[4] = { "single", "page", | 114 | static const char *type2name[4] = { "single", "page", |
103 | "scather-gather", "coherent" }; | 115 | "scather-gather", "coherent" }; |
@@ -133,9 +145,48 @@ static inline void dump_entry_trace(struct dma_debug_entry *entry) | |||
133 | #endif | 145 | #endif |
134 | } | 146 | } |
135 | 147 | ||
148 | static bool driver_filter(struct device *dev) | ||
149 | { | ||
150 | /* driver filter off */ | ||
151 | if (likely(!current_driver_name[0])) | ||
152 | return true; | ||
153 | |||
154 | /* driver filter on and initialized */ | ||
155 | if (current_driver && dev->driver == current_driver) | ||
156 | return true; | ||
157 | |||
158 | /* driver filter on but not yet initialized */ | ||
159 | if (!current_driver && current_driver_name[0]) { | ||
160 | struct device_driver *drv = get_driver(dev->driver); | ||
161 | unsigned long flags; | ||
162 | bool ret = false; | ||
163 | |||
164 | if (!drv) | ||
165 | return false; | ||
166 | |||
167 | /* lock to protect against change of current_driver_name */ | ||
168 | read_lock_irqsave(&driver_name_lock, flags); | ||
169 | |||
170 | if (drv->name && | ||
171 | strncmp(current_driver_name, drv->name, | ||
172 | NAME_MAX_LEN-1) == 0) { | ||
173 | current_driver = drv; | ||
174 | ret = true; | ||
175 | } | ||
176 | |||
177 | read_unlock_irqrestore(&driver_name_lock, flags); | ||
178 | put_driver(drv); | ||
179 | |||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | return false; | ||
184 | } | ||
185 | |||
136 | #define err_printk(dev, entry, format, arg...) do { \ | 186 | #define err_printk(dev, entry, format, arg...) do { \ |
137 | error_count += 1; \ | 187 | error_count += 1; \ |
138 | if (show_all_errors || show_num_errors > 0) { \ | 188 | if (driver_filter(dev) && \ |
189 | (show_all_errors || show_num_errors > 0)) { \ | ||
139 | WARN(1, "%s %s: " format, \ | 190 | WARN(1, "%s %s: " format, \ |
140 | dev_driver_string(dev), \ | 191 | dev_driver_string(dev), \ |
141 | dev_name(dev) , ## arg); \ | 192 | dev_name(dev) , ## arg); \ |
@@ -412,6 +463,97 @@ out_err: | |||
412 | return -ENOMEM; | 463 | return -ENOMEM; |
413 | } | 464 | } |
414 | 465 | ||
466 | static ssize_t filter_read(struct file *file, char __user *user_buf, | ||
467 | size_t count, loff_t *ppos) | ||
468 | { | ||
469 | unsigned long flags; | ||
470 | char buf[NAME_MAX_LEN + 1]; | ||
471 | int len; | ||
472 | |||
473 | if (!current_driver_name[0]) | ||
474 | return 0; | ||
475 | |||
476 | /* | ||
477 | * We can't copy to userspace directly because current_driver_name can | ||
478 | * only be read under the driver_name_lock with irqs disabled. So | ||
479 | * create a temporary copy first. | ||
480 | */ | ||
481 | read_lock_irqsave(&driver_name_lock, flags); | ||
482 | len = scnprintf(buf, NAME_MAX_LEN + 1, "%s\n", current_driver_name); | ||
483 | read_unlock_irqrestore(&driver_name_lock, flags); | ||
484 | |||
485 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
486 | } | ||
487 | |||
488 | static ssize_t filter_write(struct file *file, const char __user *userbuf, | ||
489 | size_t count, loff_t *ppos) | ||
490 | { | ||
491 | unsigned long flags; | ||
492 | char buf[NAME_MAX_LEN]; | ||
493 | size_t len = NAME_MAX_LEN - 1; | ||
494 | int i; | ||
495 | |||
496 | /* | ||
497 | * We can't copy from userspace directly. Access to | ||
498 | * current_driver_name is protected with a write_lock with irqs | ||
499 | * disabled. Since copy_from_user can fault and may sleep we | ||
500 | * need to copy to temporary buffer first | ||
501 | */ | ||
502 | len = min(count, len); | ||
503 | if (copy_from_user(buf, userbuf, len)) | ||
504 | return -EFAULT; | ||
505 | |||
506 | buf[len] = 0; | ||
507 | |||
508 | write_lock_irqsave(&driver_name_lock, flags); | ||
509 | |||
510 | /* Now handle the string we got from userspace very carefully. | ||
511 | * The rules are: | ||
512 | * - only use the first token we got | ||
513 | * - token delimiter is everything looking like a space | ||
514 | * character (' ', '\n', '\t' ...) | ||
515 | * | ||
516 | */ | ||
517 | if (!isalnum(buf[0])) { | ||
518 | /* | ||
519 | If the first character userspace gave us is not | ||
520 | * alphanumerical then assume the filter should be | ||
521 | * switched off. | ||
522 | */ | ||
523 | if (current_driver_name[0]) | ||
524 | printk(KERN_INFO "DMA-API: switching off dma-debug " | ||
525 | "driver filter\n"); | ||
526 | current_driver_name[0] = 0; | ||
527 | current_driver = NULL; | ||
528 | goto out_unlock; | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * Now parse out the first token and use it as the name for the | ||
533 | * driver to filter for. | ||
534 | */ | ||
535 | for (i = 0; i < NAME_MAX_LEN; ++i) { | ||
536 | current_driver_name[i] = buf[i]; | ||
537 | if (isspace(buf[i]) || buf[i] == ' ' || buf[i] == 0) | ||
538 | break; | ||
539 | } | ||
540 | current_driver_name[i] = 0; | ||
541 | current_driver = NULL; | ||
542 | |||
543 | printk(KERN_INFO "DMA-API: enable driver filter for driver [%s]\n", | ||
544 | current_driver_name); | ||
545 | |||
546 | out_unlock: | ||
547 | write_unlock_irqrestore(&driver_name_lock, flags); | ||
548 | |||
549 | return count; | ||
550 | } | ||
551 | |||
552 | const struct file_operations filter_fops = { | ||
553 | .read = filter_read, | ||
554 | .write = filter_write, | ||
555 | }; | ||
556 | |||
415 | static int dma_debug_fs_init(void) | 557 | static int dma_debug_fs_init(void) |
416 | { | 558 | { |
417 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); | 559 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); |
@@ -455,6 +597,11 @@ static int dma_debug_fs_init(void) | |||
455 | if (!min_free_entries_dent) | 597 | if (!min_free_entries_dent) |
456 | goto out_err; | 598 | goto out_err; |
457 | 599 | ||
600 | filter_dent = debugfs_create_file("driver_filter", 0644, | ||
601 | dma_debug_dent, NULL, &filter_fops); | ||
602 | if (!filter_dent) | ||
603 | goto out_err; | ||
604 | |||
458 | return 0; | 605 | return 0; |
459 | 606 | ||
460 | out_err: | 607 | out_err: |
@@ -1044,3 +1191,21 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |||
1044 | } | 1191 | } |
1045 | EXPORT_SYMBOL(debug_dma_sync_sg_for_device); | 1192 | EXPORT_SYMBOL(debug_dma_sync_sg_for_device); |
1046 | 1193 | ||
1194 | static int __init dma_debug_driver_setup(char *str) | ||
1195 | { | ||
1196 | int i; | ||
1197 | |||
1198 | for (i = 0; i < NAME_MAX_LEN - 1; ++i, ++str) { | ||
1199 | current_driver_name[i] = *str; | ||
1200 | if (*str == 0) | ||
1201 | break; | ||
1202 | } | ||
1203 | |||
1204 | if (current_driver_name[0]) | ||
1205 | printk(KERN_INFO "DMA-API: enable driver filter for " | ||
1206 | "driver [%s]\n", current_driver_name); | ||
1207 | |||
1208 | |||
1209 | return 1; | ||
1210 | } | ||
1211 | __setup("dma_debug_driver=", dma_debug_driver_setup); | ||