Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/msm: add basic hangcheck/recovery mechanism

A basic, no-frills recovery mechanism in case the gpu gets wedged. We
could try to be a bit more fancy and restart the next submit after the
one that got wedged, but for now keep it simple. This is enough to
recover things if, for example, the gpu hangs mid way through a piglit
run.

Signed-off-by: Rob Clark <robdclark@gmail.com>

Rob Clark bd6f82d8 7198e6b0

+87 -5
+1
drivers/gpu/drm/msm/adreno/a3xx_gpu.c
··· 371 371 .hw_init = a3xx_hw_init, 372 372 .pm_suspend = msm_gpu_pm_suspend, 373 373 .pm_resume = msm_gpu_pm_resume, 374 + .recover = adreno_recover, 374 375 .last_fence = adreno_last_fence, 375 376 .submit = adreno_submit, 376 377 .flush = adreno_flush,
+23 -3
drivers/gpu/drm/msm/adreno/adreno_gpu.c
··· 111 111 return adreno_gpu->memptrs->fence; 112 112 } 113 113 114 + void adreno_recover(struct msm_gpu *gpu) 115 + { 116 + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); 117 + struct drm_device *dev = gpu->dev; 118 + int ret; 119 + 120 + gpu->funcs->pm_suspend(gpu); 121 + 122 + /* reset ringbuffer: */ 123 + gpu->rb->cur = gpu->rb->start; 124 + 125 + /* reset completed fence seqno, just discard anything pending: */ 126 + adreno_gpu->memptrs->fence = gpu->submitted_fence; 127 + 128 + gpu->funcs->pm_resume(gpu); 129 + ret = gpu->funcs->hw_init(gpu); 130 + if (ret) { 131 + dev_err(dev->dev, "gpu hw init failed: %d\n", ret); 132 + /* hmm, oh well? */ 133 + } 134 + } 135 + 114 136 int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, 115 137 struct msm_file_private *ctx) 116 138 { ··· 140 118 struct msm_drm_private *priv = gpu->dev->dev_private; 141 119 struct msm_ringbuffer *ring = gpu->rb; 142 120 unsigned i, ibs = 0; 143 - 144 - adreno_gpu->last_fence = submit->fence; 145 121 146 122 for (i = 0; i < submit->nr_cmds; i++) { 147 123 switch (submit->cmd[i].type) { ··· 245 225 adreno_gpu->rev.patchid); 246 226 247 227 seq_printf(m, "fence: %d/%d\n", adreno_gpu->memptrs->fence, 248 - adreno_gpu->last_fence); 228 + gpu->submitted_fence); 249 229 seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr); 250 230 seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr); 251 231 seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb));
+1 -2
drivers/gpu/drm/msm/adreno/adreno_gpu.h
··· 54 54 uint32_t revn; /* numeric revision name */ 55 55 const struct adreno_gpu_funcs *funcs; 56 56 57 - uint32_t last_fence; 58 - 59 57 /* firmware: */ 60 58 const struct firmware *pm4, *pfp; 61 59 ··· 97 99 int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value); 98 100 int adreno_hw_init(struct msm_gpu *gpu); 99 101 uint32_t adreno_last_fence(struct msm_gpu *gpu); 102 + void adreno_recover(struct msm_gpu *gpu); 100 103 int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, 101 104 struct msm_file_private *ctx); 102 105 void adreno_flush(struct msm_gpu *gpu);
+52
drivers/gpu/drm/msm/msm_gpu.c
··· 203 203 } 204 204 205 205 /* 206 + * Hangcheck detection for locked gpu: 207 + */ 208 + 209 + static void recover_worker(struct work_struct *work) 210 + { 211 + struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work); 212 + struct drm_device *dev = gpu->dev; 213 + 214 + dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name); 215 + 216 + mutex_lock(&dev->struct_mutex); 217 + gpu->funcs->recover(gpu); 218 + mutex_unlock(&dev->struct_mutex); 219 + 220 + msm_gpu_retire(gpu); 221 + } 222 + 223 + static void hangcheck_timer_reset(struct msm_gpu *gpu) 224 + { 225 + DBG("%s", gpu->name); 226 + mod_timer(&gpu->hangcheck_timer, 227 + round_jiffies_up(jiffies + DRM_MSM_HANGCHECK_JIFFIES)); 228 + } 229 + 230 + static void hangcheck_handler(unsigned long data) 231 + { 232 + struct msm_gpu *gpu = (struct msm_gpu *)data; 233 + uint32_t fence = gpu->funcs->last_fence(gpu); 234 + 235 + if (fence != gpu->hangcheck_fence) { 236 + /* some progress has been made.. ya! */ 237 + gpu->hangcheck_fence = fence; 238 + } else if (fence < gpu->submitted_fence) { 239 + /* no progress and not done.. hung! */ 240 + struct msm_drm_private *priv = gpu->dev->dev_private; 241 + gpu->hangcheck_fence = fence; 242 + queue_work(priv->wq, &gpu->recover_work); 243 + } 244 + 245 + /* if still more pending work, reset the hangcheck timer: */ 246 + if (gpu->submitted_fence > gpu->hangcheck_fence) 247 + hangcheck_timer_reset(gpu); 248 + } 249 + 250 + /* 206 251 * Cmdstream submission/retirement: 207 252 */ 208 253 ··· 299 254 300 255 submit->fence = ++priv->next_fence; 301 256 257 + gpu->submitted_fence = submit->fence; 258 + 302 259 ret = gpu->funcs->submit(gpu, submit, ctx); 303 260 priv->lastctx = ctx; 304 261 ··· 323 276 324 277 msm_gem_move_to_active(&msm_obj->base, gpu, submit->fence); 325 278 } 279 + hangcheck_timer_reset(gpu); 326 280 mutex_unlock(&dev->struct_mutex); 327 281 328 282 return ret; ··· 355 307 356 308 INIT_LIST_HEAD(&gpu->active_list); 357 309 INIT_WORK(&gpu->retire_work, retire_worker); 310 + INIT_WORK(&gpu->recover_work, recover_worker); 311 + 312 + setup_timer(&gpu->hangcheck_timer, hangcheck_handler, 313 + (unsigned long)gpu); 358 314 359 315 BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks)); 360 316
+10
drivers/gpu/drm/msm/msm_gpu.h
··· 51 51 void (*idle)(struct msm_gpu *gpu); 52 52 irqreturn_t (*irq)(struct msm_gpu *irq); 53 53 uint32_t (*last_fence)(struct msm_gpu *gpu); 54 + void (*recover)(struct msm_gpu *gpu); 54 55 void (*destroy)(struct msm_gpu *gpu); 55 56 #ifdef CONFIG_DEBUG_FS 56 57 /* show GPU status in debugfs: */ ··· 70 69 /* list of GEM active objects: */ 71 70 struct list_head active_list; 72 71 72 + uint32_t submitted_fence; 73 + 73 74 /* worker for handling active-list retiring: */ 74 75 struct work_struct retire_work; 75 76 ··· 86 83 struct clk *ebi1_clk, *grp_clks[5]; 87 84 uint32_t fast_rate, slow_rate, bus_freq; 88 85 uint32_t bsc; 86 + 87 + /* Hang Detction: */ 88 + #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ 89 + #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) 90 + struct timer_list hangcheck_timer; 91 + uint32_t hangcheck_fence; 92 + struct work_struct recover_work; 89 93 }; 90 94 91 95 static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)