diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-06-07 05:36:02 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-07 05:36:02 -0400 |
commit | 62a6f465f6572e1f28765c583c12753bb3e23715 (patch) | |
tree | 35ccf596b4abbeec9e1244f31e5b4e5d079899f5 /lib/dma-debug.c | |
parent | 56fdd18c7b89a2fac1dfe5d54750c9143867fdc4 (diff) | |
parent | bdc2911cde7d18580a545483844d75fdb3551729 (diff) |
Merge branch 'dma-debug/2.6.31' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/linux-2.6-iommu into core/iommu
Diffstat (limited to 'lib/dma-debug.c')
-rw-r--r-- | lib/dma-debug.c | 291 |
1 files changed, 269 insertions, 22 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 8fcc09c91e1b..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); \ |
@@ -442,6 +498,97 @@ out_err: | |||
442 | return -ENOMEM; | 498 | return -ENOMEM; |
443 | } | 499 | } |
444 | 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 | |||
445 | static int dma_debug_fs_init(void) | 592 | static int dma_debug_fs_init(void) |
446 | { | 593 | { |
447 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); | 594 | dma_debug_dent = debugfs_create_dir("dma-api", NULL); |
@@ -485,6 +632,11 @@ static int dma_debug_fs_init(void) | |||
485 | if (!min_free_entries_dent) | 632 | if (!min_free_entries_dent) |
486 | goto out_err; | 633 | goto out_err; |
487 | 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 | |||
488 | return 0; | 640 | return 0; |
489 | 641 | ||
490 | out_err: | 642 | out_err: |
@@ -493,9 +645,60 @@ out_err: | |||
493 | return -ENOMEM; | 645 | return -ENOMEM; |
494 | } | 646 | } |
495 | 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 | |||
496 | void dma_debug_add_bus(struct bus_type *bus) | 689 | void dma_debug_add_bus(struct bus_type *bus) |
497 | { | 690 | { |
498 | /* 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); | ||
499 | } | 702 | } |
500 | 703 | ||
501 | /* | 704 | /* |
@@ -818,15 +1021,15 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
818 | entry->type = dma_debug_sg; | 1021 | entry->type = dma_debug_sg; |
819 | entry->dev = dev; | 1022 | entry->dev = dev; |
820 | entry->paddr = sg_phys(s); | 1023 | entry->paddr = sg_phys(s); |
821 | entry->size = s->length; | 1024 | entry->size = sg_dma_len(s); |
822 | entry->dev_addr = s->dma_address; | 1025 | entry->dev_addr = sg_dma_address(s); |
823 | entry->direction = direction; | 1026 | entry->direction = direction; |
824 | entry->sg_call_ents = nents; | 1027 | entry->sg_call_ents = nents; |
825 | entry->sg_mapped_ents = mapped_ents; | 1028 | entry->sg_mapped_ents = mapped_ents; |
826 | 1029 | ||
827 | if (!PageHighMem(sg_page(s))) { | 1030 | if (!PageHighMem(sg_page(s))) { |
828 | check_for_stack(dev, sg_virt(s)); | 1031 | check_for_stack(dev, sg_virt(s)); |
829 | check_for_illegal_area(dev, sg_virt(s), s->length); | 1032 | check_for_illegal_area(dev, sg_virt(s), sg_dma_len(s)); |
830 | } | 1033 | } |
831 | 1034 | ||
832 | add_dma_entry(entry); | 1035 | add_dma_entry(entry); |
@@ -834,13 +1037,32 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
834 | } | 1037 | } |
835 | EXPORT_SYMBOL(debug_dma_map_sg); | 1038 | EXPORT_SYMBOL(debug_dma_map_sg); |
836 | 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 | |||
837 | void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | 1061 | void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, |
838 | int nelems, int dir) | 1062 | int nelems, int dir) |
839 | { | 1063 | { |
840 | struct dma_debug_entry *entry; | ||
841 | struct scatterlist *s; | 1064 | struct scatterlist *s; |
842 | int mapped_ents = 0, i; | 1065 | int mapped_ents = 0, i; |
843 | unsigned long flags; | ||
844 | 1066 | ||
845 | if (unlikely(global_disable)) | 1067 | if (unlikely(global_disable)) |
846 | return; | 1068 | return; |
@@ -851,8 +1073,8 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
851 | .type = dma_debug_sg, | 1073 | .type = dma_debug_sg, |
852 | .dev = dev, | 1074 | .dev = dev, |
853 | .paddr = sg_phys(s), | 1075 | .paddr = sg_phys(s), |
854 | .dev_addr = s->dma_address, | 1076 | .dev_addr = sg_dma_address(s), |
855 | .size = s->length, | 1077 | .size = sg_dma_len(s), |
856 | .direction = dir, | 1078 | .direction = dir, |
857 | .sg_call_ents = 0, | 1079 | .sg_call_ents = 0, |
858 | }; | 1080 | }; |
@@ -860,14 +1082,9 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
860 | if (mapped_ents && i >= mapped_ents) | 1082 | if (mapped_ents && i >= mapped_ents) |
861 | break; | 1083 | break; |
862 | 1084 | ||
863 | if (mapped_ents == 0) { | 1085 | if (!i) { |
864 | struct hash_bucket *bucket; | ||
865 | ref.sg_call_ents = nelems; | 1086 | ref.sg_call_ents = nelems; |
866 | bucket = get_hash_bucket(&ref, &flags); | 1087 | mapped_ents = get_nr_mapped_entries(dev, s); |
867 | entry = hash_bucket_find(bucket, &ref); | ||
868 | if (entry) | ||
869 | mapped_ents = entry->sg_mapped_ents; | ||
870 | put_hash_bucket(bucket, &flags); | ||
871 | } | 1088 | } |
872 | 1089 | ||
873 | check_unmap(&ref); | 1090 | check_unmap(&ref); |
@@ -969,14 +1186,20 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | |||
969 | int nelems, int direction) | 1186 | int nelems, int direction) |
970 | { | 1187 | { |
971 | struct scatterlist *s; | 1188 | struct scatterlist *s; |
972 | int i; | 1189 | int mapped_ents = 0, i; |
973 | 1190 | ||
974 | if (unlikely(global_disable)) | 1191 | if (unlikely(global_disable)) |
975 | return; | 1192 | return; |
976 | 1193 | ||
977 | for_each_sg(sg, s, nelems, i) { | 1194 | for_each_sg(sg, s, nelems, i) { |
978 | check_sync(dev, s->dma_address, s->dma_length, 0, | 1195 | if (!i) |
979 | 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); | ||
980 | } | 1203 | } |
981 | } | 1204 | } |
982 | EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu); | 1205 | EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu); |
@@ -985,15 +1208,39 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |||
985 | int nelems, int direction) | 1208 | int nelems, int direction) |
986 | { | 1209 | { |
987 | struct scatterlist *s; | 1210 | struct scatterlist *s; |
988 | int i; | 1211 | int mapped_ents = 0, i; |
989 | 1212 | ||
990 | if (unlikely(global_disable)) | 1213 | if (unlikely(global_disable)) |
991 | return; | 1214 | return; |
992 | 1215 | ||
993 | for_each_sg(sg, s, nelems, i) { | 1216 | for_each_sg(sg, s, nelems, i) { |
994 | check_sync(dev, s->dma_address, s->dma_length, 0, | 1217 | if (!i) |
995 | 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); | ||
996 | } | 1225 | } |
997 | } | 1226 | } |
998 | EXPORT_SYMBOL(debug_dma_sync_sg_for_device); | 1227 | EXPORT_SYMBOL(debug_dma_sync_sg_for_device); |
999 | 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); | ||