--- a/sys/dev/drm/scheduler/sched_main.c 2026-09-23 06:03:21.388609645 +0000 +++ b/sys/dev/drm/scheduler/sched_main.c 2026-09-23 06:03:21.400609489 +0000 @@ -365,6 +365,22 @@ if (found_guilty && s_job->s_fence->scheduled.context == guilty_context) dma_fence_set_error(&s_fence->finished, -ECANCELED); + /* + * The finished fence is already signaled: a finish_work is + * queued (or running) and owns removing this job from the + * ring mirror list and freeing it. Re-running the job here + * would race finish_work across the unlocked run_job() + * window below and arm callbacks inside freed memory. + */ + if (dma_fence_is_signaled(&s_fence->finished)) + continue; + + /* + * Pin the fence chain across the unlocked run_job() window: + * a completion racing in from another context must not be + * able to drop the last reference while we still use it. + */ + dma_fence_get(&s_fence->finished); lockmgr(&sched->job_list_lock, LK_RELEASE); fence = sched->ops->run_job(s_job); atomic_inc(&sched->hw_rq_count); @@ -379,10 +395,12 @@ DRM_ERROR("fence add callback failed (%d)\n", r); dma_fence_put(fence); + dma_fence_put(&s_fence->finished); } else { if (s_fence->finished.error < 0) drm_sched_expel_job_unlocked(s_job); drm_sched_process_job(NULL, &s_fence->cb); + dma_fence_put(&s_fence->finished); } lockmgr(&sched->job_list_lock, LK_EXCLUSIVE); } @@ -653,5 +671,6 @@ { if (sched->thread) kthread_stop(sched->thread); + cancel_delayed_work_sync(&sched->work_tdr); } EXPORT_SYMBOL(drm_sched_fini);