g2d: handle fence errors
authorCho KyongHo <pullip.cho@samsung.com>
Wed, 20 Jun 2018 07:03:04 +0000 (16:03 +0900)
committerJanghyuck Kim <janghyuck.kim@samsung.com>
Mon, 23 Jul 2018 05:39:36 +0000 (14:39 +0900)
dma_fence supports delivering error to the fence waiters. The fences
with error are also signaled like good fences. The waiters then should
check if the signaled fence has error status.
G2D driver now checks the status of all signaled before pushing a task
to H/W. If it finds an error from  the fences, it does not run H/W with
the task and push the back to the free task pool after resolving all
acquired resources including fences attached to the task. If the task
has a release fence, it is also signaled with -EIO error status.
G2D driver also signals release fences with error status when the a
fence is timed out or H/W does not incur interrupt until the deadline.
Signaling release fence with an error is helpful for the system
stability because the reader of the G2D output can determine if the
data from G2D is complete.

Change-Id: Ia54fafed45d1f8f0e0e1c2e3e81f084746ef02e5
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
drivers/gpu/exynos/g2d/g2d_fence.c
drivers/gpu/exynos/g2d/g2d_fence.h
drivers/gpu/exynos/g2d/g2d_task.c
drivers/gpu/exynos/g2d/g2d_task.h
drivers/gpu/exynos/g2d/g2d_uapi_process.c

index 6985d4962189dbe2f4a7bd71b2364ba49542e03f..63ef4c8c503944c7068fa2b6f5681f77ac1eac0c 100644 (file)
@@ -124,7 +124,7 @@ void g2d_fence_timeout_handler(unsigned long arg)
 
        g2d_stamp_task(task, G2D_STAMP_STATE_TIMEOUT_FENCE, afbc);
 
-       kref_put(&task->starter, g2d_queuework_task);
+       g2d_cancel_task(task);
 };
 
 static const char *g2d_fence_get_driver_name(struct dma_fence *fence)
@@ -245,3 +245,45 @@ struct dma_fence *g2d_get_acquire_fence(struct g2d_device *g2d_dev,
 
        return fence;
 }
+
+static bool g2d_fence_has_error(struct g2d_layer *layer, int layer_idx)
+{
+       struct dma_fence *fence = layer->fence;
+       unsigned long flags;
+       bool err = false;
+
+       if (fence) {
+               spin_lock_irqsave(fence->lock, flags);
+               err = dma_fence_get_status_locked(fence) < 0;
+               spin_unlock_irqrestore(fence->lock, flags);
+       }
+
+       if (err) {
+               char name[32];
+
+               strlcpy(name, fence->ops->get_driver_name(fence), sizeof(name));
+
+               dev_err(layer->task->g2d_dev->dev,
+                       "%s: Error fence of %s%d found: %s#%d\n", __func__,
+                       (layer_idx < 0) ? "target" : "source",
+                       layer_idx, name, fence->seqno);
+
+               return true;
+       }
+
+       return false;
+}
+
+bool g2d_task_has_error_fence(struct g2d_task *task)
+{
+       unsigned int i;
+
+       for (i = 0; i < task->num_source; i++)
+               if (g2d_fence_has_error(&task->source[i], i))
+                       return false;
+
+       if (g2d_fence_has_error(&task->target, -1))
+               return true;
+
+       return false;
+}
index f8ae9fbfcd3445e603eb7aadfe23d75184329bbb..9347e78003d313c2e256722f7fa2b72525edec90 100644 (file)
@@ -28,4 +28,6 @@ struct dma_fence *g2d_get_acquire_fence(struct g2d_device *g2d_dev,
 struct sync_file *g2d_create_release_fence(struct g2d_device *g2d_dev,
                                           struct g2d_task *task,
                                           struct g2d_task_data *data);
+bool g2d_task_has_error_fence(struct g2d_task *task);
+
 #endif /*__EXYNOS_G2D_FENCE_H__*/
index ea5d9e2b53ad938ce44cf11d392150bfb0c268e8..671bee037cd5f3971d7dbd9cb735f26a623c1edd 100644 (file)
@@ -223,6 +223,9 @@ static void g2d_schedule_task(struct g2d_task *task)
 
        del_timer(&task->fence_timer);
 
+       if (g2d_task_has_error_fence(task))
+               goto err_fence;
+
        g2d_complete_commands(task);
 
        /*
@@ -258,6 +261,7 @@ static void g2d_schedule_task(struct g2d_task *task)
 err_clk:
        pm_runtime_put(g2d_dev->dev);
 err_pm:
+err_fence:
        __g2d_finish_task(task, false);
 }
 
@@ -300,6 +304,11 @@ void g2d_start_task(struct g2d_task *task)
        kref_put(&task->starter, g2d_task_direct_schedule);
 }
 
+void g2d_cancel_task(struct g2d_task *task)
+{
+       __g2d_finish_task(task, false);
+}
+
 void g2d_fence_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
 {
        struct g2d_layer *layer = container_of(cb, struct g2d_layer, fence_cb);
index f6e0ef2e8ffec658781e89b51f8d8e952469befe..79d912f18a848d97ad07a0c27ef13d5d0ba1501b 100644 (file)
@@ -182,6 +182,7 @@ struct g2d_task *g2d_get_free_task(struct g2d_device *g2d_dev,
 void g2d_put_free_task(struct g2d_device *g2d_dev, struct g2d_task *task);
 
 void g2d_start_task(struct g2d_task *task);
+void g2d_cancel_task(struct g2d_task *task);
 void g2d_finish_task_with_id(struct g2d_device *g2d_dev,
                             unsigned int job_id, bool success);
 void g2d_flush_all_tasks(struct g2d_device *g2d_dev);
index 25033ebf01b520bd8488f620d23ae1216689d564..e3a1aeb9b9c659981f2cc3bd8c32e60a61573dc5 100644 (file)
@@ -810,6 +810,9 @@ void g2d_put_images(struct g2d_device *g2d_dev, struct g2d_task *task)
        g2d_invalidate_caches_task(task);
 
        if (task->release_fence) {
+               if (is_task_state_error(task))
+                       dma_fence_set_error(task->release_fence->fence, -EIO);
+
                dma_fence_signal(task->release_fence->fence);
                fput(task->release_fence->file);
        }