From 11ccbd415c44c7eb52e623335219924d039d9180 Mon Sep 17 00:00:00 2001 From: "hyesoo.yu" Date: Tue, 21 Aug 2018 13:46:05 +0900 Subject: [PATCH] [RAMEN9610-12171] ion: add heap debug node This debug node show allocation status for heaps to allocate contiguous memory to know memory fragmentation, memory leak, or memory staticstics. Change-Id: I99a74a695587ffea75dcd543b9d59fa94d757ead Signed-off-by: hyesoo.yu --- drivers/staging/android/ion/ion.c | 2 + drivers/staging/android/ion/ion.h | 4 +- .../staging/android/ion/ion_carveout_heap.c | 17 +- drivers/staging/android/ion/ion_cma_heap.c | 16 +- drivers/staging/android/ion/ion_debug.c | 310 ++++++++++++------ drivers/staging/android/ion/ion_debug.h | 7 + drivers/staging/android/ion/ion_exynos.h | 2 + drivers/staging/android/ion/ion_heap.c | 30 -- 8 files changed, 249 insertions(+), 139 deletions(-) diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index 47f07a0b0f97..b5460b74c003 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -695,6 +695,8 @@ void ion_device_add_heap(struct ion_heap *heap) } } + ion_debug_heap_init(heap); + dev->heap_cnt++; up_write(&dev->lock); } diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h index aa5440cefd71..eb12a6c13d0c 100644 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -104,6 +104,7 @@ struct ion_device { struct rw_semaphore lock; struct plist_head heaps; struct dentry *debug_root; + struct dentry *heaps_debug_root; int heap_cnt; }; @@ -364,9 +365,6 @@ long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); int ion_query_heaps(struct ion_heap_query *query); -void ion_contig_heap_show_buffers(struct ion_heap *heap, - phys_addr_t base, size_t pool_size); - void *ion_buffer_kmap_get(struct ion_buffer *buffer); void ion_buffer_kmap_put(struct ion_buffer *buffer); diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c index dd4b10aca57b..7177184759af 100644 --- a/drivers/staging/android/ion/ion_carveout_heap.c +++ b/drivers/staging/android/ion/ion_carveout_heap.c @@ -27,6 +27,7 @@ #include "ion.h" #include "ion_exynos.h" +#include "ion_debug.h" #define ION_CARVEOUT_ALLOCATE_FAIL -1 @@ -118,7 +119,7 @@ err_free_table: sg_free_table(table); err_free: kfree(table); - ion_contig_heap_show_buffers(&carveout_heap->heap, + ion_contig_heap_show_buffers(NULL, &carveout_heap->heap, carveout_heap->base, carveout_heap->size); return ret; } @@ -197,6 +198,19 @@ static struct ion_heap_ops carveout_heap_ops = { .query_heap = carveout_heap_query, }; +static int ion_carveout_heap_debug_show(struct ion_heap *heap, + struct seq_file *s, + void *unused) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + ion_contig_heap_show_buffers(s, &carveout_heap->heap, + carveout_heap->base, carveout_heap->size); + + return 0; +} + struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) { struct ion_carveout_heap *carveout_heap; @@ -243,6 +257,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) carveout_heap->protection_id = heap_data->id; carveout_heap->secure = heap_data->secure; carveout_heap->untouchable = heap_data->untouchable; + carveout_heap->heap.debug_show = ion_carveout_heap_debug_show; return &carveout_heap->heap; } diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c index 96020b97cfb0..1367de4b2105 100644 --- a/drivers/staging/android/ion/ion_cma_heap.c +++ b/drivers/staging/android/ion/ion_cma_heap.c @@ -27,6 +27,7 @@ #include "ion.h" #include "ion_exynos.h" +#include "ion_debug.h" struct ion_cma_heap { struct ion_heap heap; @@ -116,7 +117,7 @@ free_mem: err_table: cma_release(cma_heap->cma, pages, nr_pages); err: - ion_contig_heap_show_buffers(&cma_heap->heap, + ion_contig_heap_show_buffers(NULL, &cma_heap->heap, cma_get_base(cma_heap->cma), cma_get_size(cma_heap->cma)); return ret; @@ -158,6 +159,18 @@ static struct ion_heap_ops ion_cma_ops = { }; #ifdef CONFIG_ION_EXYNOS +static int ion_cma_heap_debug_show(struct ion_heap *heap, struct seq_file *s, + void *unused) +{ + struct ion_cma_heap *cma_heap = to_cma_heap(heap); + + ion_contig_heap_show_buffers(s, &cma_heap->heap, + cma_get_base(cma_heap->cma), + cma_get_size(cma_heap->cma)); + + return 0; +} + struct ion_heap *ion_cma_heap_create(struct cma *cma, struct ion_platform_heap *heap_data) { @@ -170,6 +183,7 @@ struct ion_heap *ion_cma_heap_create(struct cma *cma, cma_heap->heap.ops = &ion_cma_ops; cma_heap->cma = cma; cma_heap->heap.type = ION_HEAP_TYPE_DMA; + cma_heap->heap.debug_show = ion_cma_heap_debug_show; cma_heap->heap.name = kstrndup(heap_data->name, MAX_HEAP_NAME - 1, GFP_KERNEL); cma_heap->align_order = get_order(heap_data->align); diff --git a/drivers/staging/android/ion/ion_debug.c b/drivers/staging/android/ion/ion_debug.c index f2e97d717525..281ad3ecda8c 100644 --- a/drivers/staging/android/ion/ion_debug.c +++ b/drivers/staging/android/ion/ion_debug.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "ion.h" #include "ion_exynos.h" @@ -25,6 +26,10 @@ #define ION_MAX_EVENT_LOG 1024 #define ION_EVENT_CLAMP_ID(id) ((id) & (ION_MAX_EVENT_LOG - 1)) +#define ion_debug_print(s, fmt, ...) \ + ((s) ? seq_printf(s, fmt, ##__VA_ARGS__) :\ + pr_info(fmt, ##__VA_ARGS__)) + static atomic_t eventid; static char * const ion_event_name[] = { @@ -155,7 +160,87 @@ static const struct file_operations debug_event_fops = { .release = single_release, }; -#define ION_MAX_LOGBUF 128 +static int contig_heap_cmp(const void *l, const void *r) +{ + struct ion_buffer *left = *((struct ion_buffer **)l); + struct ion_buffer *right = *((struct ion_buffer **)r); + + return ((unsigned long)sg_phys(left->sg_table->sgl)) - + ((unsigned long)sg_phys(right->sg_table->sgl)); +} + +void ion_contig_heap_show_buffers(struct seq_file *s, struct ion_heap *heap, + phys_addr_t base, size_t pool_size) +{ + size_t total_size = 0; + struct rb_node *n; + struct ion_buffer **sorted = NULL; + int i, count = 64, ptr = 0; + + if (heap->type != ION_HEAP_TYPE_CARVEOUT && + heap->type != ION_HEAP_TYPE_DMA) + return; + + ion_debug_print(s, + "ION heap '%s' of type %u and id %u, size %zu bytes\n", + heap->name, heap->type, heap->id, pool_size); + + mutex_lock(&heap->dev->buffer_lock); + for (n = rb_first(&heap->dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, + node); + + if (buffer->heap != heap) + continue; + + if (!sorted) + sorted = kmalloc_array(count, sizeof(*sorted), + GFP_KERNEL); + + if (ptr == count) { + struct ion_buffer **tmp; + + tmp = kmalloc_array(2 * count, sizeof(*sorted), + GFP_KERNEL); + memcpy(tmp, sorted, sizeof(*sorted) * count); + count *= 2; + kfree(sorted); + + sorted = tmp; + } + + sorted[ptr++] = buffer; + } + + if (!sorted) { + mutex_unlock(&heap->dev->buffer_lock); + return; + } + + sort(sorted, ptr, sizeof(*sorted), contig_heap_cmp, NULL); + + for (i = 0; i < ptr; i++) { + unsigned long cur, next; + + cur = (unsigned long)(sg_phys(sorted[i]->sg_table->sgl) - base); + next = (i == ptr - 1) ? + pool_size : (unsigned long) + (sg_phys(sorted[i + 1]->sg_table->sgl) - base); + + ion_debug_print(s, + "offset %#010lx size %10zu (free %10zu)\n", + cur, sorted[i]->size, + next - cur - sorted[i]->size); + + total_size += sorted[i]->size; + } + + ion_debug_print(s, "Total allocated size: %zu bytes --------------\n", + total_size); + + mutex_unlock(&heap->dev->buffer_lock); + kfree(sorted); +} const static char *heap_type_name[] = { "system", @@ -166,92 +251,139 @@ const static char *heap_type_name[] = { "hpa", }; -static size_t ion_print_buffer(struct ion_buffer *buffer, bool alive, - char *logbuf, int buflen) +static void ion_debug_freed_buffer(struct seq_file *s, struct ion_device *dev) { - struct ion_iovm_map *iovm; - int count; - unsigned int heaptype = buffer->heap->type; - - if (heaptype >= ARRAY_SIZE(heap_type_name)) - heaptype = 0; /* forces it to 0 to prevent buffer overrun */ - - count = scnprintf(logbuf, buflen, "[%4d] %15s %8s %#5lx %8zu : ", - buffer->id, buffer->heap->name, - heap_type_name[heaptype], buffer->flags, - buffer->size / SZ_1K); - buflen = max(0, buflen - count); - /* - * It is okay even if count is larger than ION_MAX_LOGBUF - * because buflen then becomes zero and scnprintf never writes to - * the buffer if the size is zero. - */ - logbuf += count; - - if (alive) { - list_for_each_entry(iovm, &buffer->iovas, list) { - count = scnprintf(logbuf, buflen, "%s(%d) ", - dev_name(iovm->dev), - atomic_read(&iovm->mapcnt)); - buflen = max(0, buflen - count); - logbuf += count; + struct ion_heap *heap; + struct ion_buffer *buffer; + + down_read(&dev->lock); + plist_for_each_entry(heap, &dev->heaps, node) { + if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE)) + continue; + + spin_lock(&heap->free_lock); + /* buffer lock is not required because the buffer is freed */ + list_for_each_entry(buffer, &heap->free_list, list) { + unsigned int heaptype = (buffer->heap->type < + ARRAY_SIZE(heap_type_name)) ? + buffer->heap->type : 0; + + ion_debug_print(s, + "[%4d] %15s %8s %#5lx %8zu : freed\n", + buffer->id, buffer->heap->name, + heap_type_name[heaptype], buffer->flags, + buffer->size / SZ_1K); } - scnprintf(logbuf, buflen, "\n"); - } else { - scnprintf(logbuf, buflen, "(freed)\n"); + spin_unlock(&heap->free_lock); } - - return buffer->size; + up_read(&dev->lock); } -static int ion_debug_buffers_show(struct seq_file *s, void *unused) +static void ion_debug_buffer_for_heap(struct seq_file *s, + struct ion_device *dev, + struct ion_heap *heap) { - struct ion_device *idev = s->private; struct rb_node *n; struct ion_buffer *buffer; - struct ion_heap *heap; - char logbuf[ION_MAX_LOGBUF]; size_t total = 0; - seq_printf(s, "[ id] %15s %8s %5s %8s : %s\n", - "heap", "heaptype", "flags", "size(kb)", "iommu_mapped..."); + ion_debug_print(s, "[ id] %15s %8s %5s %8s : %s\n", + "heap", "heaptype", "flags", "size(kb)", + "iommu_mapped..."); - mutex_lock(&idev->buffer_lock); - for (n = rb_first(&idev->buffers); n; n = rb_next(n)) { + mutex_lock(&dev->buffer_lock); + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { buffer = rb_entry(n, struct ion_buffer, node); - mutex_lock(&buffer->lock); - total += ion_print_buffer(buffer, true, logbuf, ION_MAX_LOGBUF); - seq_puts(s, logbuf); + if (!heap || heap == buffer->heap) { + struct ion_iovm_map *iovm; + unsigned int heaptype = (buffer->heap->type < + ARRAY_SIZE(heap_type_name)) ? + buffer->heap->type : 0; - mutex_unlock(&buffer->lock); - } - mutex_unlock(&idev->buffer_lock); + mutex_lock(&buffer->lock); + ion_debug_print(s, "[%4d] %15s %8s %#5lx %8zu ", + buffer->id, buffer->heap->name, + heap_type_name[heaptype], buffer->flags, + buffer->size / SZ_1K); - down_read(&idev->lock); - plist_for_each_entry(heap, &idev->heaps, node) { - if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE)) - continue; + list_for_each_entry(iovm, &buffer->iovas, list) { + ion_debug_print(s, "%s(%d) ", + dev_name(iovm->dev), + atomic_read(&iovm->mapcnt)); + } + ion_debug_print(s, "\n"); - spin_lock(&heap->free_lock); - /* buffer lock is not required because the buffer is freed */ - list_for_each_entry(buffer, &heap->free_list, list) { - ion_print_buffer(buffer, false, logbuf, ION_MAX_LOGBUF); - seq_puts(s, logbuf); + total += buffer->size; + + mutex_unlock(&buffer->lock); } - spin_unlock(&heap->free_lock); } - up_read(&idev->lock); + mutex_unlock(&dev->buffer_lock); - seq_printf(s, "TOTAL: %zu kb\n\n", total / SZ_1K); + total /= SZ_1K; - down_read(&idev->lock); - plist_for_each_entry(heap, &idev->heaps, node) - if (heap->debug_show) { - seq_printf(s, "Page pools of %s:\n", heap->name); - heap->debug_show(heap, s, unused); - } - up_read(&idev->lock); + ion_debug_print(s, "TOTAL: %zu kb\n", total); +} + +#define ion_debug_buffer_for_all_heap(s, dev) \ + ion_debug_buffer_for_heap(s, dev, NULL) + +static int ion_debug_heap_show(struct seq_file *s, void *unused) +{ + struct ion_heap *heap = s->private; + + seq_printf(s, "ION heap '%s' of type %u and id %u\n", + heap->name, heap->type, heap->id); + + ion_debug_buffer_for_heap(s, heap->dev, heap); + + if (heap->debug_show) + heap->debug_show(heap, s, unused); + + return 0; +} + +static int ion_debug_heap_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_heap_show, inode->i_private); +} + +static const struct file_operations debug_heap_fops = { + .open = ion_debug_heap_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void ion_debug_heap_init(struct ion_heap *heap) +{ + char debug_name[64]; + struct dentry *heap_file; + + if (!heap->dev->heaps_debug_root) + return; + + snprintf(debug_name, 64, "%s", heap->name); + + heap_file = debugfs_create_file( + debug_name, 0444, heap->dev->heaps_debug_root, + heap, &debug_heap_fops); + if (!heap_file) { + char buf[256], *path; + + path = dentry_path(heap->dev->heaps_debug_root, + buf, 256); + perrfn("failed to create %s/%s", path, heap_file); + } +} + +static int ion_debug_buffers_show(struct seq_file *s, void *unused) +{ + struct ion_device *idev = s->private; + + ion_debug_buffer_for_all_heap(s, idev); + ion_debug_freed_buffer(s, idev); return 0; } @@ -278,43 +410,9 @@ static int ion_oom_notifier_fn(struct notifier_block *nb, { struct ion_device *idev = container_of(nb, struct ion_oom_notifier_struct, nb)->idev; - struct rb_node *n; - struct ion_buffer *buffer; - struct ion_heap *heap; - char logbuf[ION_MAX_LOGBUF]; - size_t total = 0; - - pr_info("[ id] %15s %8s %5s %8s : %s\n", - "heap", "heaptype", "flags", "size(kb)", "iommu_mapped..."); - - mutex_lock(&idev->buffer_lock); - for (n = rb_first(&idev->buffers); n; n = rb_next(n)) { - buffer = rb_entry(n, struct ion_buffer, node); - mutex_lock(&buffer->lock); - - total += ion_print_buffer(buffer, true, logbuf, ION_MAX_LOGBUF); - pr_info("%s", logbuf); - mutex_unlock(&buffer->lock); - } - mutex_unlock(&idev->buffer_lock); - - down_read(&idev->lock); - plist_for_each_entry(heap, &idev->heaps, node) { - if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE)) - continue; - - spin_lock(&heap->free_lock); - /* buffer lock is not required because the buffer is freed */ - list_for_each_entry(buffer, &heap->free_list, list) { - ion_print_buffer(buffer, false, logbuf, ION_MAX_LOGBUF); - pr_info("%s", logbuf); - } - spin_unlock(&heap->free_lock); - } - up_read(&idev->lock); - - pr_info("TOTAL: %zu kb\n", total / SZ_1K); + ion_debug_buffer_for_all_heap(NULL, idev); + ion_debug_freed_buffer(NULL, idev); return 0; } @@ -335,7 +433,11 @@ void ion_debug_initialize(struct ion_device *idev) event_file = debugfs_create_file("event", 0444, idev->debug_root, idev, &debug_event_fops); if (!event_file) - pr_err("%s: failed to create debugfs/ion/event\n", __func__); + perrfn("failed to create debugfs/ion/event"); + + idev->heaps_debug_root = debugfs_create_dir("heaps", idev->debug_root); + if (!idev->heaps_debug_root) + perrfn("failed to create debugfs/ion/heaps directory"); ion_oom_notifier.idev = idev; register_oom_notifier(&ion_oom_notifier.nb); diff --git a/drivers/staging/android/ion/ion_debug.h b/drivers/staging/android/ion/ion_debug.h index d756a7095035..a60c5ead3b81 100644 --- a/drivers/staging/android/ion/ion_debug.h +++ b/drivers/staging/android/ion/ion_debug.h @@ -17,6 +17,13 @@ #ifndef _ION_DEBUG_H_ #define _ION_DEBUG_H_ +#ifdef CONFIG_ION_EXYNOS +void ion_contig_heap_show_buffers(struct seq_file *s, struct ion_heap *heap, + phys_addr_t base, size_t pool_size); +#else +#define ion_contig_heap_show_buffers do { } while (0) +#endif + enum ion_event_type { ION_EVENT_TYPE_ALLOC = 0, ION_EVENT_TYPE_FREE, diff --git a/drivers/staging/android/ion/ion_exynos.h b/drivers/staging/android/ion/ion_exynos.h index c226eed56816..7855535dc524 100644 --- a/drivers/staging/android/ion/ion_exynos.h +++ b/drivers/staging/android/ion/ion_exynos.h @@ -115,6 +115,7 @@ int ion_exynos_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, int ion_exynos_dma_buf_end_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction); void ion_debug_initialize(struct ion_device *idev); +void ion_debug_heap_init(struct ion_heap *heap); #else static inline void *ion_buffer_protect_single(unsigned int protection_id, @@ -167,6 +168,7 @@ static inline int ion_exynos_dma_buf_end_cpu_access( } #define ion_debug_initialize(idev) do { } while (0) +#define ion_debug_heap_init(idev) do { } while (0) #endif extern const struct dma_buf_ops ion_dma_buf_ops; diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c index d37bebe3c4c3..87857a48eca6 100644 --- a/drivers/staging/android/ion/ion_heap.c +++ b/drivers/staging/android/ion/ion_heap.c @@ -315,33 +315,3 @@ void ion_heap_init_shrinker(struct ion_heap *heap) heap->shrinker.batch = 0; register_shrinker(&heap->shrinker); } - -void ion_contig_heap_show_buffers(struct ion_heap *heap, - phys_addr_t base, size_t pool_size) -{ - size_t total_size = 0; - struct rb_node *n; - - if (heap->type != ION_HEAP_TYPE_CARVEOUT && - heap->type != ION_HEAP_TYPE_DMA) - return; - - pr_info("ION heap '%s' of type %u and id %u, size %zu bytes\n", - heap->name, heap->type, heap->id, pool_size); - pr_info("List of buffers --------------------------------\n"); - mutex_lock(&heap->dev->buffer_lock); - for (n = rb_first(&heap->dev->buffers); n; n = rb_next(n)) { - struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, - node); - unsigned long offset; - - if (buffer->heap != heap) - continue; - - offset = (unsigned long)(sg_phys(buffer->sg_table->sgl) - base); - pr_info(" OFFSET %#010lx SIZE %zu\n", offset, buffer->size); - total_size += buffer->size; - } - mutex_unlock(&heap->dev->buffer_lock); - pr_info("Total allocated size: %zu bytes --------------\n", total_size); -} -- 2.20.1