drm/v3d: Clear queue->active_job when v3d_fence_create() fails

The run_job() callbacks for BIN, RENDER, TFU and CSD assign the incoming
job to queue->active_job before calling v3d_fence_create(). If
v3d_fence_create() fails, the callback returns NULL without clearing
active_job, leaving a dangling pointer.

Create a failure path in all run_job() callbacks that clears the active
job before returning NULL. The BIN path takes queue->queue_lock around the
clear as it races against v3d_overflow_mem_work(); RENDER, TFU and CSD
paths have no concurrent reader, so the clear is lock-free.

Fixes: a783a09ee7 ("drm/v3d: Refactor job management.")
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
Link: https://patch.msgid.link/20260604-v3d-sched-misc-fixes-v4-2-c068f5bf5ccf@igalia.com
Signed-off-by: Maíra Canal <mcanal@igalia.com>
This commit is contained in:
Maíra Canal
2026-06-04 17:32:15 -03:00
parent cc29c864ee
commit 25a1669907

View File

@@ -189,15 +189,11 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
struct v3d_dev *v3d = job->base.v3d;
struct v3d_queue_state *queue = &v3d->queue[V3D_BIN];
struct drm_device *dev = &v3d->drm;
struct dma_fence *fence;
struct dma_fence *fence = NULL;
unsigned long irqflags;
if (unlikely(job->base.base.s_fence->finished.error)) {
spin_lock_irqsave(&queue->queue_lock, irqflags);
queue->active_job = NULL;
spin_unlock_irqrestore(&queue->queue_lock, irqflags);
return NULL;
}
if (unlikely(job->base.base.s_fence->finished.error))
goto out_clean_job;
/* Lock required around bin_job update vs
* v3d_overflow_mem_work().
@@ -214,7 +210,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
fence = v3d_fence_create(v3d, V3D_BIN);
if (IS_ERR(fence))
return NULL;
goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -242,6 +238,12 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job)
V3D_CORE_WRITE(0, V3D_CLE_CT0QEA, job->end);
return fence;
out_clean_job:
spin_lock_irqsave(&queue->queue_lock, irqflags);
queue->active_job = NULL;
spin_unlock_irqrestore(&queue->queue_lock, irqflags);
return fence;
}
static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
@@ -249,12 +251,10 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
struct v3d_render_job *job = to_render_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
struct drm_device *dev = &v3d->drm;
struct dma_fence *fence;
struct dma_fence *fence = NULL;
if (unlikely(job->base.base.s_fence->finished.error)) {
v3d->queue[V3D_RENDER].active_job = NULL;
return NULL;
}
if (unlikely(job->base.base.s_fence->finished.error))
goto out_clean_job;
v3d->queue[V3D_RENDER].active_job = &job->base;
@@ -268,7 +268,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
fence = v3d_fence_create(v3d, V3D_RENDER);
if (IS_ERR(fence))
return NULL;
goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -289,6 +289,10 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job)
V3D_CORE_WRITE(0, V3D_CLE_CT1QEA, job->end);
return fence;
out_clean_job:
v3d->queue[V3D_RENDER].active_job = NULL;
return fence;
}
static struct dma_fence *
@@ -297,18 +301,16 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
struct v3d_tfu_job *job = to_tfu_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
struct drm_device *dev = &v3d->drm;
struct dma_fence *fence;
struct dma_fence *fence = NULL;
if (unlikely(job->base.base.s_fence->finished.error)) {
v3d->queue[V3D_TFU].active_job = NULL;
return NULL;
}
if (unlikely(job->base.base.s_fence->finished.error))
goto out_clean_job;
v3d->queue[V3D_TFU].active_job = &job->base;
fence = v3d_fence_create(v3d, V3D_TFU);
if (IS_ERR(fence))
return NULL;
goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -336,6 +338,10 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job)
V3D_WRITE(V3D_TFU_ICFG(v3d->ver), job->args.icfg | V3D_TFU_ICFG_IOC);
return fence;
out_clean_job:
v3d->queue[V3D_TFU].active_job = NULL;
return fence;
}
static struct dma_fence *
@@ -344,13 +350,11 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
struct v3d_csd_job *job = to_csd_job(sched_job);
struct v3d_dev *v3d = job->base.v3d;
struct drm_device *dev = &v3d->drm;
struct dma_fence *fence;
struct dma_fence *fence = NULL;
int i, csd_cfg0_reg;
if (unlikely(job->base.base.s_fence->finished.error)) {
v3d->queue[V3D_CSD].active_job = NULL;
return NULL;
}
if (unlikely(job->base.base.s_fence->finished.error))
goto out_clean_job;
v3d->queue[V3D_CSD].active_job = &job->base;
@@ -358,7 +362,7 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
fence = v3d_fence_create(v3d, V3D_CSD);
if (IS_ERR(fence))
return NULL;
goto out_clean_job;
if (job->base.irq_fence)
dma_fence_put(job->base.irq_fence);
@@ -385,6 +389,10 @@ v3d_csd_job_run(struct drm_sched_job *sched_job)
V3D_CORE_WRITE(0, csd_cfg0_reg, job->args.cfg[0]);
return fence;
out_clean_job:
v3d->queue[V3D_CSD].active_job = NULL;
return fence;
}
static void