From 7aff7071722c20674478d7bf654d6bed7f71a022 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Fri, 19 Jan 2018 12:20:34 +0900 Subject: [PATCH] g2d: fix fence timeout handling If an fence timeout is caused by unsignaled fences, G2D driver forcefully schedules the task to H/W because it is not a good idea that a driver waits for a fence to be signaled undefinitely. Sometimes fence timeout is happened when a task queued in the workqueue is pended too long. We don't need to restart the fence timer again because is has no meaning to recover the situation. Moreover, it may corrupt the timer initialized for H/W timeouts if the restarting timer for fence happens later than initializing the timer for H/W. In the most cases, fence timeout is happened by unsigned fences. The counter task.starter is not zeroed when the expired task is forcefully started. It makes confusing when debugging problems related to timeouts. Change-Id: I2bdcc9df1b891cdeaf315b3380f41a99cc5a3ce0 Signed-off-by: Cho KyongHo --- drivers/gpu/exynos/g2d/g2d_fence.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/exynos/g2d/g2d_fence.c b/drivers/gpu/exynos/g2d/g2d_fence.c index eda8056daa7a..d95eee05d11c 100644 --- a/drivers/gpu/exynos/g2d/g2d_fence.c +++ b/drivers/gpu/exynos/g2d/g2d_fence.c @@ -75,13 +75,14 @@ void g2d_fence_timeout_handler(unsigned long arg) */ if (atomic_read(&task->starter.refcount.refs) == 0) { spin_unlock_irqrestore(&task->fence_timeout_lock, flags); - pr_err("All fences have been signaled. (work_busy? %d)\n", - work_busy(&task->work)); - /* If this happens again, the problem is obviously caused by the - * workqueue that does not schedule the work of this context. + pr_err("All fences are signaled. (work_busy? %d, state %#lx)\n", + work_busy(&task->work), task->state); + /* + * If this happens, there is racing between + * g2d_fence_timeout_handler() and g2d_queuework_task(). Once + * g2d_queuework_task() is invoked, it is guaranteed that the + * task is to be scheduled to H/W. */ - mod_timer(&task->timer, - jiffies + msecs_to_jiffies(G2D_FENCE_TIMEOUT_MSEC)); return; } @@ -105,6 +106,12 @@ void g2d_fence_timeout_handler(unsigned long arg) if (fence) dma_fence_remove_callback(fence, &task->target.fence_cb); + /* + * Now it is OK to init kref for g2d_start_task() below. + * All fences waiters are removed + */ + kref_init(&task->starter); + /* check compressed buffer because crashed buffer makes recovery */ for (i = 0; i < task->num_source; i++) { if (IS_AFBC( @@ -117,7 +124,7 @@ void g2d_fence_timeout_handler(unsigned long arg) g2d_stamp_task(task, G2D_STAMP_STATE_TIMEOUT_FENCE, afbc); - g2d_queuework_task(&task->starter); + kref_put(&task->starter, g2d_queuework_task); }; static const char *g2d_fence_get_driver_name(struct dma_fence *fence) -- 2.20.1