dma-debug: dump buffers and mappings via debugfs
Konsta Holtta [Fri, 30 Aug 2013 07:16:41 +0000 (10:16 +0300)]
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>

arch/arm/include/asm/dma-iommu.h
arch/arm/mm/dma-mapping.c
lib/dma-debug.c

index ed1e019..4d9f574 100644 (file)
@@ -21,6 +21,7 @@ struct dma_iommu_mapping {
 
        spinlock_t              lock;
        struct kref             kref;
+       struct list_head        list;
 };
 
 struct dma_iommu_mapping *
index eecc6fd..9389226 100644 (file)
@@ -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);
index cc42171..554352e 100644 (file)
@@ -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: