From: Cho KyongHo Date: Sun, 23 Apr 2017 12:26:32 +0000 (+0900) Subject: [COMMON] g2d: cancel jobs running too much time X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=4aa4161296a3fe5c12abffeccb68f0ced7613111;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [COMMON] g2d: cancel jobs running too much time We might face with an unexpected accident that G2D does not complete a pushed job in a definite time. We should be able to identify that jobs and to discard them to make the H/W predictable. We decided 500 milliseconds for the dead-line of the completion of a job running in the H/W. If H/W does not complete a job in 500 msec., The driver kills the job running in H/W and check if it is really killed. If it is not killed in the next 500 msec., the driver initializes H/W which means that all the jobs running in H/W are discarded. Change-Id: I8119a476bb2e736380031f8277d04caf16baf2ba Signed-off-by: hyesoo.yu Signed-off-by: Cho KyongHo --- diff --git a/drivers/gpu/exynos/g2d/g2d.h b/drivers/gpu/exynos/g2d/g2d.h index 1b2afe12e55a..38bd33b23e7d 100644 --- a/drivers/gpu/exynos/g2d/g2d.h +++ b/drivers/gpu/exynos/g2d/g2d.h @@ -46,5 +46,6 @@ struct g2d_context { #define g2d_job_empty(id, job_mask) ((id & job_mask) == 0) int g2d_device_run(struct g2d_device *g2d_dev, struct g2d_task *task); +void g2d_hw_timeout_handler(unsigned long arg); #endif /* __EXYNOS_G2D_H__ */ diff --git a/drivers/gpu/exynos/g2d/g2d_drv.c b/drivers/gpu/exynos/g2d/g2d_drv.c index 41a2496f084d..99325b88dac2 100644 --- a/drivers/gpu/exynos/g2d/g2d_drv.c +++ b/drivers/gpu/exynos/g2d/g2d_drv.c @@ -30,6 +30,63 @@ #define MODULE_NAME "exynos-g2d" +void g2d_hw_timeout_handler(unsigned long arg) +{ + struct g2d_task *task = (struct g2d_task *)arg; + struct g2d_device *g2d_dev = task->g2d_dev; + unsigned long flags; + u32 job_state; + + /* TODO: Dump of internal state of G2D */ + + dev_err(g2d_dev->dev, "%s: Time is up: %d msec for job %d\n", + __func__, G2D_HW_TIMEOUT_MSEC, task->job_id); + + spin_lock_irqsave(&g2d_dev->lock_task, flags); + + if (!is_task_state_active(task)) + /* + * The task timed out is not currently running in H/W. + * It might be just finished by interrupt. + */ + goto out; + + job_state = g2d_hw_get_job_state(g2d_dev, task->job_id); + if (job_state == G2D_JOB_STATE_DONE) + /* + * The task timed out is not currently running in H/W. + * It will be processed in the interrupt handler. + */ + goto out; + + if (is_task_state_killed(task)) { + /* The killed task is not died in the time out priod. */ + g2d_hw_global_reset(g2d_dev); + + g2d_flush_all_tasks(g2d_dev); + + dev_err(g2d_dev->dev, + "GLOBAL RESET: killed task not dead in %d msec.\n", + G2D_HW_TIMEOUT_MSEC); + goto out; + } + + mod_timer(&task->hw_timer, + jiffies + msecs_to_jiffies(G2D_HW_TIMEOUT_MSEC)); + + if (job_state != G2D_JOB_STATE_RUNNING) + /* G2D_JOB_STATE_QUEUEING or G2D_JOB_STATE_SUSPENDING */ + /* Time out is not caused by this task */ + goto out; + + mark_task_state_killed(task); + + g2d_hw_kill_task(g2d_dev, task->job_id); + +out: + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); +} + int g2d_device_run(struct g2d_device *g2d_dev, struct g2d_task *task) { g2d_hw_push_task(g2d_dev, task); diff --git a/drivers/gpu/exynos/g2d/g2d_regs.c b/drivers/gpu/exynos/g2d/g2d_regs.c index 504fc20d2ea2..c33104ffd8b1 100644 --- a/drivers/gpu/exynos/g2d/g2d_regs.c +++ b/drivers/gpu/exynos/g2d/g2d_regs.c @@ -84,3 +84,20 @@ int g2d_hw_get_current_task(struct g2d_device *g2d_dev) return -1; } + +void g2d_hw_kill_task(struct g2d_device *g2d_dev, unsigned int job_id) +{ + int retry_count = 120; + + writel((0 << 4) | job_id, g2d_dev->reg + G2D_JOB_KILL_REG); + + while (retry_count-- > 0) { + if (!(readl(g2d_dev->reg + G2D_JOB_PUSHKILL_STATE_REG) & 0x2)) { + dev_err(g2d_dev->dev, + "%s: Killed JOB %d\n", __func__, job_id); + return; + } + } + + dev_err(g2d_dev->dev, "%s: Failed to kill job %d\n", __func__, job_id); +} diff --git a/drivers/gpu/exynos/g2d/g2d_regs.h b/drivers/gpu/exynos/g2d/g2d_regs.h index 43d62c280894..9f9ff6a48407 100644 --- a/drivers/gpu/exynos/g2d/g2d_regs.h +++ b/drivers/gpu/exynos/g2d/g2d_regs.h @@ -76,6 +76,7 @@ void g2d_hw_push_task(struct g2d_device *g2d_dev, struct g2d_task *task); int g2d_hw_get_current_task(struct g2d_device *g2d_dev); +void g2d_hw_kill_task(struct g2d_device *g2d_dev, unsigned int job_id); static inline u32 g2d_hw_finished_job_ids(struct g2d_device *g2d_dev) { diff --git a/drivers/gpu/exynos/g2d/g2d_task.c b/drivers/gpu/exynos/g2d/g2d_task.c index cce2d86c3faf..6fb3462b8263 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.c +++ b/drivers/gpu/exynos/g2d/g2d_task.c @@ -51,6 +51,8 @@ static void g2d_finish_task(struct g2d_device *g2d_dev, { list_del_init(&task->node); + del_timer(&task->hw_timer); + clk_disable(g2d_dev->clock); pm_runtime_put(g2d_dev->dev); @@ -67,6 +69,12 @@ void g2d_finish_task_with_id(struct g2d_device *g2d_dev, if (!task) return; + if (is_task_state_killed(task)) { + dev_err(g2d_dev->dev, "%s: Killed task ID %d is completed\n", + __func__, job_id); + success = false; + } + task->ktime_end = ktime_get(); g2d_finish_task(g2d_dev, task, success); @@ -137,6 +145,9 @@ static void g2d_schedule_task(struct g2d_task *task) goto err_run; } + mod_timer(&task->hw_timer, + jiffies + msecs_to_jiffies(G2D_HW_TIMEOUT_MSEC)); + spin_unlock_irqrestore(&g2d_dev->lock_task, flags); return; err_run: @@ -249,6 +260,9 @@ static struct g2d_task *g2d_create_task(struct g2d_device *g2d_dev) if (!task->cmd_page) goto err_page; + setup_timer(&task->hw_timer, + g2d_hw_timeout_handler, (unsigned long)task); + /* mapping the command data */ sg_init_table(&sgl, 1); sg_set_page(&sgl, task->cmd_page, G2D_CMD_LIST_SIZE, 0); diff --git a/drivers/gpu/exynos/g2d/g2d_task.h b/drivers/gpu/exynos/g2d/g2d_task.h index 6fb09569485e..b65459f7f00b 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.h +++ b/drivers/gpu/exynos/g2d/g2d_task.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "g2d_format.h" @@ -74,6 +75,7 @@ struct g2d_task { unsigned int job_id; unsigned long state; struct kref starter; + struct timer_list hw_timer; struct g2d_layer source[G2D_MAX_IMAGES]; struct g2d_layer target; @@ -130,6 +132,11 @@ static inline void clear_task_state(struct g2d_task *task) task->state = 0; } +#define is_task_state_active(task) (((task)->state & G2D_TASKSTATE_ACTIVE) != 0) +#define is_task_state_killed(task) (((task)->state & G2D_TASKSTATE_KILLED) != 0) + +#define G2D_HW_TIMEOUT_MSEC 500 + struct g2d_task *g2d_get_active_task_from_id(struct g2d_device *g2d_dev, unsigned int id); void g2d_destroy_tasks(struct g2d_device *g2d_dev);