diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-20 13:07:25 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-20 13:07:25 -0500 |
| commit | 787314c35fbb97e02823a1b8eb8cfa58f366cd49 (patch) | |
| tree | 3fe5a484c1846c80361217a726997484533e8344 /lib | |
| parent | 6491d4d02893d9787ba67279595990217177b351 (diff) | |
| parent | 9c6ecf6a3ade2dc4b03a239af68058b22897af41 (diff) | |
Merge tag 'iommu-updates-v3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu
Pull IOMMU updates from Joerg Roedel:
"A few new features this merge-window. The most important one is
probably, that dma-debug now warns if a dma-handle is not checked with
dma_mapping_error by the device driver. This requires minor changes
to some architectures which make use of dma-debug. Most of these
changes have the respective Acks by the Arch-Maintainers.
Besides that there are updates to the AMD IOMMU driver for refactor
the IOMMU-Groups support and to make sure it does not trigger a
hardware erratum.
The OMAP changes (for which I pulled in a branch from Tony Lindgren's
tree) have a conflict in linux-next with the arm-soc tree. The
conflict is in the file arch/arm/mach-omap2/clock44xx_data.c which is
deleted in the arm-soc tree. It is safe to delete the file too so
solve the conflict. Similar changes are done in the arm-soc tree in
the common clock framework migration. A missing hunk from the patch
in the IOMMU tree will be submitted as a seperate patch when the
merge-window is closed."
* tag 'iommu-updates-v3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (29 commits)
ARM: dma-mapping: support debug_dma_mapping_error
ARM: OMAP4: hwmod data: ipu and dsp to use parent clocks instead of leaf clocks
iommu/omap: Adapt to runtime pm
iommu/omap: Migrate to hwmod framework
iommu/omap: Keep mmu enabled when requested
iommu/omap: Remove redundant clock handling on ISR
iommu/amd: Remove obsolete comment
iommu/amd: Don't use 512GB pages
iommu/tegra: smmu: Move bus_set_iommu after probe for multi arch
iommu/tegra: gart: Move bus_set_iommu after probe for multi arch
iommu/tegra: smmu: Remove unnecessary PTC/TLB flush all
tile: dma_debug: add debug_dma_mapping_error support
sh: dma_debug: add debug_dma_mapping_error support
powerpc: dma_debug: add debug_dma_mapping_error support
mips: dma_debug: add debug_dma_mapping_error support
microblaze: dma-mapping: support debug_dma_mapping_error
ia64: dma_debug: add debug_dma_mapping_error support
c6x: dma_debug: add debug_dma_mapping_error support
ARM64: dma_debug: add debug_dma_mapping_error support
intel-iommu: Prevent devices with RMRRs from being placed into SI Domain
...
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/dma-debug.c | 66 |
1 files changed, 57 insertions, 9 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index d84beb994f3..5e396accd3d 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 | ||
| @@ -844,16 +858,16 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
| 844 | struct hash_bucket *bucket; | 858 | struct hash_bucket *bucket; |
| 845 | unsigned long flags; | 859 | unsigned long flags; |
| 846 | 860 | ||
| 847 | if (dma_mapping_error(ref->dev, ref->dev_addr)) { | ||
| 848 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " | ||
| 849 | "to free an invalid DMA memory address\n"); | ||
| 850 | return; | ||
| 851 | } | ||
| 852 | |||
| 853 | bucket = get_hash_bucket(ref, &flags); | 861 | bucket = get_hash_bucket(ref, &flags); |
| 854 | entry = bucket_find_exact(bucket, ref); | 862 | entry = bucket_find_exact(bucket, ref); |
| 855 | 863 | ||
| 856 | if (!entry) { | 864 | if (!entry) { |
| 865 | if (dma_mapping_error(ref->dev, ref->dev_addr)) { | ||
| 866 | err_printk(ref->dev, NULL, | ||
| 867 | "DMA-API: device driver tries " | ||
| 868 | "to free an invalid DMA memory address\n"); | ||
| 869 | return; | ||
| 870 | } | ||
| 857 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " | 871 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " |
| 858 | "to free DMA memory it has not allocated " | 872 | "to free DMA memory it has not allocated " |
| 859 | "[device address=0x%016llx] [size=%llu bytes]\n", | 873 | "[device address=0x%016llx] [size=%llu bytes]\n", |
| @@ -910,6 +924,15 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
| 910 | dir2name[ref->direction]); | 924 | dir2name[ref->direction]); |
| 911 | } | 925 | } |
| 912 | 926 | ||
| 927 | if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { | ||
| 928 | err_printk(ref->dev, entry, | ||
| 929 | "DMA-API: device driver failed to check map error" | ||
| 930 | "[device address=0x%016llx] [size=%llu bytes] " | ||
| 931 | "[mapped as %s]", | ||
| 932 | ref->dev_addr, ref->size, | ||
| 933 | type2name[entry->type]); | ||
| 934 | } | ||
| 935 | |||
| 913 | hash_bucket_del(entry); | 936 | hash_bucket_del(entry); |
| 914 | dma_entry_free(entry); | 937 | dma_entry_free(entry); |
| 915 | 938 | ||
| @@ -1017,7 +1040,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
| 1017 | if (unlikely(global_disable)) | 1040 | if (unlikely(global_disable)) |
| 1018 | return; | 1041 | return; |
| 1019 | 1042 | ||
| 1020 | if (unlikely(dma_mapping_error(dev, dma_addr))) | 1043 | if (dma_mapping_error(dev, dma_addr)) |
| 1021 | return; | 1044 | return; |
| 1022 | 1045 | ||
| 1023 | entry = dma_entry_alloc(); | 1046 | entry = dma_entry_alloc(); |
| @@ -1030,6 +1053,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
| 1030 | entry->dev_addr = dma_addr; | 1053 | entry->dev_addr = dma_addr; |
| 1031 | entry->size = size; | 1054 | entry->size = size; |
| 1032 | entry->direction = direction; | 1055 | entry->direction = direction; |
| 1056 | entry->map_err_type = MAP_ERR_NOT_CHECKED; | ||
| 1033 | 1057 | ||
| 1034 | if (map_single) | 1058 | if (map_single) |
| 1035 | entry->type = dma_debug_single; | 1059 | entry->type = dma_debug_single; |
| @@ -1045,6 +1069,30 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
| 1045 | } | 1069 | } |
| 1046 | EXPORT_SYMBOL(debug_dma_map_page); | 1070 | EXPORT_SYMBOL(debug_dma_map_page); |
| 1047 | 1071 | ||
| 1072 | void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) | ||
| 1073 | { | ||
| 1074 | struct dma_debug_entry ref; | ||
| 1075 | struct dma_debug_entry *entry; | ||
| 1076 | struct hash_bucket *bucket; | ||
| 1077 | unsigned long flags; | ||
| 1078 | |||
| 1079 | if (unlikely(global_disable)) | ||
| 1080 | return; | ||
| 1081 | |||
| 1082 | ref.dev = dev; | ||
| 1083 | ref.dev_addr = dma_addr; | ||
| 1084 | bucket = get_hash_bucket(&ref, &flags); | ||
| 1085 | entry = bucket_find_exact(bucket, &ref); | ||
| 1086 | |||
| 1087 | if (!entry) | ||
| 1088 | goto out; | ||
| 1089 | |||
| 1090 | entry->map_err_type = MAP_ERR_CHECKED; | ||
| 1091 | out: | ||
| 1092 | put_hash_bucket(bucket, &flags); | ||
| 1093 | } | ||
| 1094 | EXPORT_SYMBOL(debug_dma_mapping_error); | ||
| 1095 | |||
| 1048 | void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, | 1096 | void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, |
| 1049 | size_t size, int direction, bool map_single) | 1097 | size_t size, int direction, bool map_single) |
| 1050 | { | 1098 | { |
