[RAMEN9610-12171] ion: add heap debug node
authorhyesoo.yu <hyesoo.yu@samsung.com>
Tue, 21 Aug 2018 04:46:05 +0000 (13:46 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:23:16 +0000 (20:23 +0300)
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 <hyesoo.yu@samsung.com>
drivers/staging/android/ion/ion.c
drivers/staging/android/ion/ion.h
drivers/staging/android/ion/ion_carveout_heap.c
drivers/staging/android/ion/ion_cma_heap.c
drivers/staging/android/ion/ion_debug.c
drivers/staging/android/ion/ion_debug.h
drivers/staging/android/ion/ion_exynos.h
drivers/staging/android/ion/ion_heap.c

index 47f07a0b0f971a63179524d91f1fabd0fcbc8b94..b5460b74c0038bd786bef0d9149d6cc126d7fc6a 100644 (file)
@@ -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);
 }
index aa5440cefd716647b14c3f8ea18341a8e7546d51..eb12a6c13d0ceffea9516c7281e9405e83852639 100644 (file)
@@ -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);
 
index dd4b10aca57b3df9d9245920a73fe4443d35bc5d..7177184759af5cee58529d4f2a29db96bbb7bab1 100644 (file)
@@ -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;
 }
index 96020b97cfb052f9f05aadbcc25cdf26650f89bd..1367de4b2105657b88d9dba3ced2790cce467611 100644 (file)
@@ -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);
index f2e97d717525af15983e17d3c67bbc4c39e96ec0..281ad3ecda8c79b133331dff9e431ec139765811 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/rbtree.h>
 #include <linux/debugfs.h>
 #include <linux/oom.h>
+#include <linux/sort.h>
 
 #include "ion.h"
 #include "ion_exynos.h"
 #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);
index d756a709503597b5638bd6f0852b6a6fe1fe3737..a60c5ead3b81ae28e48cb0c5b37ab2e0c8c98473 100644 (file)
 #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,
index c226eed5681615f99a7fecd6235c20276b53c55a..7855535dc52411cb74fc502eb92dc95e0c860fe7 100644 (file)
@@ -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;
index d37bebe3c4c3f30481d074961ebd507986a05f7e..87857a48eca6b96cd5fae0c043402e806054373a 100644 (file)
@@ -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);
-}