From da8a2bb9e1ccb27061d306dfdbaa4343cf775a5b Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Wed, 20 Jun 2018 16:03:04 +0900 Subject: [PATCH] g2d: handle fence errors 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 --- drivers/gpu/exynos/g2d/g2d_fence.c | 44 ++++++++++++++++++++++- drivers/gpu/exynos/g2d/g2d_fence.h | 2 ++ drivers/gpu/exynos/g2d/g2d_task.c | 9 +++++ drivers/gpu/exynos/g2d/g2d_task.h | 1 + drivers/gpu/exynos/g2d/g2d_uapi_process.c | 3 ++ 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/exynos/g2d/g2d_fence.c b/drivers/gpu/exynos/g2d/g2d_fence.c index 6985d4962189..63ef4c8c5039 100644 --- a/drivers/gpu/exynos/g2d/g2d_fence.c +++ b/drivers/gpu/exynos/g2d/g2d_fence.c @@ -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; +} diff --git a/drivers/gpu/exynos/g2d/g2d_fence.h b/drivers/gpu/exynos/g2d/g2d_fence.h index f8ae9fbfcd34..9347e78003d3 100644 --- a/drivers/gpu/exynos/g2d/g2d_fence.h +++ b/drivers/gpu/exynos/g2d/g2d_fence.h @@ -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__*/ diff --git a/drivers/gpu/exynos/g2d/g2d_task.c b/drivers/gpu/exynos/g2d/g2d_task.c index ea5d9e2b53ad..671bee037cd5 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.c +++ b/drivers/gpu/exynos/g2d/g2d_task.c @@ -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); diff --git a/drivers/gpu/exynos/g2d/g2d_task.h b/drivers/gpu/exynos/g2d/g2d_task.h index f6e0ef2e8ffe..79d912f18a84 100644 --- a/drivers/gpu/exynos/g2d/g2d_task.h +++ b/drivers/gpu/exynos/g2d/g2d_task.h @@ -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); diff --git a/drivers/gpu/exynos/g2d/g2d_uapi_process.c b/drivers/gpu/exynos/g2d/g2d_uapi_process.c index 25033ebf01b5..e3a1aeb9b9c6 100644 --- a/drivers/gpu/exynos/g2d/g2d_uapi_process.c +++ b/drivers/gpu/exynos/g2d/g2d_uapi_process.c @@ -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); } -- 2.20.1