diff options
| -rw-r--r-- | Documentation/DMA-API.txt | 12 | ||||
| -rw-r--r-- | Documentation/kernel-parameters.txt | 7 | ||||
| -rw-r--r-- | lib/dma-debug.c | 167 |
3 files changed, 185 insertions, 1 deletions
diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index d9aa43d78bcc..25fb8bcf32a2 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt | |||
| @@ -704,12 +704,24 @@ this directory the following files can currently be found: | |||
| 704 | The current number of free dma_debug_entries | 704 | The current number of free dma_debug_entries |
| 705 | in the allocator. | 705 | in the allocator. |
| 706 | 706 | ||
| 707 | dma-api/driver-filter | ||
| 708 | You can write a name of a driver into this file | ||
| 709 | to limit the debug output to requests from that | ||
| 710 | particular driver. Write an empty string to | ||
| 711 | that file to disable the filter and see | ||
| 712 | all errors again. | ||
| 713 | |||
| 707 | If you have this code compiled into your kernel it will be enabled by default. | 714 | If you have this code compiled into your kernel it will be enabled by default. |
| 708 | If you want to boot without the bookkeeping anyway you can provide | 715 | If you want to boot without the bookkeeping anyway you can provide |
| 709 | 'dma_debug=off' as a boot parameter. This will disable DMA-API debugging. | 716 | 'dma_debug=off' as a boot parameter. This will disable DMA-API debugging. |
| 710 | Notice that you can not enable it again at runtime. You have to reboot to do | 717 | Notice that you can not enable it again at runtime. You have to reboot to do |
| 711 | so. | 718 | so. |
| 712 | 719 | ||
| 720 | If you want to see debug messages only for a special device driver you can | ||
| 721 | specify the dma_debug_driver=<drivername> parameter. This will enable the | ||
| 722 | driver filter at boot time. The debug code will only print errors for that | ||
| 723 | driver afterwards. This filter can be disabled or changed later using debugfs. | ||
| 724 | |||
| 713 | When the code disables itself at runtime this is most likely because it ran | 725 | When the code disables itself at runtime this is most likely because it ran |
| 714 | out of dma_debug_entries. These entries are preallocated at boot. The number | 726 | out of dma_debug_entries. These entries are preallocated at boot. The number |
| 715 | of preallocated entries is defined per architecture. If it is too low for you | 727 | of preallocated entries is defined per architecture. If it is too low for you |
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e87bdbfbcc75..b3f1314588c9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -646,6 +646,13 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 646 | DMA-API debugging code disables itself because the | 646 | DMA-API debugging code disables itself because the |
| 647 | architectural default is too low. | 647 | architectural default is too low. |
| 648 | 648 | ||
| 649 | dma_debug_driver=<driver_name> | ||
| 650 | With this option the DMA-API debugging driver | ||
| 651 | filter feature can be enabled at boot time. Just | ||
| 652 | pass the driver to filter for as the parameter. | ||
| 653 | The filter can be disabled or changed to another | ||
| 654 | driver later using sysfs. | ||
| 655 | |||
| 649 | dscc4.setup= [NET] | 656 | dscc4.setup= [NET] |
| 650 | 657 | ||
| 651 | dtc3181e= [HW,SCSI] | 658 | dtc3181e= [HW,SCSI] |
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); | ||
