diff options
author | Konsta Holtta <kholtta@nvidia.com> | 2013-08-30 10:16:41 +0300 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-27 12:51:52 -0700 |
commit | 41797576db9434854211956abe9cc56ad79f2a9c (patch) | |
tree | 300e2fd63a532fdc75b15652b54321a33efb0fd0 | |
parent | 9a725a610d02715b098e20936b1f3103cfffe755 (diff) |
dma-debug: dump buffers and mappings via debugfs
Export via debugfs the debug-dma infrastructure's data about allocated
mappings, and architecture specific information about possible mappings.
Bug 1173494
Change-Id: I6c64364dad69f83fd301a89938fe184dde33806a
Signed-off-by: Konsta Holtta <kholtta@nvidia.com>
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Reviewed-on: http://git-master/r/268384
GVS: Gerrit_Virtual_Submit
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
-rw-r--r-- | arch/arm/include/asm/dma-iommu.h | 1 | ||||
-rw-r--r-- | arch/arm/mm/dma-mapping.c | 120 | ||||
-rw-r--r-- | lib/dma-debug.c | 108 |
3 files changed, 229 insertions, 0 deletions
diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h index ed1e019b59e1..4d9f5742a149 100644 --- a/arch/arm/include/asm/dma-iommu.h +++ b/arch/arm/include/asm/dma-iommu.h @@ -21,6 +21,7 @@ struct dma_iommu_mapping { spinlock_t lock; struct kref kref; + struct list_head list; }; struct dma_iommu_mapping * diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index eecc6fd69ca0..938922676144 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -25,6 +25,8 @@ #include <linux/io.h> #include <linux/vmalloc.h> #include <linux/sizes.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> #include <asm/memory.h> #include <asm/highmem.h> @@ -1010,11 +1012,108 @@ int arm_dma_set_mask(struct device *dev, u64 dma_mask) return 0; } +#if defined(CONFIG_ARM_DMA_USE_IOMMU) + +static LIST_HEAD(iommu_mapping_list); +static DEFINE_SPINLOCK(iommu_mapping_list_lock); + +#if defined(CONFIG_DEBUG_FS) +static dma_addr_t bit_to_addr(size_t pos, dma_addr_t base, size_t order) +{ + return base + pos * (1 << (PAGE_SHIFT + order)); +} + +static void seq_print_dma_areas(struct seq_file *s, void *bitmap, + dma_addr_t base, size_t bits, size_t order) +{ + /* one bit = one (page + order) sized block */ + size_t pos = find_first_bit(bitmap, bits), end; + + for (; pos < bits; pos = find_next_bit(bitmap, bits, end + 1)) { + end = find_next_zero_bit(bitmap, bits, pos); + seq_printf(s, " 0x%08x-0x%08x pages=%d\n", + bit_to_addr(pos, base, order), + bit_to_addr(end, base, order) - 1, + (end - pos) << order); + } +} + +static void seq_print_mapping(struct seq_file *s, + struct dma_iommu_mapping *mapping) +{ + seq_printf(s, " memory map: base=0x%x size=%d order=%d domain=%p\n", + mapping->base, mapping->end - mapping->base, + mapping->order, mapping->domain); + + seq_print_dma_areas(s, mapping->bitmap, mapping->base, mapping->bits, + mapping->order); +} + +static void debug_dma_seq_print_mappings(struct seq_file *s) +{ + struct dma_iommu_mapping *mapping; + int i = 0; + + list_for_each_entry(mapping, &iommu_mapping_list, list) { + seq_printf(s, "Map %d (%p):\n", i, mapping); + seq_print_mapping(s, mapping); + i++; + } +} + +static int dump_iommu_mappings(struct seq_file *s, void *data) +{ + debug_dma_seq_print_mappings(s); + return 0; +} + +static int dump_iommu_mappings_open(struct inode *inode, struct file *file) +{ + return single_open(file, dump_iommu_mappings, NULL); +} + +static const struct file_operations dump_iommu_mappings_fops = { + .open = dump_iommu_mappings_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#endif /* CONFIG_DEBUG_FS */ + +void dma_debugfs_platform_info(struct dentry *dent) +{ + debugfs_create_file("dump_mappings", S_IRUGO, dent, NULL, + &dump_iommu_mappings_fops); +} + +#else /* !CONFIG_ARM_DMA_USE_IOMMU */ +static inline void dma_debugfs_platform_info(struct dentry *dent) +{ +} +#endif /* !CONFIG_ARM_DMA_USE_IOMMU */ + +#if defined(CONFIG_DMA_API_DEBUG) +static inline void dma_debug_platform(void) +{ +} +#else /* !CONFIG_DMA_API_DEBUG */ +static void dma_debug_platform(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("dma-api", NULL); + if (dent) + dma_debugfs_platform_info(dent); +} +#endif /* !CONFIG_DMA_API_DEBUG */ + #define PREALLOC_DMA_DEBUG_ENTRIES 4096 static int __init dma_debug_do_init(void) { dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); + dma_debug_platform(); return 0; } fs_initcall(dma_debug_do_init); @@ -1048,6 +1147,24 @@ static int __init iova_gap_pages_init(void) } core_initcall(iova_gap_pages_init); +static void iommu_mapping_list_add(struct dma_iommu_mapping *mapping) +{ + unsigned long flags; + + spin_lock_irqsave(&iommu_mapping_list_lock, flags); + list_add_tail(&mapping->list, &iommu_mapping_list); + spin_unlock_irqrestore(&iommu_mapping_list_lock, flags); +} + +static void iommu_mapping_list_del(struct dma_iommu_mapping *mapping) +{ + unsigned long flags; + + spin_lock_irqsave(&iommu_mapping_list_lock, flags); + list_del(&mapping->list); + spin_unlock_irqrestore(&iommu_mapping_list_lock, flags); +} + static int pg_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t phys, size_t len, int prot) { @@ -2155,6 +2272,8 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size, goto err3; kref_init(&mapping->kref); + + iommu_mapping_list_add(mapping); return mapping; err3: kfree(mapping->bitmap); @@ -2170,6 +2289,7 @@ static void release_iommu_mapping(struct kref *kref) struct dma_iommu_mapping *mapping = container_of(kref, struct dma_iommu_mapping, kref); + iommu_mapping_list_del(mapping); iommu_domain_free(mapping->domain); kfree(mapping->bitmap); kfree(mapping); diff --git a/lib/dma-debug.c b/lib/dma-debug.c index cc42171ff462..554352eeae2e 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c @@ -659,6 +659,111 @@ out_unlock: return count; } +char *__weak debug_dma_platformdata(struct device *dev) +{ + /* empty string by default */ + static char buf[1]; + + return buf; +} + +static inline void seq_print_ip_sym(struct seq_file *s, unsigned long ip) +{ + seq_printf(s, "[<%p>] %pS\n", (void *)ip, (void *)ip); +} + +void seq_print_trace(struct seq_file *s, struct stack_trace *trace) +{ + int i; + + if (WARN_ON(!trace->entries)) + return; + + for (i = trace->skip; i < trace->nr_entries; i++) + seq_print_ip_sym(s, trace->entries[i]); +} + +/* + * Print all map entries just in the order they are stored. We assume that the + * user will be able to parse this later anyway. Detailed output includes stack + * traces of allocations. + */ +void seq_print_dma_mappings(struct seq_file *s, int detail) +{ + int idx; + + for (idx = 0; idx < HASH_SIZE; idx++) { + struct hash_bucket *bucket = &dma_entry_hash[idx]; + struct dma_debug_entry *entry; + unsigned long flags; + + spin_lock_irqsave(&bucket->lock, flags); + + list_for_each_entry(entry, &bucket->list, list) { + seq_printf(s, + " %s %s idx %d P=%llx D=%llx L=%llx %s A=%s\n", + dev_name(entry->dev), + type2name[entry->type], idx, + (u64)entry->paddr, + entry->dev_addr, entry->size, + dir2name[entry->direction], + debug_dma_platformdata(entry->dev)); + + if (detail) + seq_print_trace(s, &entry->stacktrace); + } + + spin_unlock_irqrestore(&bucket->lock, flags); + } +} + +void __weak dma_debugfs_platform_info(struct dentry *dent) +{ +} + +static int _dump_allocs(struct seq_file *s, void *data) +{ + int detail = (int)s->private; + + seq_print_dma_mappings(s, detail); + return 0; +} + +#define DEFINE_DEBUGFS(__name, __func, __data) \ +static int __name ## _open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, __func, __data); \ +} \ +static const struct file_operations __name ## _fops = { \ + .open = __name ## _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +DEFINE_DEBUGFS(_dump_allocs, _dump_allocs, NULL); +DEFINE_DEBUGFS(_dump_allocs_detail, _dump_allocs, (void *)1); +#undef DEFINE_DEBUGFS + +static int map_dump_debug_fs_init(void) +{ +#define CREATE_FILE(name) \ + debugfs_create_file(#name, S_IRUGO, \ + dma_debug_dent, NULL, \ + &_##name##_fops) + + if (!CREATE_FILE(dump_allocs)) + return -ENOMEM; + + if (!CREATE_FILE(dump_allocs_detail)) + return -ENOMEM; + +#undef CREATE_FILE + + dma_debugfs_platform_info(dma_debug_dent); + return 0; +} + static const struct file_operations filter_fops = { .read = filter_read, .write = filter_write, @@ -713,6 +818,9 @@ static int dma_debug_fs_init(void) if (!filter_dent) goto out_err; + if (map_dump_debug_fs_init()) + goto out_err; + return 0; out_err: |