From d4feeeb89bfa210091e159773c8175f4a33a7dcf Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Thu, 22 Mar 2018 20:20:41 +0900 Subject: [PATCH] android: ion: show the list of buffers for debug debugfs/ion/buffers shows the list of buffers and the page pools of ion_system_heap. Also added an oom notifier to show the list of buffers when OOM Killer is invoked. It is very useful to see if the OOM is caused by too many ION buffers. Change-Id: I1784014099844917ad15901909a9c2c87e23f32f Signed-off-by: Cho KyongHo --- drivers/staging/android/ion/Makefile | 1 + drivers/staging/android/ion/ion.c | 2 + drivers/staging/android/ion/ion_debug.c | 188 +++++++++++++++++++++++ drivers/staging/android/ion/ion_exynos.c | 9 -- drivers/staging/android/ion/ion_exynos.h | 12 ++ 5 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 drivers/staging/android/ion/ion_debug.c diff --git a/drivers/staging/android/ion/Makefile b/drivers/staging/android/ion/Makefile index 1a7c0c815d38..f9a469e915e4 100644 --- a/drivers/staging/android/ion/Makefile +++ b/drivers/staging/android/ion/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_ION_CMA_HEAP) += ion_cma_heap.o obj-$(CONFIG_ION_HPA_HEAP) += ion_hpa_heap.o obj-$(CONFIG_ION_TEST) += ion_test.o obj-$(CONFIG_ION_EXYNOS) += ion_fdt_exynos.o ion_buffer_protect.o ion_exynos.o +obj-$(CONFIG_ION_EXYNOS) += ion_debug.o diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index a30dcbb1ddad..f2cdff7f9160 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -711,6 +711,8 @@ static int ion_device_create(void) goto debugfs_done; } + ion_debug_initialize(idev); + debugfs_done: exynos_ion_fixup(idev); idev->buffers = RB_ROOT; diff --git a/drivers/staging/android/ion/ion_debug.c b/drivers/staging/android/ion/ion_debug.c new file mode 100644 index 000000000000..2e1d71ebb731 --- /dev/null +++ b/drivers/staging/android/ion/ion_debug.c @@ -0,0 +1,188 @@ +/* + * drivers/staging/android/ion/ion_debug.c + * + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +#include "ion.h" +#include "ion_exynos.h" + +#define ION_MAX_LOGBUF 128 + +static size_t ion_print_buffer(struct ion_buffer *buffer, bool alive, + char *logbuf, int buflen) +{ + struct ion_iovm_map *iovm; + int count; + + count = scnprintf(logbuf, buflen, "%15s %#5lx %8zu : ", + buffer->heap->name, 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; + } + scnprintf(logbuf, buflen, "\n"); + } else { + scnprintf(logbuf, buflen, "(freed)\n"); + } + + return buffer->size; +} + +static int ion_debug_buffers_show(struct seq_file *s, void *unused) +{ + 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, "%15s %5s %8s : %s\n", + "heap", "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); + seq_puts(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); + seq_puts(s, logbuf); + } + spin_unlock(&heap->free_lock); + } + up_read(&idev->lock); + + seq_printf(s, "TOTAL: %zu kb\n\n", 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); + + return 0; +} + +static int ion_debug_buffers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_buffers_show, inode->i_private); +} + +static const struct file_operations debug_buffers_fops = { + .open = ion_debug_buffers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct ion_oom_notifier_struct { + struct notifier_block nb; + struct ion_device *idev; +}; + +static int ion_oom_notifier_fn(struct notifier_block *nb, + unsigned long action, void *data) +{ + 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("%15s %5s %8s : %s\n", + "heap", "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); + + return 0; +} + +static struct ion_oom_notifier_struct ion_oom_notifier = { + .nb = { .notifier_call = ion_oom_notifier_fn} +}; + +void ion_debug_initialize(struct ion_device *idev) +{ + struct dentry *buffer_file; + + buffer_file = debugfs_create_file("buffers", 0444, idev->debug_root, + idev, &debug_buffers_fops); + if (!buffer_file) + pr_err("%s: failed to create debugfs/ion/buffers\n", __func__); + + register_oom_notifier(&ion_oom_notifier.nb); +} diff --git a/drivers/staging/android/ion/ion_exynos.c b/drivers/staging/android/ion/ion_exynos.c index efdbe3e9b4be..853202d6271a 100644 --- a/drivers/staging/android/ion/ion_exynos.c +++ b/drivers/staging/android/ion/ion_exynos.c @@ -46,15 +46,6 @@ bool ion_hwrender_dmabuf(struct dma_buf *dmabuf) return !!(buffer->flags & ION_FLAG_MAY_HWRENDER); } -struct ion_iovm_map { - struct list_head list; - struct device *dev; - struct iommu_domain *domain; - dma_addr_t iova; - atomic_t mapcnt; - int prop; -}; - static struct ion_iovm_map *ion_buffer_iova_create(struct ion_buffer *buffer, struct device *dev, enum dma_data_direction dir, diff --git a/drivers/staging/android/ion/ion_exynos.h b/drivers/staging/android/ion/ion_exynos.h index f6a8e87519c1..e36e3d0659db 100644 --- a/drivers/staging/android/ion/ion_exynos.h +++ b/drivers/staging/android/ion/ion_exynos.h @@ -46,6 +46,15 @@ struct ion_buffer_prot_info { unsigned long bus_address; }; +struct ion_iovm_map { + struct list_head list; + struct device *dev; + struct iommu_domain *domain; + dma_addr_t iova; + atomic_t mapcnt; + int prop; +}; + #ifdef CONFIG_ION_CARVEOUT_HEAP extern struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *); #else @@ -90,6 +99,8 @@ void ion_exynos_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *table, enum dma_data_direction direction); +void ion_debug_initialize(struct ion_device *idev); + #else static inline void *ion_buffer_protect_single(unsigned int protection_id, unsigned int size, @@ -125,6 +136,7 @@ static inline struct sg_table *ion_exynos_map_dma_buf( } #define ion_exynos_unmap_dma_buf(attachment, table, direction) do { } while (0) +#define ion_debug_initialize(idev) do { } while (0) #endif extern const struct dma_buf_ops ion_dma_buf_ops; -- 2.20.1