From: Vitaly Prosyak <[email protected]>

Currently drm_sched runs run_job and free_job on the per-scheduler
ordered submit workqueue, while timeouts (drm_sched_job_timedout())
run on sched->timeout_wq (e.g. amdgpu reset_domain->wq).

For drivers like amdgpu the reset path entered from timeout_wq may
still access the guilty job while free_job, running on submit_wq,
frees it. This allows a use-after-free when recovery continues to
touch job fields after drm_sched_free_job_work() has called
ops->free_job().

Queue work_free_job on sched->timeout_wq instead of submit_wq, both
from __drm_sched_run_free_queue() and drm_sched_wqueue_start(), so
timeout/reset and free_job are always serialized on the same
workqueue.

Behavior changes:

- work_run_job stays on sched->submit_wq (ordered).
- work_free_job moves to sched->timeout_wq (timeout/reset queue).
- Submission and freeing may now run in parallel on different
  workqueues, but all shared state is already protected by
  job_list_lock and atomics.

Pros:
- Eliminates reset vs free_job race (no freeing while reset still
  uses the job).
- Matches the logical model: timeout selects guilty job and recovery,
  including freeing, is handled on one queue.

Cons / considerations:
- For users that don’t provide timeout_wq, free_job moves from the
  per-sched ordered queue to system_wq, which slightly changes
  scheduling behaviour but keeps correctness.

Cc: Philipp Stanner <[email protected]>
Cc: Alex Deucher <[email protected]>
Cc: Christian König <[email protected]>
Suggested-by: Mathew from Intel during XDC
Suggested-by: Christian König <[email protected]>
Signed-off-by: Vitaly Prosyak <[email protected]>
---
 drivers/gpu/drm/scheduler/sched_main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/scheduler/sched_main.c 
b/drivers/gpu/drm/scheduler/sched_main.c
index 81ad40d9582b..1243200d475e 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -355,7 +355,7 @@ static void drm_sched_run_job_queue(struct 
drm_gpu_scheduler *sched)
 static void __drm_sched_run_free_queue(struct drm_gpu_scheduler *sched)
 {
        if (!READ_ONCE(sched->pause_submit))
-               queue_work(sched->submit_wq, &sched->work_free_job);
+               queue_work(sched->timeout_wq, &sched->work_free_job);
 }
 
 /**
@@ -1493,6 +1493,6 @@ void drm_sched_wqueue_start(struct drm_gpu_scheduler 
*sched)
 {
        WRITE_ONCE(sched->pause_submit, false);
        queue_work(sched->submit_wq, &sched->work_run_job);
-       queue_work(sched->submit_wq, &sched->work_free_job);
+       queue_work(sched->timeout_wq, &sched->work_free_job);
 }
 EXPORT_SYMBOL(drm_sched_wqueue_start);
-- 
2.51.2

Reply via email to