summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKonsta Holtta <kholtta@nvidia.com>2013-07-09 13:59:19 +0300
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-27 12:51:53 -0700
commit3db0818748a209a39ce7064f397c7adf0824a9a7 (patch)
tree15bcd18e95adad69b3b50393af5e884651269627 /lib
parent7c26a79def7c8e8f0761290dd5e2b468b6dccc98 (diff)
dma-debug: track and export map counts per device
Store mapping/allocation counts of devices through their lifetime and export via debugfs the current, all time total, and maximum number of mappings and mapped bytes. Bug 1351794 Bug 1173494 Change-Id: I07ba73d44bcd34e37b2036507da65706a973fc92 Signed-off-by: Konsta Holtta <kholtta@nvidia.com> Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com> Reviewed-on: http://git-master/r/268456 GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/dma-debug.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 554352eeae2e..0fe2791d0e47 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -142,6 +142,24 @@ static const char *type2name[4] = { "single", "page",
static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE",
"DMA_FROM_DEVICE", "DMA_NONE" };
+/* dma statistics per device */
+struct dma_dev_info {
+ struct list_head list;
+ struct device *dev;
+ spinlock_t lock; /* Protects dma_dev_info itself */
+
+ int current_allocs;
+ int total_allocs;
+ int max_allocs;
+
+ int current_alloc_size;
+ int total_alloc_size;
+ int max_alloc_size;
+};
+
+static LIST_HEAD(dev_info_list);
+static DEFINE_SPINLOCK(dev_info_lock); /* Protects dev_info_list */
+
/*
* The access to some variables in this macro is racy. We can't use atomic_t
* here because all these variables are exported to debugfs. Some of them even
@@ -413,6 +431,79 @@ void debug_dma_dump_mappings(struct device *dev)
EXPORT_SYMBOL(debug_dma_dump_mappings);
/*
+ * device info snapshot updating functions
+ */
+static void ____dev_info_incr(struct dma_dev_info *info,
+ struct dma_debug_entry *entry)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+
+ info->current_allocs++;
+ info->total_allocs++;
+ if (info->current_allocs > info->max_allocs)
+ info->max_allocs = info->current_allocs;
+
+ info->current_alloc_size += entry->size;
+ info->total_alloc_size += entry->size;
+ if (info->current_alloc_size > info->max_alloc_size)
+ info->max_alloc_size = info->current_alloc_size;
+
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void ____dev_info_decr(struct dma_dev_info *info,
+ struct dma_debug_entry *entry)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+
+ info->current_allocs--;
+ info->current_alloc_size -= entry->size;
+
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void __dev_info_fn(struct dma_debug_entry *entry,
+ void (*fn)(struct dma_dev_info *, struct dma_debug_entry *))
+{
+ struct dma_dev_info *info;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_info_lock, flags);
+
+ list_for_each_entry(info, &dev_info_list, list)
+ if (info->dev == entry->dev)
+ goto found;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(entry->dev, "Out of memory at %s\n", __func__);
+ spin_unlock_irqrestore(&dev_info_lock, flags);
+ return;
+ }
+
+ spin_lock_init(&info->lock);
+ info->dev = entry->dev;
+ list_add(&info->list, &dev_info_list);
+found:
+ spin_unlock_irqrestore(&dev_info_lock, flags);
+ fn(info, entry);
+}
+
+static inline void dev_info_alloc(struct dma_debug_entry *entry)
+{
+ __dev_info_fn(entry, ____dev_info_incr);
+}
+
+static inline void dev_info_free(struct dma_debug_entry *entry)
+{
+ __dev_info_fn(entry, ____dev_info_decr);
+}
+
+/*
* Wrapper function for adding an entry to the hash.
* This function takes care of locking itself.
*/
@@ -424,6 +515,8 @@ static void add_dma_entry(struct dma_debug_entry *entry)
bucket = get_hash_bucket(entry, &flags);
hash_bucket_add(bucket, entry);
put_hash_bucket(bucket, &flags);
+
+ dev_info_alloc(entry);
}
static struct dma_debug_entry *__dma_entry_alloc(void)
@@ -729,6 +822,24 @@ static int _dump_allocs(struct seq_file *s, void *data)
return 0;
}
+static int _dump_dev_info(struct seq_file *s, void *data)
+{
+ struct dma_dev_info *i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_info_lock, flags);
+
+ list_for_each_entry(i, &dev_info_list, list)
+ seq_printf(s,
+ "dev=%s curallocs=%d totallocs=%d maxallocs=%d cursize=%d totsize=%d maxsize=%d\n",
+ dev_name(i->dev), i->current_allocs, i->total_allocs,
+ i->max_allocs, i->current_alloc_size,
+ i->total_alloc_size, i->max_alloc_size);
+
+ spin_unlock_irqrestore(&dev_info_lock, flags);
+ return 0;
+}
+
#define DEFINE_DEBUGFS(__name, __func, __data) \
static int __name ## _open(struct inode *inode, struct file *file) \
{ \
@@ -743,6 +854,7 @@ static const struct file_operations __name ## _fops = { \
DEFINE_DEBUGFS(_dump_allocs, _dump_allocs, NULL);
DEFINE_DEBUGFS(_dump_allocs_detail, _dump_allocs, (void *)1);
+DEFINE_DEBUGFS(_dump_dev_info, _dump_dev_info, NULL);
#undef DEFINE_DEBUGFS
static int map_dump_debug_fs_init(void)
@@ -758,6 +870,9 @@ static int map_dump_debug_fs_init(void)
if (!CREATE_FILE(dump_allocs_detail))
return -ENOMEM;
+ if (!CREATE_FILE(dump_dev_info))
+ return -ENOMEM;
+
#undef CREATE_FILE
dma_debugfs_platform_info(dma_debug_dent);
@@ -1055,6 +1170,8 @@ static void check_unmap(struct dma_debug_entry *ref)
type2name[entry->type]);
}
+ dev_info_free(entry);
+
hash_bucket_del(entry);
dma_entry_free(entry);