diff options
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/adreno_gpu.c')
-rw-r--r-- | drivers/gpu/drm/msm/adreno/adreno_gpu.c | 59 |
1 files changed, 56 insertions, 3 deletions
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index c0bf75b47ef6..5e10446f7798 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -60,6 +60,50 @@ const char *adreno_get_name(struct adreno_gpu *gpu) return gpu->info->name; } +/* + * Hangcheck detection for locked gpu: + */ + +static void recover_worker(struct work_struct *work) +{ + struct adreno_gpu *gpu = container_of(work, struct adreno_gpu, recover_work); + struct drm_device *dev = gpu->drm; + + dev_err(dev->dev, "%s: hangcheck recover!\n", adreno_get_name(gpu)); + + mutex_lock(&dev->struct_mutex); + gpu->funcs->recover(gpu); + mutex_unlock(&dev->struct_mutex); + + gpu->gem->gem_retire(gpu->gem->priv); +} + +static void hangcheck_timer_reset(struct adreno_gpu *gpu) +{ + DBG("%s", adreno_get_name(gpu)); + mod_timer(&gpu->hangcheck_timer, + round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES)); +} + +static void hangcheck_handler(unsigned long data) +{ + struct adreno_gpu *gpu = (struct adreno_gpu *)data; + uint32_t fence = gpu->funcs->last_fence(gpu); + + if (fence != gpu->hangcheck_fence) { + /* some progress has been made.. ya! */ + gpu->hangcheck_fence = fence; + } else if (fence < gpu->submitted_fence) { + /* no progress and not done.. hung! */ + gpu->hangcheck_fence = fence; + queue_work(gpu->shared_wq, &gpu->recover_work); + } + + /* if still more pending work, reset the hangcheck timer: */ + if (gpu->submitted_fence > gpu->hangcheck_fence) + hangcheck_timer_reset(gpu); +} + int adreno_get_param(struct adreno_gpu *gpu, uint32_t param, uint64_t *value) { switch (param) { @@ -120,7 +164,7 @@ void adreno_recover(struct adreno_gpu *gpu) gpu->rb->cur = gpu->rb->start; /* reset completed fence seqno, just discard anything pending: */ - gpu->memptrs->fence = gpu->base.submitted_fence; + gpu->memptrs->fence = gpu->submitted_fence; gpu->funcs->pm_resume(gpu); ret = gpu->funcs->hw_init(gpu); @@ -196,6 +240,9 @@ int adreno_submit(struct adreno_gpu *gpu, struct adreno_submit *submit, gpu->funcs->flush(gpu); + gpu->submitted_fence = submit->fence; + hangcheck_timer_reset(gpu); + return 0; } @@ -238,7 +285,7 @@ void adreno_show(struct adreno_gpu *gpu, struct seq_file *m) gpu->rev.patchid); seq_printf(m, "fence: %d/%d\n", gpu->memptrs->fence, - gpu->base.submitted_fence); + gpu->submitted_fence); seq_printf(m, "rptr: %d\n", gpu->memptrs->rptr); seq_printf(m, "wptr: %d\n", gpu->memptrs->wptr); seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb)); @@ -268,7 +315,8 @@ static inline bool _rev_match(uint8_t entry, uint8_t id) int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, - struct adreno_gem *gem, struct adreno_rev rev) + struct adreno_gem *gem, struct workqueue_struct *wq, + struct adreno_rev rev) { int i, ret; @@ -297,6 +345,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->drm = drm; gpu->funcs = funcs; gpu->gem = gem; + gpu->shared_wq = wq; gpu->rev = rev; /* XXX: urgh - pointers to self are fun! */ @@ -368,6 +417,10 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, return ret; } + INIT_WORK(&gpu->recover_work, recover_worker); + setup_timer(&gpu->hangcheck_timer, hangcheck_handler, + (unsigned long)gpu); + return 0; } |