diff options
| author | Joerg Roedel <joerg.roedel@amd.com> | 2009-06-09 04:50:57 -0400 |
|---|---|---|
| committer | Joerg Roedel <joerg.roedel@amd.com> | 2009-06-09 04:50:57 -0400 |
| commit | d2dd01de9924ae24afeba5aa5bc2e08287701df6 (patch) | |
| tree | 3021bf496579a48984666355b59df5e44b42dd32 /lib/dma-debug.c | |
| parent | 367d04c4ec02dad34d80452e32e3370db7fb6fee (diff) | |
| parent | 62a6f465f6572e1f28765c583c12753bb3e23715 (diff) | |
Merge commit 'tip/core/iommu' into amd-iommu/fixes
Diffstat (limited to 'lib/dma-debug.c')
| -rw-r--r-- | lib/dma-debug.c | 334 |
1 files changed, 308 insertions, 26 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index cdd205d6bf7c..77053d9ef513 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" }; |
| @@ -105,6 +117,11 @@ static const char *type2name[4] = { "single", "page", | |||
| 105 | static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", | 117 | static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", |
| 106 | "DMA_FROM_DEVICE", "DMA_NONE" }; | 118 | "DMA_FROM_DEVICE", "DMA_NONE" }; |
| 107 | 119 | ||
| 120 | /* little merge helper - remove it after the merge window */ | ||
| 121 | #ifndef BUS_NOTIFY_UNBOUND_DRIVER | ||
| 122 | #define BUS_NOTIFY_UNBOUND_DRIVER 0x0005 | ||
| 123 | #endif | ||
| 124 | |||
| 108 | /* | 125 | /* |
| 109 | * The access to some variables in this macro is racy. We can't use atomic_t | 126 | * The access to some variables in this macro is racy. We can't use atomic_t |
| 110 | * here because all these variables are exported to debugfs. Some of them even | 127 | * here because all these variables are exported to debugfs. Some of them even |
| @@ -128,9 +145,48 @@ static inline void dump_entry_trace(struct dma_debug_entry *entry) | |||
| 128 | #endif | 145 | #endif |
| 129 | } | 146 | } |
| 130 | 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 | |||
| 131 | #define err_printk(dev, entry, format, arg...) do { \ | 186 | #define err_printk(dev, entry, format, arg...) do { \ |
| 132 | error_count += 1; \ | 187 | error_count += 1; \ |
| 133 | if (show_all_errors || show_num_errors > 0) { \ | 188 | if (driver_filter(dev) && \ |
| 189 | (show_all_errors || show_num_errors > 0)) { \ | ||
| 134 | WARN(1, "%s %s: " format, \ | 190 | WARN(1, "%s %s: " format, \ |
| 135 | dev_driver_string(dev), \ | 191 | dev_driver_string(dev), \ |
| 136 | dev_name(dev) , ## arg); \ | 192 | dev_name(dev) , ## arg); \ |
| @@ -186,15 +242,50 @@ static void put_hash_bucket(struct hash_bucket *bucket, | |||
| 186 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, | 242 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, |
| 187 | struct dma_debug_entry *ref) | 243 | struct dma_debug_entry *ref) |
| 188 | { | 244 | { |
| 189 | struct dma_debug_entry *entry; | 245 | struct dma_debug_entry *entry, *ret = NULL; |
| 246 | int matches = 0, match_lvl, last_lvl = 0; | ||
| 190 | 247 | ||
| 191 | list_for_each_entry(entry, &bucket->list, list) { | 248 | list_for_each_entry(entry, &bucket->list, list) { |
| 192 | if ((entry->dev_addr == ref->dev_addr) && | 249 | if ((entry->dev_addr != ref->dev_addr) || |
| 193 | (entry->dev == ref->dev)) | 250 | (entry->dev != ref->dev)) |
| 251 | continue; | ||
| 252 | |||
| 253 | /* | ||
| 254 | * Some drivers map the same physical address multiple | ||
| 255 | * times. Without a hardware IOMMU this results in the | ||
| 256 | * same device addresses being put into the dma-debug | ||
| 257 | * hash multiple times too. This can result in false | ||
| 258 | * positives being reported. Therfore we implement a | ||
| 259 | * best-fit algorithm here which returns the entry from | ||
| 260 | * the hash which fits best to the reference value | ||
| 261 | * instead of the first-fit. | ||
| 262 | */ | ||
| 263 | matches += 1; | ||
| 264 | match_lvl = 0; | ||
| 265 | entry->size == ref->size ? ++match_lvl : match_lvl; | ||
| 266 | entry->type == ref->type ? ++match_lvl : match_lvl; | ||
| 267 | entry->direction == ref->direction ? ++match_lvl : match_lvl; | ||
| 268 | |||
| 269 | if (match_lvl == 3) { | ||
| 270 | /* perfect-fit - return the result */ | ||
| 194 | return entry; | 271 | return entry; |
| 272 | } else if (match_lvl > last_lvl) { | ||
| 273 | /* | ||
| 274 | * We found an entry that fits better then the | ||
| 275 | * previous one | ||
| 276 | */ | ||
| 277 | last_lvl = match_lvl; | ||
| 278 | ret = entry; | ||
| 279 | } | ||
| 195 | } | 280 | } |
| 196 | 281 | ||
| 197 | return NULL; | 282 | /* |
| 283 | * If we have multiple matches but no perfect-fit, just return | ||
| 284 | * NULL. | ||
| 285 | */ | ||
| 286 | ret = (matches == 1) ? ret : NULL; | ||
| 287 | |||
| 288 | return ret; | ||
| 198 | } | 289 | } |
| 199 | 290 | ||
| 200 | /* | 291 | /* |
| @@ -407,6 +498,97 @@ out_err: | |||
| 407 | return -ENOMEM; | 498 | return -ENOMEM; |
| 408 | } | 499 | } |
| 409 | 500 | ||
| 501 | static ssize_t filter_read(struct file *file, char __user *user_buf, | ||
| 502 | size_t count, loff_t *ppos) | ||
| 503 | { | ||
| 504 | unsigned long flags; | ||
| 505 | char buf[NAME_MAX_LEN + 1]; | ||
| 506 | int len; | ||
| 507 | |||
| 508 | if (!current_driver_name[0]) | ||
| 509 | return 0; | ||
| 510 | |||
| 511 | /* | ||
| 512 | * We can't copy to userspace directly because current_driver_name can | ||
| 513 | * only be read under the driver_name_lock with irqs disabled. So | ||
| 514 | * create a temporary copy first. | ||
| 515 | */ | ||
| 516 | read_lock_irqsave(&driver_name_lock, flags); | ||
| 517 | len = scnprintf(buf, NAME_MAX_LEN + 1, "%s\n", current_driver_name); | ||
| 518 | read_unlock_irqrestore(&driver_name_lock, flags); | ||
| 519 | |||
| 520 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
| 521 | } | ||
| 522 | |||
| 523 | static ssize_t filter_write(struct file *file, const char __user *userbuf, | ||
| 524 | size_t count, loff_t *ppos) | ||
| 525 | { | ||
| 526 | unsigned long flags; | ||
| 527 | char buf[NAME_MAX_LEN]; | ||
| 528 | size_t len = NAME_MAX_LEN - 1; | ||
| 529 | int i; | ||
| 530 | |||
| 531 | /* | ||
| 532 | * We can't copy from userspace directly. Access to | ||
| 533 | * current_driver_name is protected with a write_lock with irqs | ||
| 534 | * disabled. Since copy_from_user can fault and may sleep we | ||
| 535 | * need to copy to temporary buffer first | ||
| 536 | */ | ||
| 537 | len = min(count, len); | ||
| 538 | if (copy_from_user(buf, userbuf, len)) | ||
| 539 | return -EFAULT; | ||
| 540 | |||
| 541 | buf[len] = 0; | ||
| 542 | |||
| 543 | write_lock_irqsave(&driver_name_lock, flags); | ||
| 544 | |||
| 545 | /* Now handle the string we got from userspace very carefully. | ||
| 546 | * The rules are: | ||
| 547 | * - only use the first token we got | ||
| 548 | * - token delimiter is everything looking like a space | ||
| 549 | * character (' ', '\n', '\t' ...) | ||
| 550 | * | ||
| 551 | */ | ||
| 552 | if (!isalnum(buf[0])) { | ||
| 553 | /* | ||
| 554 | If the first character userspace gave us is not | ||
| 555 | * alphanumerical then assume the filter should be | ||
| 556 | * switched off. | ||
| 557 | */ | ||
| 558 | if (current_driver_name[0]) | ||
| 559 | printk(KERN_INFO "DMA-API: switching off dma-debug " | ||
| 560 | "driver filter\n"); | ||
| 561 | current_driver_name[0] = 0; | ||
| 562 | current_driver = NULL; | ||
| 563 | goto out_unlock; | ||
| 564 | } | ||
| 565 | |||
| 566 | /* | ||
| 567 | * Now parse out the first token and use it as the name for the | ||
| 568 | * driver to filter for. | ||
| 569 | */ | ||
| 570 | for (i = 0; i < NAME_MAX_LEN; ++i) { | ||
| 571 | current_driver_name[i] = buf[i]; | ||
| 572 | if (isspace(buf[i]) || buf[i] == ' ' || buf[i] == 0) | ||
| 573 | break; | ||
| 574 | } | ||
| 575 | current_driver_name[i] = 0; | ||
| 576 | current_driver = NULL; | ||
| 577 | |||
| 578 | printk(KERN_INFO "DMA-API: enable driver filter for driver [%s]\n", | ||
| 579 | current_driver_name); | ||
| 580 | |||
| 581 | out_unlock: | ||
| 582 | write_unlock_irqrestore(&driver_name_lock, flags); | ||
| 583 | |||
| 584 | return count; | ||
| 585 | } | ||
| 586 | |||
| 587 | const struct file_operations filter_fops = { | ||
| 588 | .read = filter_read, | ||
| 589 | .write = filter_write, | ||
| 590 | }; | ||
| 591 | |||
| 410 | static int dma_debug_fs_init(void) | 592 | static int dma_debug_fs_init(void) |
| 411 | { | 593 | { |
| 412 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); | 594 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); |
| @@ -450,6 +632,11 @@ static int dma_debug_fs_init(void) | |||
| 450 | if (!min_free_entries_dent) | 632 | if (!min_free_entries_dent) |
| 451 | goto out_err; | 633 | goto out_err; |
| 452 | 634 | ||
| 635 | filter_dent = debugfs_create_file("driver_filter", 0644, | ||
| 636 | dma_debug_dent, NULL, &filter_fops); | ||
| 637 | if (!filter_dent) | ||
| 638 | goto out_err; | ||
| 639 | |||
| 453 | return 0; | 640 | return 0; |
| 454 | 641 | ||
| 455 | out_err: | 642 | out_err: |
| @@ -458,9 +645,60 @@ out_err: | |||
| 458 | return -ENOMEM; | 645 | return -ENOMEM; |
| 459 | } | 646 | } |
| 460 | 647 | ||
| 648 | static int device_dma_allocations(struct device *dev) | ||
| 649 | { | ||
| 650 | struct dma_debug_entry *entry; | ||
| 651 | unsigned long flags; | ||
| 652 | int count = 0, i; | ||
| 653 | |||
| 654 | for (i = 0; i < HASH_SIZE; ++i) { | ||
| 655 | spin_lock_irqsave(&dma_entry_hash[i].lock, flags); | ||
| 656 | list_for_each_entry(entry, &dma_entry_hash[i].list, list) { | ||
| 657 | if (entry->dev == dev) | ||
| 658 | count += 1; | ||
| 659 | } | ||
| 660 | spin_unlock_irqrestore(&dma_entry_hash[i].lock, flags); | ||
| 661 | } | ||
| 662 | |||
| 663 | return count; | ||
| 664 | } | ||
| 665 | |||
| 666 | static int dma_debug_device_change(struct notifier_block *nb, | ||
| 667 | unsigned long action, void *data) | ||
| 668 | { | ||
| 669 | struct device *dev = data; | ||
| 670 | int count; | ||
| 671 | |||
| 672 | |||
| 673 | switch (action) { | ||
| 674 | case BUS_NOTIFY_UNBOUND_DRIVER: | ||
| 675 | count = device_dma_allocations(dev); | ||
| 676 | if (count == 0) | ||
| 677 | break; | ||
| 678 | err_printk(dev, NULL, "DMA-API: device driver has pending " | ||
| 679 | "DMA allocations while released from device " | ||
| 680 | "[count=%d]\n", count); | ||
| 681 | break; | ||
| 682 | default: | ||
| 683 | break; | ||
| 684 | } | ||
| 685 | |||
| 686 | return 0; | ||
| 687 | } | ||
| 688 | |||
| 461 | void dma_debug_add_bus(struct bus_type *bus) | 689 | void dma_debug_add_bus(struct bus_type *bus) |
| 462 | { | 690 | { |
| 463 | /* FIXME: register notifier */ | 691 | struct notifier_block *nb; |
| 692 | |||
| 693 | nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); | ||
| 694 | if (nb == NULL) { | ||
| 695 | printk(KERN_ERR "dma_debug_add_bus: out of memory\n"); | ||
| 696 | return; | ||
| 697 | } | ||
| 698 | |||
| 699 | nb->notifier_call = dma_debug_device_change; | ||
| 700 | |||
| 701 | bus_register_notifier(bus, nb); | ||
| 464 | } | 702 | } |
| 465 | 703 | ||
| 466 | /* | 704 | /* |
| @@ -783,15 +1021,15 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
| 783 | entry->type = dma_debug_sg; | 1021 | entry->type = dma_debug_sg; |
| 784 | entry->dev = dev; | 1022 | entry->dev = dev; |
| 785 | entry->paddr = sg_phys(s); | 1023 | entry->paddr = sg_phys(s); |
| 786 | entry->size = s->length; | 1024 | entry->size = sg_dma_len(s); |
| 787 | entry->dev_addr = s->dma_address; | 1025 | entry->dev_addr = sg_dma_address(s); |
| 788 | entry->direction = direction; | 1026 | entry->direction = direction; |
| 789 | entry->sg_call_ents = nents; | 1027 | entry->sg_call_ents = nents; |
| 790 | entry->sg_mapped_ents = mapped_ents; | 1028 | entry->sg_mapped_ents = mapped_ents; |
| 791 | 1029 | ||
| 792 | if (!PageHighMem(sg_page(s))) { | 1030 | if (!PageHighMem(sg_page(s))) { |
| 793 | check_for_stack(dev, sg_virt(s)); | 1031 | check_for_stack(dev, sg_virt(s)); |
| 794 | check_for_illegal_area(dev, sg_virt(s), s->length); | 1032 | check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); |
| 795 | } | 1033 | } |
| 796 | 1034 | ||
| 797 | add_dma_entry(entry); | 1035 | add_dma_entry(entry); |
| @@ -799,13 +1037,32 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
| 799 | } | 1037 | } |
| 800 | EXPORT_SYMBOL(debug_dma_map_sg); | 1038 | EXPORT_SYMBOL(debug_dma_map_sg); |
| 801 | 1039 | ||
| 1040 | static int get_nr_mapped_entries(struct device *dev, struct scatterlist *s) | ||
| 1041 | { | ||
| 1042 | struct dma_debug_entry *entry; | ||
| 1043 | struct hash_bucket *bucket; | ||
| 1044 | unsigned long flags; | ||
| 1045 | int mapped_ents = 0; | ||
| 1046 | struct dma_debug_entry ref; | ||
| 1047 | |||
| 1048 | ref.dev = dev; | ||
| 1049 | ref.dev_addr = sg_dma_address(s); | ||
| 1050 | ref.size = sg_dma_len(s), | ||
| 1051 | |||
| 1052 | bucket = get_hash_bucket(&ref, &flags); | ||
| 1053 | entry = hash_bucket_find(bucket, &ref); | ||
| 1054 | if (entry) | ||
| 1055 | mapped_ents = entry->sg_mapped_ents; | ||
| 1056 | put_hash_bucket(bucket, &flags); | ||
| 1057 | |||
| 1058 | return mapped_ents; | ||
| 1059 | } | ||
| 1060 | |||
| 802 | void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | 1061 | void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, |
| 803 | int nelems, int dir) | 1062 | int nelems, int dir) |
| 804 | { | 1063 | { |
| 805 | struct dma_debug_entry *entry; | ||
| 806 | struct scatterlist *s; | 1064 | struct scatterlist *s; |
| 807 | int mapped_ents = 0, i; | 1065 | int mapped_ents = 0, i; |
| 808 | unsigned long flags; | ||
| 809 | 1066 | ||
| 810 | if (unlikely(global_disable)) | 1067 | if (unlikely(global_disable)) |
| 811 | return; | 1068 | return; |
| @@ -816,8 +1073,8 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
| 816 | .type = dma_debug_sg, | 1073 | .type = dma_debug_sg, |
| 817 | .dev = dev, | 1074 | .dev = dev, |
| 818 | .paddr = sg_phys(s), | 1075 | .paddr = sg_phys(s), |
| 819 | .dev_addr = s->dma_address, | 1076 | .dev_addr = sg_dma_address(s), |
| 820 | .size = s->length, | 1077 | .size = sg_dma_len(s), |
| 821 | .direction = dir, | 1078 | .direction = dir, |
| 822 | .sg_call_ents = 0, | 1079 | .sg_call_ents = 0, |
| 823 | }; | 1080 | }; |
| @@ -825,14 +1082,9 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
| 825 | if (mapped_ents && i >= mapped_ents) | 1082 | if (mapped_ents && i >= mapped_ents) |
| 826 | break; | 1083 | break; |
| 827 | 1084 | ||
| 828 | if (mapped_ents == 0) { | 1085 | if (!i) { |
| 829 | struct hash_bucket *bucket; | ||
| 830 | ref.sg_call_ents = nelems; | 1086 | ref.sg_call_ents = nelems; |
| 831 | bucket = get_hash_bucket(&ref, &flags); | 1087 | mapped_ents = get_nr_mapped_entries(dev, s); |
| 832 | entry = hash_bucket_find(bucket, &ref); | ||
| 833 | if (entry) | ||
| 834 | mapped_ents = entry->sg_mapped_ents; | ||
| 835 | put_hash_bucket(bucket, &flags); | ||
| 836 | } | 1088 | } |
| 837 | 1089 | ||
| 838 | check_unmap(&ref); | 1090 | check_unmap(&ref); |
| @@ -934,14 +1186,20 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | |||
| 934 | int nelems, int direction) | 1186 | int nelems, int direction) |
| 935 | { | 1187 | { |
| 936 | struct scatterlist *s; | 1188 | struct scatterlist *s; |
| 937 | int i; | 1189 | int mapped_ents = 0, i; |
| 938 | 1190 | ||
| 939 | if (unlikely(global_disable)) | 1191 | if (unlikely(global_disable)) |
| 940 | return; | 1192 | return; |
| 941 | 1193 | ||
| 942 | for_each_sg(sg, s, nelems, i) { | 1194 | for_each_sg(sg, s, nelems, i) { |
| 943 | check_sync(dev, s->dma_address, s->dma_length, 0, | 1195 | if (!i) |
| 944 | direction, true); | 1196 | mapped_ents = get_nr_mapped_entries(dev, s); |
| 1197 | |||
| 1198 | if (i >= mapped_ents) | ||
| 1199 | break; | ||
| 1200 | |||
| 1201 | check_sync(dev, sg_dma_address(s), sg_dma_len(s), 0, | ||
| 1202 | direction, true); | ||
| 945 | } | 1203 | } |
| 946 | } | 1204 | } |
| 947 | EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu); | 1205 | EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu); |
| @@ -950,15 +1208,39 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |||
| 950 | int nelems, int direction) | 1208 | int nelems, int direction) |
| 951 | { | 1209 | { |
| 952 | struct scatterlist *s; | 1210 | struct scatterlist *s; |
| 953 | int i; | 1211 | int mapped_ents = 0, i; |
| 954 | 1212 | ||
| 955 | if (unlikely(global_disable)) | 1213 | if (unlikely(global_disable)) |
| 956 | return; | 1214 | return; |
| 957 | 1215 | ||
| 958 | for_each_sg(sg, s, nelems, i) { | 1216 | for_each_sg(sg, s, nelems, i) { |
| 959 | check_sync(dev, s->dma_address, s->dma_length, 0, | 1217 | if (!i) |
| 960 | direction, false); | 1218 | mapped_ents = get_nr_mapped_entries(dev, s); |
| 1219 | |||
| 1220 | if (i >= mapped_ents) | ||
| 1221 | break; | ||
| 1222 | |||
| 1223 | check_sync(dev, sg_dma_address(s), sg_dma_len(s), 0, | ||
| 1224 | direction, false); | ||
| 961 | } | 1225 | } |
| 962 | } | 1226 | } |
| 963 | EXPORT_SYMBOL(debug_dma_sync_sg_for_device); | 1227 | EXPORT_SYMBOL(debug_dma_sync_sg_for_device); |
| 964 | 1228 | ||
| 1229 | static int __init dma_debug_driver_setup(char *str) | ||
| 1230 | { | ||
| 1231 | int i; | ||
| 1232 | |||
| 1233 | for (i = 0; i < NAME_MAX_LEN - 1; ++i, ++str) { | ||
| 1234 | current_driver_name[i] = *str; | ||
| 1235 | if (*str == 0) | ||
| 1236 | break; | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | if (current_driver_name[0]) | ||
| 1240 | printk(KERN_INFO "DMA-API: enable driver filter for " | ||
| 1241 | "driver [%s]\n", current_driver_name); | ||
| 1242 | |||
| 1243 | |||
| 1244 | return 1; | ||
| 1245 | } | ||
| 1246 | __setup("dma_debug_driver=", dma_debug_driver_setup); | ||
