On 03/06/2026 23:25, Maíra Canal wrote:
Currently, v3d_submit_jobs() arms and pushes each job one at a time,
wiring dependencies between consecutive jobs after each push. If
drm_sched_job_add_dependency() fails midway, the already-pushed jobs are
scheduler-owned and will be submitted to the GPU for execution, even though
the subsequent jobs won't be submitted.

This breaks the atomicity of the submissions, as only some of the jobs
from a submission would be submitted to the hardware, while the other part
fails.

Restructure v3d_submit_jobs() into three phases: (1) arm all jobs belonging
to a given submission, (2) wire inter-job dependencies, and (3) push all
jobs to the scheduler unconditionally. Phase (2) can fail; on failure,
it marks every armed job finished fence with an error, so that run_job()
callbacks skip hardware execution.

This guarantees that every armed job is always pushed, either to run
or to be skipped, and it also ensures the atomicity of a submission.

Suggested-by: Tvrtko Ursulin <[email protected]>
Signed-off-by: Maíra Canal <[email protected]>
---
  drivers/gpu/drm/v3d/v3d_sched.c  |  6 ++++
  drivers/gpu/drm/v3d/v3d_submit.c | 65 ++++++++++++++++++++--------------------
  2 files changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index 04fd1a365576..c16a9d4d41e6 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -656,6 +656,9 @@ v3d_cpu_job_run(struct drm_sched_job *sched_job)
        struct v3d_cpu_job *job = to_cpu_job(sched_job);
        struct v3d_dev *v3d = job->base.v3d;
+ if (unlikely(job->base.base.s_fence->finished.error))
+               return NULL;
+
        if (job->job_type >= ARRAY_SIZE(cpu_job_function)) {
                drm_dbg(&v3d->drm, "Unknown CPU job: %d\n", job->job_type);
                return NULL;
@@ -679,6 +682,9 @@ v3d_cache_clean_job_run(struct drm_sched_job *sched_job)
        struct v3d_job *job = to_v3d_job(sched_job);
        struct v3d_dev *v3d = job->v3d;
+ if (unlikely(job->base.s_fence->finished.error))
+               return NULL;
+
        v3d_job_start_stats(job);
v3d_clean_caches(v3d);
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index dc27770d85fd..e118ba69e308 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -365,19 +365,6 @@ v3d_submit_process_post_deps(struct v3d_submit *submit, 
struct drm_syncobj *sync
        }
  }
-static void
-v3d_push_job(struct v3d_job *job)
-{
-       drm_sched_job_arm(&job->base);
-
-       job->done_fence = dma_fence_get(&job->base.s_fence->finished);
-
-       /* put by scheduler job completion */
-       kref_get(&job->refcount);
-
-       drm_sched_entity_push_job(&job->base);
-}
-
  static int
  v3d_submit_jobs(struct v3d_submit *submit, struct drm_syncobj *sync_out,
                struct v3d_submit_ext *se)
@@ -390,16 +377,23 @@ v3d_submit_jobs(struct v3d_submit *submit, struct 
drm_syncobj *sync_out,
        for (int i = 0; i < submit->job_count; i++) {
                struct v3d_job *job = submit->jobs[i];
- v3d_push_job(job);
+               drm_sched_job_arm(&job->base);
+               job->done_fence = dma_fence_get(&job->base.s_fence->finished);
- if (i + 1 < submit->job_count) {
-                       ret = drm_sched_job_add_dependency(&submit->jobs[i + 
1]->base,
-                                                          
dma_fence_get(job->done_fence));
-                       if (ret)
-                               goto err;
-               }
+               /* put by scheduler job completion */
+               kref_get(&job->refcount);
        }
+ for (int i = 0; i + 1 < submit->job_count; i++) {

i + 1 < count is a pattern I dont' remember seeing but maybe it is just me. Normally I would expect something like:

for (i = 1; i < count; i++)
   add_dep(job[i], job[i - 1]);

Anyway, it looks functionally correct to me as is:

Reviewed-by: Tvrtko Ursulin <[email protected]>

Regards,

Tvrtko

+               ret = drm_sched_job_add_dependency(&submit->jobs[i + 1]->base,
+                                                  
dma_fence_get(submit->jobs[i]->done_fence));
+               if (ret)
+                       goto err;
+       }
+
+       for (int i = 0; i < submit->job_count; i++)
+               drm_sched_entity_push_job(&submit->jobs[i]->base);
+
        mutex_unlock(&v3d->sched_lock);
v3d_submit_attach_object_fences(submit);
@@ -411,7 +405,18 @@ v3d_submit_jobs(struct v3d_submit *submit, struct 
drm_syncobj *sync_out,
        return 0;
err:
+       /* Mark every armed job as failed so run_job() skips execution */
+       for (int i = 0; i < submit->job_count; i++)
+               dma_fence_set_error(&submit->jobs[i]->base.s_fence->finished, 
ret);
+
+       for (int i = 0; i < submit->job_count; i++)
+               drm_sched_entity_push_job(&submit->jobs[i]->base);
+
        mutex_unlock(&v3d->sched_lock);
+
+       v3d_submit_unlock_reservations(submit);
+       v3d_submit_put_jobs(submit);
+
        return ret;
  }
@@ -1098,14 +1103,13 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, ret = v3d_submit_jobs(&submit, sync_out, &se);
        if (ret)
-               goto fail_unreserve;
+               goto fail_submit;
return 0; -fail_unreserve:
-       v3d_submit_unlock_reservations(&submit);
  fail:
        v3d_submit_cleanup_jobs(&submit);
+fail_submit:
        v3d_submit_put_post_deps(sync_out, &se);
return ret;
@@ -1195,14 +1199,13 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data,
ret = v3d_submit_jobs(&submit, sync_out, &se);
        if (ret)
-               goto fail_unreserve;
+               goto fail_submit;
return 0; -fail_unreserve:
-       v3d_submit_unlock_reservations(&submit);
  fail:
        v3d_submit_cleanup_jobs(&submit);
+fail_submit:
        v3d_submit_put_post_deps(sync_out, &se);
return ret;
@@ -1270,14 +1273,13 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data,
ret = v3d_submit_jobs(&submit, sync_out, &se);
        if (ret)
-               goto fail_unreserve;
+               goto fail_submit;
return 0; -fail_unreserve:
-       v3d_submit_unlock_reservations(&submit);
  fail:
        v3d_submit_cleanup_jobs(&submit);
+fail_submit:
        v3d_submit_put_post_deps(sync_out, &se);
return ret;
@@ -1378,14 +1380,13 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data,
ret = v3d_submit_jobs(&submit, NULL, &se);
        if (ret)
-               goto fail_unreserve;
+               goto fail_submit;
return 0; -fail_unreserve:
-       v3d_submit_unlock_reservations(&submit);
  fail:
        v3d_submit_cleanup_jobs(&submit);
+fail_submit:
        v3d_submit_put_post_deps(NULL, &se);
return ret;


Reply via email to