Commit 8a6fc708b9bb48a79a385bdc2be0959ee2ab788d
1 parent
2e507d849f
Exists in
master
and in
4 other branches
dma-debug: add debugfs file for driver filter
This patch adds the dma-api/driver_filter file to debugfs. The root user can write a driver name into this file to see only dma-api errors for that particular driver in the kernel log. Writing an empty string to that file disables the driver filter. Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Showing 1 changed file with 101 additions and 1 deletions Side-by-side Diff
lib/dma-debug.c
... | ... | @@ -23,9 +23,11 @@ |
23 | 23 | #include <linux/dma-debug.h> |
24 | 24 | #include <linux/spinlock.h> |
25 | 25 | #include <linux/debugfs.h> |
26 | +#include <linux/uaccess.h> | |
26 | 27 | #include <linux/device.h> |
27 | 28 | #include <linux/types.h> |
28 | 29 | #include <linux/sched.h> |
30 | +#include <linux/ctype.h> | |
29 | 31 | #include <linux/list.h> |
30 | 32 | #include <linux/slab.h> |
31 | 33 | |
... | ... | @@ -98,6 +100,7 @@ |
98 | 100 | static struct dentry *show_num_errors_dent __read_mostly; |
99 | 101 | static struct dentry *num_free_entries_dent __read_mostly; |
100 | 102 | static struct dentry *min_free_entries_dent __read_mostly; |
103 | +static struct dentry *filter_dent __read_mostly; | |
101 | 104 | |
102 | 105 | /* per-driver filter related state */ |
103 | 106 | |
... | ... | @@ -160,7 +163,8 @@ |
160 | 163 | read_lock_irqsave(&driver_name_lock, flags); |
161 | 164 | |
162 | 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 | 168 | current_driver = drv; |
165 | 169 | ret = true; |
166 | 170 | } |
... | ... | @@ -454,6 +458,97 @@ |
454 | 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 | 552 | static int dma_debug_fs_init(void) |
458 | 553 | { |
459 | 554 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); |
... | ... | @@ -495,6 +590,11 @@ |
495 | 590 | dma_debug_dent, |
496 | 591 | &min_free_entries); |
497 | 592 | if (!min_free_entries_dent) |
593 | + goto out_err; | |
594 | + | |
595 | + filter_dent = debugfs_create_file("driver_filter", 0644, | |
596 | + dma_debug_dent, NULL, &filter_fops); | |
597 | + if (!filter_dent) | |
498 | 598 | goto out_err; |
499 | 599 | |
500 | 600 | return 0; |