g2d: fix fence timeout handling
authorCho KyongHo <pullip.cho@samsung.com>
Fri, 19 Jan 2018 03:20:34 +0000 (12:20 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:28:02 +0000 (14:28 +0900)
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 <pullip.cho@samsung.com>
drivers/gpu/exynos/g2d/g2d_fence.c

index eda8056daa7a2cd545817a06fa9ef860bf6ef3d0..d95eee05d11c7a97d5356df16cbe9f13c9e2a19a 100644 (file)
@@ -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)