From: hyesoo.yu Date: Wed, 24 May 2017 11:27:49 +0000 (+0900) Subject: [COMMON] g2d: add debugging feature X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=4845efb4b8422858c40bce18834fd0590037dd4e;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [COMMON] g2d: add debugging feature remove the spinlock on g2d_dump_task to avoid spinlock lockup and add the stamp at each task state for debug Change-Id: I449dbe41d56bdbb0dc468b970e2c9102c2b46979 Signed-off-by: hyesoo.yu --- diff --git a/drivers/gpu/exynos/g2d/Makefile b/drivers/gpu/exynos/g2d/Makefile index 0cfe4f68f64e..1db499152d20 100644 --- a/drivers/gpu/exynos/g2d/Makefile +++ b/drivers/gpu/exynos/g2d/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_drv.o g2d_task.o g2d_regs.o obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_uapi_process.o g2d_command.o -obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_fence.o +obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_fence.o g2d_debug.o diff --git a/drivers/gpu/exynos/g2d/g2d.h b/drivers/gpu/exynos/g2d/g2d.h index 4fdff95ae460..b2856fbff56c 100644 --- a/drivers/gpu/exynos/g2d/g2d.h +++ b/drivers/gpu/exynos/g2d/g2d.h @@ -50,6 +50,10 @@ struct g2d_device { struct notifier_block pm_notifier; wait_queue_head_t freeze_wait; + + struct dentry *debug_root; + struct dentry *debug; + struct dentry *debug_logs; }; struct g2d_context { diff --git a/drivers/gpu/exynos/g2d/g2d_debug.c b/drivers/gpu/exynos/g2d/g2d_debug.c new file mode 100644 index 000000000000..aea9c9bf5f00 --- /dev/null +++ b/drivers/gpu/exynos/g2d/g2d_debug.c @@ -0,0 +1,193 @@ +/* + * linux/drivers/gpu/exynos/g2d/g2d_debug.c + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 "g2d.h" +#include "g2d_task.h" +#include "g2d_uapi.h" +#include "g2d_debug.h" + +static unsigned int g2d_debug; + +#define G2D_MAX_STAMP_SIZE 1024 + +static struct g2d_stamp { + ktime_t time; + struct g2d_task *task; + u32 state; + u32 job_id; + u32 val; + u8 cpu; +} g2d_stamp_list[G2D_MAX_STAMP_SIZE]; + +static atomic_t p_stamp; + +static int g2d_stamp_show(struct seq_file *s, void *unused) +{ + int ptr = atomic_read(&p_stamp); + struct g2d_stamp *stamp; + int i; + + if (ptr < 0) + return 0; + + /* in chronological order */ + ptr = (ptr + 1) & (G2D_MAX_STAMP_SIZE - 1); + i = ptr; + + while (1) { + stamp = &g2d_stamp_list[i]; + + seq_printf(s, "[%d] %u:%u@%u (0x%x) %06llu\n", i++, + stamp->cpu, stamp->job_id, stamp->val, stamp->state, + ktime_to_us(stamp->time)); + + i &= (G2D_MAX_STAMP_SIZE - 1); + + if (i == ptr) + break; + } + + return 0; +} + +static int g2d_debug_logs_open(struct inode *inode, struct file *file) +{ + return single_open(file, g2d_stamp_show, inode->i_private); +} + +static const struct file_operations g2d_debug_logs_fops = { + .open = g2d_debug_logs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void g2d_init_debug(struct g2d_device *g2d_dev) +{ + atomic_set(&p_stamp, -1); + + g2d_dev->debug_root = debugfs_create_dir("g2d", NULL); + if (!g2d_dev->debug_root) { + dev_err(g2d_dev->dev, "debugfs : failed to create root directory\n"); + return; + } + + g2d_dev->debug = debugfs_create_u32("debug", + 0644, g2d_dev->debug_root, &g2d_debug); + if (!g2d_dev->debug) { + dev_err(g2d_dev->dev, "debugfs : failed to create debug file\n"); + return; + } + + g2d_dev->debug_logs = debugfs_create_file("logs", + 0444, g2d_dev->debug_root, g2d_dev, &g2d_debug_logs_fops); + if (!g2d_dev->debug_logs) { + dev_err(g2d_dev->dev, "debugfs : failed to create debug logs file\n"); + return; + } +} + +void g2d_destroy_debug(struct g2d_device *g2d_dev) +{ + debugfs_remove_recursive(g2d_dev->debug_root); +} + +static struct regs_info g2d_reg_info[] = { + /* Start, Size, Name */ + { 0x0, 0x20, "General" }, + { 0x34, 0x10, "Secure Layer" }, + { 0xF0, 0x10, "AFBC debugging" }, + { 0x80, 0x70, "Job manager" }, + { 0x8000, 0x100, "HW flow control" }, + { 0x2000, 0x120, "Layer CSC Coefficient" }, + { 0x120, 0xE0, "Destination" }, + { 0x200, 0x100, "Layer0" }, + { 0x300, 0x100, "Layer1" }, + { 0x400, 0x100, "Layer2" }, + { 0x500, 0x100, "Layer3" }, + { 0x600, 0x100, "Layer4" }, + { 0x700, 0x100, "Layer5" }, + { 0x800, 0x100, "Layer6" }, + { 0x900, 0x100, "Layer7" }, + { 0xA00, 0x100, "Layer8" }, + { 0xB00, 0x100, "Layer9" }, + { 0xC00, 0x100, "Layer10" }, + { 0xD00, 0x100, "Layer11" }, + { 0xE00, 0x100, "Layer12" }, + { 0xF00, 0x100, "Layer13" }, + { 0x1000, 0x100, "Layer14" }, + { 0x1100, 0x100, "Layer15" }, +}; + +static void g2d_dump_task(struct g2d_task *task) +{ + struct g2d_device *g2d_dev = task->g2d_dev; + unsigned int i, num_array; + struct g2d_reg *regs; + + num_array = ARRAY_SIZE(g2d_reg_info) - G2D_MAX_IMAGES + task->num_source; + + for (i = 0; i < num_array; i++) { + pr_info("[%s: %04X .. %04X]\n", + g2d_reg_info[i].name, g2d_reg_info[i].start, + g2d_reg_info[i].start + g2d_reg_info[i].size); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, + g2d_dev->reg + g2d_reg_info[i].start, + g2d_reg_info[i].size, false); + } + + regs = page_address(task->cmd_page); + + for (i = 0; i < task->cmd_count; i++) + pr_info("G2D: CMD[%03d] %#06x, %#010x\n", + i, regs[i].offset, regs[i].value); +} + +void g2d_stamp_task(struct g2d_task *task, u32 val) +{ + int ptr = atomic_inc_return(&p_stamp) & (G2D_MAX_STAMP_SIZE - 1); + struct g2d_stamp *stamp = &g2d_stamp_list[ptr]; + + if (task) { + stamp->state = task->state; + stamp->job_id = task->job_id; + stamp->task = task; + } else { + stamp->task = NULL; + } + + stamp->time = ktime_get(); + stamp->val = val; + stamp->cpu = smp_processor_id(); + + /* when error status, dump the task */ + if ((stamp->val == G2D_STAMP_STATE_TIMEOUT_HW) || + (stamp->val == G2D_STAMP_STATE_ERR_INT) || + (stamp->val == G2D_STAMP_STATE_MMUFAULT)) + g2d_dump_task(task); + + if (stamp->val == G2D_STAMP_STATE_DONE) { + if (g2d_debug == 1) { + pr_info("Job #%x took %06llu to H/W process\n", + task->job_id, ktime_us_delta(task->ktime_end, task->ktime_begin)); + } else if (g2d_debug == 2) { + g2d_dump_task(task); + } + } +} diff --git a/drivers/gpu/exynos/g2d/g2d_debug.h b/drivers/gpu/exynos/g2d/g2d_debug.h new file mode 100644 index 000000000000..17a768087521 --- /dev/null +++ b/drivers/gpu/exynos/g2d/g2d_debug.h @@ -0,0 +1,50 @@ +/* + * linux/drivers/gpu/exynos/g2d/g2d_debug.h + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + * Contact: Hyesoo Yu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __EXYNOS_G2D_DEBUG_H_ +#define __EXYNOS_G2D_DEBUG_H_ + +struct regs_info { + int start; + int size; + const char *name; +}; + +enum g2d_stamp_id { + G2D_STAMP_STATE_ALLOC, + G2D_STAMP_STATE_BEGIN, + G2D_STAMP_STATE_PM_RESUME, + G2D_STAMP_STATE_PUSH, + G2D_STAMP_STATE_DONE, + G2D_STAMP_STATE_PM_SUSPEND, + G2D_STAMP_STATE_FREE, + G2D_STAMP_STATE_TIMEOUT_FENCE, + G2D_STAMP_STATE_TIMEOUT_HW, + G2D_STAMP_STATE_ERR_INT, + G2D_STAMP_STATE_MMUFAULT, + G2D_STAMP_STATE_SHUTDOWN_S, + G2D_STAMP_STATE_SHUTDOWN_E, + G2D_STAMP_STATE_SUSPEND_S, + G2D_STAMP_STATE_SUSPEND_E, + G2D_STAMP_STATE_RESUME_S, + G2D_STAMP_STATE_RESUME_E, +}; + +void g2d_init_debug(struct g2d_device *dev); +void g2d_destroy_debug(struct g2d_device *dev); +void g2d_stamp_task(struct g2d_task *task, u32 val); +#endif /* __EXYNOS_G2D_HELPER_H_ */ diff --git a/drivers/gpu/exynos/g2d/g2d_drv.c b/drivers/gpu/exynos/g2d/g2d_drv.c index 0d1afbd578fc..4b5ad3333559 100644 --- a/drivers/gpu/exynos/g2d/g2d_drv.c +++ b/drivers/gpu/exynos/g2d/g2d_drv.c @@ -29,6 +29,7 @@ #include "g2d_regs.h" #include "g2d_task.h" #include "g2d_uapi_process.h" +#include "g2d_debug.h" #define MODULE_NAME "exynos-g2d" @@ -81,6 +82,8 @@ void g2d_hw_timeout_handler(unsigned long arg) /* Time out is not caused by this task */ goto out; + g2d_stamp_task(task, G2D_STAMP_STATE_TIMEOUT_HW); + mark_task_state_killed(task); g2d_hw_kill_task(g2d_dev, task->job_id); @@ -93,6 +96,8 @@ int g2d_device_run(struct g2d_device *g2d_dev, struct g2d_task *task) { g2d_hw_push_task(g2d_dev, task); + g2d_stamp_task(task, G2D_STAMP_STATE_PUSH); + return 0; } @@ -133,7 +138,7 @@ static irqreturn_t g2d_irq_handler(int irq, void *priv) "%s: Error occurred during running job %d\n", __func__, job_id); - g2d_dump_task(g2d_dev, job_id); + g2d_stamp_task(task, G2D_STAMP_STATE_ERR_INT); } g2d_flush_all_tasks(g2d_dev); @@ -156,9 +161,15 @@ static int g2d_iommu_fault_handler(struct iommu_domain *domain, int fault_flags, void *token) { struct g2d_device *g2d_dev = token; + struct g2d_task *task; int job_id = g2d_hw_get_current_task(g2d_dev); + unsigned long flags; + + spin_lock_irqsave(&g2d_dev->lock_task, flags); + task = g2d_get_active_task_from_id(g2d_dev, job_id); + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); - g2d_dump_task(g2d_dev, job_id); + g2d_stamp_task(task, G2D_STAMP_STATE_MMUFAULT); return 0; } @@ -248,6 +259,8 @@ static long g2d_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) break; } + g2d_stamp_task(task, G2D_STAMP_STATE_BEGIN); + g2d_start_task(task); if (!(task->flags & G2D_FLAG_NONBLOCK)) @@ -384,6 +397,8 @@ static int g2d_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probed FIMG2D version %#010x\n", version); + g2d_init_debug(g2d_dev); + return 0; err_pm: g2d_destroy_tasks(g2d_dev); @@ -402,18 +417,23 @@ static void g2d_shutdown(struct platform_device *pdev) { struct g2d_device *g2d_dev = platform_get_drvdata(pdev); + g2d_stamp_task(NULL, G2D_STAMP_STATE_SHUTDOWN_S); g2d_prepare_suspend(g2d_dev); wait_event(g2d_dev->freeze_wait, list_empty(&g2d_dev->tasks_active)); if (test_and_set_bit(G2D_DEVICE_STATE_IOVMM_DISABLED, &g2d_dev->state)) iovmm_deactivate(g2d_dev->dev); + + g2d_stamp_task(NULL, G2D_STAMP_STATE_SHUTDOWN_E); } static int g2d_remove(struct platform_device *pdev) { struct g2d_device *g2d_dev = platform_get_drvdata(pdev); + g2d_destroy_debug(g2d_dev); + g2d_shutdown(pdev); g2d_destroy_tasks(g2d_dev); @@ -428,6 +448,8 @@ static int g2d_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int g2d_runtime_resume(struct device *dev) { + g2d_stamp_task(NULL, G2D_STAMP_STATE_PM_RESUME); + return 0; } @@ -436,6 +458,7 @@ static int g2d_runtime_suspend(struct device *dev) struct g2d_device *g2d_dev = dev_get_drvdata(dev); clk_unprepare(g2d_dev->clock); + g2d_stamp_task(NULL, G2D_STAMP_STATE_PM_SUSPEND); return 0; } diff --git a/drivers/gpu/exynos/g2d/g2d_fence.c b/drivers/gpu/exynos/g2d/g2d_fence.c index 3a021482d29e..44c29f6e5e92 100644 --- a/drivers/gpu/exynos/g2d/g2d_fence.c +++ b/drivers/gpu/exynos/g2d/g2d_fence.c @@ -23,6 +23,7 @@ #include "g2d_uapi.h" #include "g2d_task.h" #include "g2d_fence.h" +#include "g2d_debug.h" void g2d_fence_timeout_handler(unsigned long arg) { @@ -106,6 +107,8 @@ void g2d_fence_timeout_handler(unsigned long arg) g2d_queuework_task(&task->starter); spin_unlock_irqrestore(&task->fence_timeout_lock, flags); + + g2d_stamp_task(task, G2D_STAMP_STATE_TIMEOUT_FENCE); }; static const char *g2d_fence_get_driver_name(struct dma_fence *fence) diff --git a/drivers/gpu/exynos/g2d/g2d_task.c b/drivers/gpu/exynos/g2d/g2d_task.c index 8b6c0faaa461..f26852328bf0 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.c +++ b/drivers/gpu/exynos/g2d/g2d_task.c @@ -23,6 +23,7 @@ #include "g2d_uapi_process.h" #include "g2d_command.h" #include "g2d_fence.h" +#include "g2d_debug.h" #ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION #include @@ -146,6 +147,8 @@ static void g2d_finish_task(struct g2d_device *g2d_dev, { list_del_init(&task->node); + g2d_stamp_task(task, G2D_STAMP_STATE_DONE); + del_timer(&task->timer); g2d_secure_disable(); @@ -226,7 +229,9 @@ void g2d_prepare_suspend(struct g2d_device *g2d_dev) set_bit(G2D_DEVICE_STATE_SUSPEND, &g2d_dev->state); spin_unlock_irq(&g2d_dev->lock_task); + g2d_stamp_task(NULL, G2D_STAMP_STATE_SUSPEND_S); wait_event(g2d_dev->freeze_wait, list_empty(&g2d_dev->tasks_active)); + g2d_stamp_task(NULL, G2D_STAMP_STATE_SUSPEND_E); } void g2d_suspend_finish(struct g2d_device *g2d_dev) @@ -235,6 +240,7 @@ void g2d_suspend_finish(struct g2d_device *g2d_dev) spin_lock_irq(&g2d_dev->lock_task); + g2d_stamp_task(NULL, G2D_STAMP_STATE_RESUME_S); clear_bit(G2D_DEVICE_STATE_SUSPEND, &g2d_dev->state); while (!list_empty(&g2d_dev->tasks_prepared)) { @@ -245,6 +251,7 @@ void g2d_suspend_finish(struct g2d_device *g2d_dev) } spin_unlock_irq(&g2d_dev->lock_task); + g2d_stamp_task(NULL, G2D_STAMP_STATE_RESUME_E); } static void g2d_schedule_task(struct g2d_task *task) @@ -359,6 +366,8 @@ struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev) g2d_init_commands(task); + g2d_stamp_task(task, G2D_STAMP_STATE_ALLOC); + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); return task; @@ -374,6 +383,8 @@ void g2d_put_free_task(struct g2d_device *g2d_dev, struct g2d_task *task) list_add(&task->node, &g2d_dev->tasks_free); + g2d_stamp_task(task, G2D_STAMP_STATE_FREE); + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); } @@ -469,28 +480,3 @@ int g2d_create_tasks(struct g2d_device *g2d_dev) return 0; } - -void g2d_dump_task(struct g2d_device *g2d_dev, unsigned int job_id) -{ - struct g2d_task *task; - unsigned long flags; - unsigned int i; - struct g2d_reg *regs; - - spin_lock_irqsave(&g2d_dev->lock_task, flags); - - list_for_each_entry(task, &g2d_dev->tasks_active, node) { - if (task->job_id == job_id) - break; - } - - /* TODO: more dump task */ - - regs = page_address(task->cmd_page); - - for (i = 0; i < task->cmd_count; i++) - pr_info("G2D: CMD[%03d] %#06x, %#010x\n", - i, regs[i].offset, regs[i].value); - - spin_unlock_irqrestore(&g2d_dev->lock_task, flags); -} diff --git a/drivers/gpu/exynos/g2d/g2d_task.h b/drivers/gpu/exynos/g2d/g2d_task.h index b8139febc2f5..18d28cd28440 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.h +++ b/drivers/gpu/exynos/g2d/g2d_task.h @@ -184,7 +184,6 @@ void g2d_suspend_finish(struct g2d_device *g2d_dev); void g2d_fence_callback(struct dma_fence *fence, struct dma_fence_cb *cb); -void g2d_dump_task(struct g2d_device *g2d_dev, unsigned int job_id); void g2d_queuework_task(struct kref *kref); #endif /*__EXYNOS_G2D_TASK_H__*/ diff --git a/drivers/gpu/exynos/g2d/g2d_uapi_process.c b/drivers/gpu/exynos/g2d/g2d_uapi_process.c index 67b86baf2f19..26e1e29ea5c2 100644 --- a/drivers/gpu/exynos/g2d/g2d_uapi_process.c +++ b/drivers/gpu/exynos/g2d/g2d_uapi_process.c @@ -32,7 +32,6 @@ #include "g2d_command.h" #include "g2d_fence.h" - unsigned int get_layer_payload(struct g2d_layer *layer) { unsigned int payload = 0;