diff options
| author | Joerg Roedel <joerg.roedel@amd.com> | 2009-05-22 15:23:13 -0400 |
|---|---|---|
| committer | Joerg Roedel <joerg.roedel@amd.com> | 2009-06-02 10:21:18 -0400 |
| commit | 8a6fc708b9bb48a79a385bdc2be0959ee2ab788d (patch) | |
| tree | 55db1582e9d14092855ef28a050fb1913d76667c /lib | |
| parent | 2e507d849f1834d3fe9aae71b7aabf4c2a3827b8 (diff) | |
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>
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: |
