A recoverable GP error, reported by the GP or the GP MMU interrupt, queues the pipe's recover_work on the system workqueue. Nothing drains it on device removal: the worker can run after the GP task slab has been destroyed, call drm_sched_fault() on a scheduler that drm_sched_fini() has already torn down, or outlive the devm allocation that embeds struct lima_device. Free the GP and GP MMU interrupts, the only sources of recover_work, before the GP pipe teardown, and drain the work while the task slab and the scheduler are still alive. Stop the GP with a final reset after drm_sched_fini(): nothing can restart the job anymore. Free the error task list only after both schedulers are fini'd, as the timeout handler locks it. This issue was found by an in-house static analysis tool. Fixes: 2081e8dcf1ee ("drm/lima: recover task by enlarging heap buffer") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Co-developed-by: Song Li Signed-off-by: Song Li Signed-off-by: Fan Wu --- drivers/gpu/drm/lima/lima_device.c | 22 +++++++++++++++++----- drivers/gpu/drm/lima/lima_gp.c | 7 +++++++ drivers/gpu/drm/lima/lima_gp.h | 1 + 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c index 0bf7105c8748..5f728230fce4 100644 --- a/drivers/gpu/drm/lima/lima_device.c +++ b/drivers/gpu/drm/lima/lima_device.c @@ -297,8 +297,14 @@ static void lima_fini_gp_pipe(struct lima_device *dev) { struct lima_sched_pipe *pipe = dev->pipe + lima_pipe_gp; - lima_gp_pipe_fini(dev); + cancel_work_sync(&pipe->recover_work); + lima_sched_pipe_fini(pipe); + + /* a recovery may have restarted the GP job */ + lima_gp_stop(dev->ip + lima_ip_gp); + + lima_gp_pipe_fini(dev); } static int lima_init_pp_pipe(struct lima_device *dev) @@ -442,17 +448,23 @@ void lima_device_fini(struct lima_device *ldev) int i; struct lima_sched_error_task *et, *tmp; + lima_fini_pp_pipe(ldev); + + /* free the IRQ sources of recover_work before the GP pipe drain */ + lima_fini_ip(ldev, lima_ip_gp); + lima_fini_ip(ldev, lima_ip_gpmmu); + lima_fini_gp_pipe(ldev); + + /* the timeout handlers lock it: free after both schedulers */ list_for_each_entry_safe(et, tmp, &ldev->error_task_list, list) { list_del(&et->list); kvfree(et); } mutex_destroy(&ldev->error_task_list_lock); - lima_fini_pp_pipe(ldev); - lima_fini_gp_pipe(ldev); - for (i = lima_ip_num - 1; i >= 0; i--) - lima_fini_ip(ldev, i); + if (i != lima_ip_gp && i != lima_ip_gpmmu) + lima_fini_ip(ldev, i); if (ldev->dlbu_cpu) dma_free_wc(ldev->dev, LIMA_PAGE_SIZE, diff --git a/drivers/gpu/drm/lima/lima_gp.c b/drivers/gpu/drm/lima/lima_gp.c index 3282997a0358..7287af7829f8 100644 --- a/drivers/gpu/drm/lima/lima_gp.c +++ b/drivers/gpu/drm/lima/lima_gp.c @@ -350,6 +350,13 @@ void lima_gp_fini(struct lima_ip *ip) devm_free_irq(dev->dev, ip->irq, ip); } +/* keep the irq masked: hard_reset() re-enables it */ +void lima_gp_stop(struct lima_ip *ip) +{ + lima_gp_hard_reset(ip); + gp_write(LIMA_GP_INT_MASK, 0); +} + int lima_gp_pipe_init(struct lima_device *dev) { int frame_size = sizeof(struct drm_lima_gp_frame); diff --git a/drivers/gpu/drm/lima/lima_gp.h b/drivers/gpu/drm/lima/lima_gp.h index 02ec9af78a51..c2a1e54ea567 100644 --- a/drivers/gpu/drm/lima/lima_gp.h +++ b/drivers/gpu/drm/lima/lima_gp.h @@ -11,6 +11,7 @@ int lima_gp_resume(struct lima_ip *ip); void lima_gp_suspend(struct lima_ip *ip); int lima_gp_init(struct lima_ip *ip); void lima_gp_fini(struct lima_ip *ip); +void lima_gp_stop(struct lima_ip *ip); int lima_gp_pipe_init(struct lima_device *dev); void lima_gp_pipe_fini(struct lima_device *dev);