diff options
-rw-r--r-- | Documentation/DMA-API.txt | 12 | ||||
-rw-r--r-- | arch/x86/include/asm/dma-mapping.h | 1 | ||||
-rw-r--r-- | include/linux/dma-debug.h | 7 | ||||
-rw-r--r-- | lib/dma-debug.c | 71 |
4 files changed, 87 insertions, 4 deletions
diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt index 66bd97a95f10..78a6c569d204 100644 --- a/Documentation/DMA-API.txt +++ b/Documentation/DMA-API.txt | |||
@@ -678,3 +678,15 @@ out of dma_debug_entries. These entries are preallocated at boot. The number | |||
678 | of preallocated entries is defined per architecture. If it is too low for you | 678 | of preallocated entries is defined per architecture. If it is too low for you |
679 | boot with 'dma_debug_entries=<your_desired_number>' to overwrite the | 679 | boot with 'dma_debug_entries=<your_desired_number>' to overwrite the |
680 | architectural default. | 680 | architectural default. |
681 | |||
682 | void debug_dmap_mapping_error(struct device *dev, dma_addr_t dma_addr); | ||
683 | |||
684 | dma-debug interface debug_dma_mapping_error() to debug drivers that fail | ||
685 | to check dma mapping errors on addresses returned by dma_map_single() and | ||
686 | dma_map_page() interfaces. This interface clears a flag set by | ||
687 | debug_dma_map_page() to indicate that dma_mapping_error() has been called by | ||
688 | the driver. When driver does unmap, debug_dma_unmap() checks the flag and if | ||
689 | this flag is still set, prints warning message that includes call trace that | ||
690 | leads up to the unmap. This interface can be called from dma_mapping_error() | ||
691 | routines to enable dma mapping error check debugging. | ||
692 | |||
diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h index f7b4c7903e7e..808dae63eeea 100644 --- a/arch/x86/include/asm/dma-mapping.h +++ b/arch/x86/include/asm/dma-mapping.h | |||
@@ -47,6 +47,7 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) | |||
47 | static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) | 47 | static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) |
48 | { | 48 | { |
49 | struct dma_map_ops *ops = get_dma_ops(dev); | 49 | struct dma_map_ops *ops = get_dma_ops(dev); |
50 | debug_dma_mapping_error(dev, dma_addr); | ||
50 | if (ops->mapping_error) | 51 | if (ops->mapping_error) |
51 | return ops->mapping_error(dev, dma_addr); | 52 | return ops->mapping_error(dev, dma_addr); |
52 | 53 | ||
diff --git a/include/linux/dma-debug.h b/include/linux/dma-debug.h index 171ad8aedc83..fc0e34ce038f 100644 --- a/include/linux/dma-debug.h +++ b/include/linux/dma-debug.h | |||
@@ -39,6 +39,8 @@ extern void debug_dma_map_page(struct device *dev, struct page *page, | |||
39 | int direction, dma_addr_t dma_addr, | 39 | int direction, dma_addr_t dma_addr, |
40 | bool map_single); | 40 | bool map_single); |
41 | 41 | ||
42 | extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr); | ||
43 | |||
42 | extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, | 44 | extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, |
43 | size_t size, int direction, bool map_single); | 45 | size_t size, int direction, bool map_single); |
44 | 46 | ||
@@ -105,6 +107,11 @@ static inline void debug_dma_map_page(struct device *dev, struct page *page, | |||
105 | { | 107 | { |
106 | } | 108 | } |
107 | 109 | ||
110 | static inline void debug_dma_mapping_error(struct device *dev, | ||
111 | dma_addr_t dma_addr) | ||
112 | { | ||
113 | } | ||
114 | |||
108 | static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, | 115 | static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, |
109 | size_t size, int direction, | 116 | size_t size, int direction, |
110 | bool map_single) | 117 | bool map_single) |
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index d84beb994f36..59f4a1a8187d 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -45,6 +45,12 @@ enum { | |||
45 | dma_debug_coherent, | 45 | dma_debug_coherent, |
46 | }; | 46 | }; |
47 | 47 | ||
48 | enum map_err_types { | ||
49 | MAP_ERR_CHECK_NOT_APPLICABLE, | ||
50 | MAP_ERR_NOT_CHECKED, | ||
51 | MAP_ERR_CHECKED, | ||
52 | }; | ||
53 | |||
48 | #define DMA_DEBUG_STACKTRACE_ENTRIES 5 | 54 | #define DMA_DEBUG_STACKTRACE_ENTRIES 5 |
49 | 55 | ||
50 | struct dma_debug_entry { | 56 | struct dma_debug_entry { |
@@ -57,6 +63,7 @@ struct dma_debug_entry { | |||
57 | int direction; | 63 | int direction; |
58 | int sg_call_ents; | 64 | int sg_call_ents; |
59 | int sg_mapped_ents; | 65 | int sg_mapped_ents; |
66 | enum map_err_types map_err_type; | ||
60 | #ifdef CONFIG_STACKTRACE | 67 | #ifdef CONFIG_STACKTRACE |
61 | struct stack_trace stacktrace; | 68 | struct stack_trace stacktrace; |
62 | unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; | 69 | unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; |
@@ -114,6 +121,12 @@ static struct device_driver *current_driver __read_mostly; | |||
114 | 121 | ||
115 | static DEFINE_RWLOCK(driver_name_lock); | 122 | static DEFINE_RWLOCK(driver_name_lock); |
116 | 123 | ||
124 | static const char *const maperr2str[] = { | ||
125 | [MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable", | ||
126 | [MAP_ERR_NOT_CHECKED] = "dma map error not checked", | ||
127 | [MAP_ERR_CHECKED] = "dma map error checked", | ||
128 | }; | ||
129 | |||
117 | static const char *type2name[4] = { "single", "page", | 130 | static const char *type2name[4] = { "single", "page", |
118 | "scather-gather", "coherent" }; | 131 | "scather-gather", "coherent" }; |
119 | 132 | ||
@@ -376,11 +389,12 @@ void debug_dma_dump_mappings(struct device *dev) | |||
376 | list_for_each_entry(entry, &bucket->list, list) { | 389 | list_for_each_entry(entry, &bucket->list, list) { |
377 | if (!dev || dev == entry->dev) { | 390 | if (!dev || dev == entry->dev) { |
378 | dev_info(entry->dev, | 391 | dev_info(entry->dev, |
379 | "%s idx %d P=%Lx D=%Lx L=%Lx %s\n", | 392 | "%s idx %d P=%Lx D=%Lx L=%Lx %s %s\n", |
380 | type2name[entry->type], idx, | 393 | type2name[entry->type], idx, |
381 | (unsigned long long)entry->paddr, | 394 | (unsigned long long)entry->paddr, |
382 | entry->dev_addr, entry->size, | 395 | entry->dev_addr, entry->size, |
383 | dir2name[entry->direction]); | 396 | dir2name[entry->direction], |
397 | maperr2str[entry->map_err_type]); | ||
384 | } | 398 | } |
385 | } | 399 | } |
386 | 400 | ||
@@ -838,13 +852,28 @@ static __init int dma_debug_entries_cmdline(char *str) | |||
838 | __setup("dma_debug=", dma_debug_cmdline); | 852 | __setup("dma_debug=", dma_debug_cmdline); |
839 | __setup("dma_debug_entries=", dma_debug_entries_cmdline); | 853 | __setup("dma_debug_entries=", dma_debug_entries_cmdline); |
840 | 854 | ||
855 | /* Calling dma_mapping_error() from dma-debug api will result in calling | ||
856 | debug_dma_mapping_error() - need internal mapping error routine to | ||
857 | avoid debug checks */ | ||
858 | #ifndef DMA_ERROR_CODE | ||
859 | #define DMA_ERROR_CODE 0 | ||
860 | #endif | ||
861 | static inline int has_mapping_error(struct device *dev, dma_addr_t dma_addr) | ||
862 | { | ||
863 | const struct dma_map_ops *ops = get_dma_ops(dev); | ||
864 | if (ops->mapping_error) | ||
865 | return ops->mapping_error(dev, dma_addr); | ||
866 | |||
867 | return (dma_addr == DMA_ERROR_CODE); | ||
868 | } | ||
869 | |||
841 | static void check_unmap(struct dma_debug_entry *ref) | 870 | static void check_unmap(struct dma_debug_entry *ref) |
842 | { | 871 | { |
843 | struct dma_debug_entry *entry; | 872 | struct dma_debug_entry *entry; |
844 | struct hash_bucket *bucket; | 873 | struct hash_bucket *bucket; |
845 | unsigned long flags; | 874 | unsigned long flags; |
846 | 875 | ||
847 | if (dma_mapping_error(ref->dev, ref->dev_addr)) { | 876 | if (unlikely(has_mapping_error(ref->dev, ref->dev_addr))) { |
848 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " | 877 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " |
849 | "to free an invalid DMA memory address\n"); | 878 | "to free an invalid DMA memory address\n"); |
850 | return; | 879 | return; |
@@ -910,6 +939,15 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
910 | dir2name[ref->direction]); | 939 | dir2name[ref->direction]); |
911 | } | 940 | } |
912 | 941 | ||
942 | if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { | ||
943 | err_printk(ref->dev, entry, | ||
944 | "DMA-API: device driver failed to check map error" | ||
945 | "[device address=0x%016llx] [size=%llu bytes] " | ||
946 | "[mapped as %s]", | ||
947 | ref->dev_addr, ref->size, | ||
948 | type2name[entry->type]); | ||
949 | } | ||
950 | |||
913 | hash_bucket_del(entry); | 951 | hash_bucket_del(entry); |
914 | dma_entry_free(entry); | 952 | dma_entry_free(entry); |
915 | 953 | ||
@@ -1017,7 +1055,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
1017 | if (unlikely(global_disable)) | 1055 | if (unlikely(global_disable)) |
1018 | return; | 1056 | return; |
1019 | 1057 | ||
1020 | if (unlikely(dma_mapping_error(dev, dma_addr))) | 1058 | if (unlikely(has_mapping_error(dev, dma_addr))) |
1021 | return; | 1059 | return; |
1022 | 1060 | ||
1023 | entry = dma_entry_alloc(); | 1061 | entry = dma_entry_alloc(); |
@@ -1030,6 +1068,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
1030 | entry->dev_addr = dma_addr; | 1068 | entry->dev_addr = dma_addr; |
1031 | entry->size = size; | 1069 | entry->size = size; |
1032 | entry->direction = direction; | 1070 | entry->direction = direction; |
1071 | entry->map_err_type = MAP_ERR_NOT_CHECKED; | ||
1033 | 1072 | ||
1034 | if (map_single) | 1073 | if (map_single) |
1035 | entry->type = dma_debug_single; | 1074 | entry->type = dma_debug_single; |
@@ -1045,6 +1084,30 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
1045 | } | 1084 | } |
1046 | EXPORT_SYMBOL(debug_dma_map_page); | 1085 | EXPORT_SYMBOL(debug_dma_map_page); |
1047 | 1086 | ||
1087 | void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) | ||
1088 | { | ||
1089 | struct dma_debug_entry ref; | ||
1090 | struct dma_debug_entry *entry; | ||
1091 | struct hash_bucket *bucket; | ||
1092 | unsigned long flags; | ||
1093 | |||
1094 | if (unlikely(global_disable)) | ||
1095 | return; | ||
1096 | |||
1097 | ref.dev = dev; | ||
1098 | ref.dev_addr = dma_addr; | ||
1099 | bucket = get_hash_bucket(&ref, &flags); | ||
1100 | entry = bucket_find_exact(bucket, &ref); | ||
1101 | |||
1102 | if (!entry) | ||
1103 | goto out; | ||
1104 | |||
1105 | entry->map_err_type = MAP_ERR_CHECKED; | ||
1106 | out: | ||
1107 | put_hash_bucket(bucket, &flags); | ||
1108 | } | ||
1109 | EXPORT_SYMBOL(debug_dma_mapping_error); | ||
1110 | |||
1048 | void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, | 1111 | void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, |
1049 | size_t size, int direction, bool map_single) | 1112 | size_t size, int direction, bool map_single) |
1050 | { | 1113 | { |