On 27.08.25 18:03, Alex Deucher wrote: > Decouple the amdgpu fence from the amdgpu_job structure. > This lets us clean up the separate fence ops for the embedded > fence and other fences. This also allows us to allocate the > vm fence up front when we allocate the job. > > Cc: david....@amd.com > Cc: christian.koe...@amd.com > Signed-off-by: Alex Deucher <alexander.deuc...@amd.com> > --- > drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 2 +- > drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 +- > drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c | 111 +++----------------- > drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | 12 ++- > drivers/gpu/drm/amd/amdgpu/amdgpu_job.c | 36 ++++--- > drivers/gpu/drm/amd/amdgpu/amdgpu_job.h | 3 +- > drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h | 3 + > drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 6 +- > 8 files changed, 58 insertions(+), 117 deletions(-) > > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c > index f81608330a3d0..7ea3cb6491b1b 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c > @@ -1902,7 +1902,7 @@ static void amdgpu_ib_preempt_mark_partial_job(struct > amdgpu_ring *ring) > continue; > } > job = to_amdgpu_job(s_job); > - if (preempted && (&job->hw_fence.base) == fence) > + if (preempted && (&job->hw_fence->base) == fence) > /* mark the job as preempted */ > job->preemption_status |= AMDGPU_IB_PREEMPTED; > } > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > index e117494e80547..efd86f095323a 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > @@ -6494,7 +6494,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device > *adev, > * > * job->base holds a reference to parent fence > */ > - if (job && dma_fence_is_signaled(&job->hw_fence.base)) { > + if (job && dma_fence_is_signaled(&job->hw_fence->base)) { > job_signaled = true; > dev_info(adev->dev, "Guilty job already signaled, skipping HW > reset"); > goto skip_hw_reset; > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c > index 2d58aefbd68a7..0afe2427c25cc 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c > @@ -45,16 +45,11 @@ > * Cast helper > */ > static const struct dma_fence_ops amdgpu_fence_ops; > -static const struct dma_fence_ops amdgpu_job_fence_ops; > static inline struct amdgpu_fence *to_amdgpu_fence(struct dma_fence *f) > { > struct amdgpu_fence *__f = container_of(f, struct amdgpu_fence, base); > > - if (__f->base.ops == &amdgpu_fence_ops || > - __f->base.ops == &amdgpu_job_fence_ops) > - return __f; > - > - return NULL; > + return __f; > } > > /** > @@ -110,35 +105,18 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, struct > dma_fence **f, > {
While at it I would also drop the "struct dma_fence **f" parameter. > struct amdgpu_device *adev = ring->adev; > struct dma_fence *fence; > - struct amdgpu_fence *am_fence; > struct dma_fence __rcu **ptr; > uint32_t seq; > int r; > > - if (!af) { > - /* create a separate hw fence */ > - am_fence = kzalloc(sizeof(*am_fence), GFP_KERNEL); > - if (!am_fence) > - return -ENOMEM; > - } else { > - am_fence = af; > - } > - fence = &am_fence->base; > - am_fence->ring = ring; > + fence = &af->base; > + af->ring = ring; > > seq = ++ring->fence_drv.sync_seq; > - am_fence->seq = seq; > - if (af) { > - dma_fence_init(fence, &amdgpu_job_fence_ops, > - &ring->fence_drv.lock, > - adev->fence_context + ring->idx, seq); > - /* Against remove in amdgpu_job_{free, free_cb} */ > - dma_fence_get(fence); > - } else { > - dma_fence_init(fence, &amdgpu_fence_ops, > - &ring->fence_drv.lock, > - adev->fence_context + ring->idx, seq); > - } > + af->seq = seq; > + dma_fence_init(fence, &amdgpu_fence_ops, > + &ring->fence_drv.lock, > + adev->fence_context + ring->idx, seq); > > amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr, > seq, flags | AMDGPU_FENCE_FLAG_INT); > @@ -683,18 +661,22 @@ void amdgpu_fence_driver_clear_job_fences(struct > amdgpu_ring *ring) Please nuke that complete function. As far as I can see it is unecessary now and the whole concept was always a completely broken idea since it signals fences in an incorrect order. > for (i = 0; i <= ring->fence_drv.num_fences_mask; i++) { > ptr = &ring->fence_drv.fences[i]; > old = rcu_dereference_protected(*ptr, 1); > - if (old && old->ops == &amdgpu_job_fence_ops) { > + if (old) { > + struct amdgpu_fence *af; > struct amdgpu_job *job; > > /* For non-scheduler bad job, i.e. failed ib test, we > need to signal > * it right here or we won't be able to track them in > fence_drv > * and they will remain unsignaled during sa_bo free. > */ > - job = container_of(old, struct amdgpu_job, > hw_fence.base); > - if (!job->base.s_fence && !dma_fence_is_signaled(old)) > - dma_fence_signal(old); > - RCU_INIT_POINTER(*ptr, NULL); > - dma_fence_put(old); > + af = container_of(old, struct amdgpu_fence, base); > + job = af->job; > + if (job) { > + if (!job->base.s_fence && > !dma_fence_is_signaled(old)) > + dma_fence_signal(old); > + RCU_INIT_POINTER(*ptr, NULL); > + dma_fence_put(old); > + } > } > } > } > @@ -830,13 +812,6 @@ static const char *amdgpu_fence_get_timeline_name(struct > dma_fence *f) > return (const char *)to_amdgpu_fence(f)->ring->name; > } > > -static const char *amdgpu_job_fence_get_timeline_name(struct dma_fence *f) > -{ > - struct amdgpu_job *job = container_of(f, struct amdgpu_job, > hw_fence.base); > - > - return (const char *)to_amdgpu_ring(job->base.sched)->name; > -} > - > /** > * amdgpu_fence_enable_signaling - enable signalling on fence > * @f: fence > @@ -853,23 +828,6 @@ static bool amdgpu_fence_enable_signaling(struct > dma_fence *f) > return true; > } > > -/** > - * amdgpu_job_fence_enable_signaling - enable signalling on job fence > - * @f: fence > - * > - * This is the simliar function with amdgpu_fence_enable_signaling above, it > - * only handles the job embedded fence. > - */ > -static bool amdgpu_job_fence_enable_signaling(struct dma_fence *f) > -{ > - struct amdgpu_job *job = container_of(f, struct amdgpu_job, > hw_fence.base); > - > - if > (!timer_pending(&to_amdgpu_ring(job->base.sched)->fence_drv.fallback_timer)) > - amdgpu_fence_schedule_fallback(to_amdgpu_ring(job->base.sched)); > - > - return true; > -} > - > /** > * amdgpu_fence_free - free up the fence memory > * > @@ -885,21 +843,6 @@ static void amdgpu_fence_free(struct rcu_head *rcu) > kfree(to_amdgpu_fence(f)); > } > > -/** > - * amdgpu_job_fence_free - free up the job with embedded fence > - * > - * @rcu: RCU callback head > - * > - * Free up the job with embedded fence after the RCU grace period. > - */ > -static void amdgpu_job_fence_free(struct rcu_head *rcu) > -{ > - struct dma_fence *f = container_of(rcu, struct dma_fence, rcu); > - > - /* free job if fence has a parent job */ > - kfree(container_of(f, struct amdgpu_job, hw_fence.base)); > -} > - > /** > * amdgpu_fence_release - callback that fence can be freed > * > @@ -913,19 +856,6 @@ static void amdgpu_fence_release(struct dma_fence *f) > call_rcu(&f->rcu, amdgpu_fence_free); > } > > -/** > - * amdgpu_job_fence_release - callback that job embedded fence can be freed > - * > - * @f: fence > - * > - * This is the simliar function with amdgpu_fence_release above, it > - * only handles the job embedded fence. > - */ > -static void amdgpu_job_fence_release(struct dma_fence *f) > -{ > - call_rcu(&f->rcu, amdgpu_job_fence_free); > -} > - > static const struct dma_fence_ops amdgpu_fence_ops = { > .get_driver_name = amdgpu_fence_get_driver_name, > .get_timeline_name = amdgpu_fence_get_timeline_name, > @@ -933,13 +863,6 @@ static const struct dma_fence_ops amdgpu_fence_ops = { > .release = amdgpu_fence_release, > }; > > -static const struct dma_fence_ops amdgpu_job_fence_ops = { > - .get_driver_name = amdgpu_fence_get_driver_name, > - .get_timeline_name = amdgpu_job_fence_get_timeline_name, > - .enable_signaling = amdgpu_job_fence_enable_signaling, > - .release = amdgpu_job_fence_release, > -}; > - > /* > * Fence debugfs > */ > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c > index 7d9bcb72e8dd3..192ed1bd2ec63 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c > @@ -128,7 +128,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned > int num_ibs, > struct amdgpu_device *adev = ring->adev; > struct amdgpu_ib *ib = &ibs[0]; > struct dma_fence *tmp = NULL; > - struct amdgpu_fence *af; > + struct amdgpu_fence *af, *vm_af; > bool need_ctx_switch; > struct amdgpu_vm *vm; > uint64_t fence_ctx; > @@ -154,12 +154,15 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, > unsigned int num_ibs, > csa_va = job->csa_va; > gds_va = job->gds_va; > init_shadow = job->init_shadow; > - af = &job->hw_fence; > + af = job->hw_fence; > /* Save the context of the job for reset handling. > * The driver needs this so it can skip the ring > * contents for guilty contexts. > */ > af->context = job->base.s_fence ? > job->base.s_fence->finished.context : 0; > + vm_af = job->hw_vm_fence; > + /* the vm fence is also part of the job's context */ > + vm_af->context = job->base.s_fence ? > job->base.s_fence->finished.context : 0; We already have the fence_ctx around as local variable, but at the moment it uses job->base.s_fence->scheduled.context. Should be trivial to switch that to job->base.s_fence->finished.context so that we don't need to duplicate the "job->base.s_fence ? " part multiple times. > } else { > vm = NULL; > fence_ctx = 0; > @@ -167,7 +170,10 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, > unsigned int num_ibs, > csa_va = 0; > gds_va = 0; > init_shadow = false; > - af = NULL; > + af = kzalloc(sizeof(*af), GFP_NOWAIT); > + if (!af) > + return -ENOMEM; > + vm_af = NULL; > } > > if (!ring->sched.ready) { > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c > index 311e97c96c4e0..4189a2b8cb674 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c > @@ -138,7 +138,7 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct > drm_sched_job *s_job) > ring->funcs->reset) { > dev_err(adev->dev, "Starting %s ring reset\n", > s_job->sched->name); > - r = amdgpu_ring_reset(ring, job->vmid, &job->hw_fence); > + r = amdgpu_ring_reset(ring, job->vmid, job->hw_fence); > if (!r) { > atomic_inc(&ring->adev->gpu_reset_counter); > dev_err(adev->dev, "Ring %s reset succeeded\n", > @@ -185,6 +185,8 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct > amdgpu_vm *vm, > struct drm_sched_entity *entity, void *owner, > unsigned int num_ibs, struct amdgpu_job **job) > { > + struct amdgpu_fence *af; > + > if (num_ibs == 0) > return -EINVAL; > > @@ -192,6 +194,23 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct > amdgpu_vm *vm, > if (!*job) > return -ENOMEM; > > + af = kzalloc(sizeof(struct amdgpu_fence), GFP_KERNEL); > + if (!af) { > + kfree(*job); > + return -ENOMEM; > + } > + /* assign the job so we can clean up in > amdgpu_fence_driver_clear_job_fences() */ > + af->job = *job; That becomes unecessary when we nuke amdgpu_fence_driver_clear_job_fences(). > + (*job)->hw_fence = af; > + > + af = kzalloc(sizeof(struct amdgpu_fence), GFP_KERNEL); > + if (!af) { > + kfree((*job)->hw_fence); > + kfree(*job); > + return -ENOMEM; > + } Not a must have, but goto cleanup would probably look nicer. > + (*job)->hw_vm_fence = af; > + > (*job)->vm = vm; > > amdgpu_sync_create(&(*job)->explicit_sync); > @@ -251,8 +270,8 @@ void amdgpu_job_free_resources(struct amdgpu_job *job) > /* Check if any fences where initialized */ > if (job->base.s_fence && job->base.s_fence->finished.ops) > f = &job->base.s_fence->finished; > - else if (job->hw_fence.base.ops) > - f = &job->hw_fence.base; > + else if (job->hw_fence) > + f = &job->hw_fence->base; > else > f = NULL; > > @@ -268,11 +287,7 @@ static void amdgpu_job_free_cb(struct drm_sched_job > *s_job) > > amdgpu_sync_free(&job->explicit_sync); > > - /* only put the hw fence if has embedded fence */ > - if (!job->hw_fence.base.ops) > - kfree(job); > - else > - dma_fence_put(&job->hw_fence.base); > + kfree(job); > } > > void amdgpu_job_set_gang_leader(struct amdgpu_job *job, > @@ -301,10 +316,7 @@ void amdgpu_job_free(struct amdgpu_job *job) > if (job->gang_submit != &job->base.s_fence->scheduled) > dma_fence_put(job->gang_submit); > > - if (!job->hw_fence.base.ops) > - kfree(job); > - else > - dma_fence_put(&job->hw_fence.base); > + kfree(job); > } > > struct dma_fence *amdgpu_job_submit(struct amdgpu_job *job) > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h > b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h > index 931fed8892cc1..077b2414a24b7 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h > @@ -48,7 +48,8 @@ struct amdgpu_job { > struct drm_sched_job base; > struct amdgpu_vm *vm; > struct amdgpu_sync explicit_sync; > - struct amdgpu_fence hw_fence; > + struct amdgpu_fence *hw_fence; > + struct amdgpu_fence *hw_vm_fence; > struct dma_fence *gang_submit; > uint32_t preamble_status; > uint32_t preemption_status; > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h > b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h > index 7670f5d82b9e4..ceadd7e4bdb58 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h > @@ -143,6 +143,9 @@ struct amdgpu_fence { > struct amdgpu_ring *ring; > ktime_t start_timestamp; > > + /* store the job for cleanup */ > + struct amdgpu_job *job; > + That can be dropped as well. Regards, Christian. > /* wptr for the fence for resets */ > u64 wptr; > /* fence context for resets */ > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > index bf42246a3db2f..a287718506aa6 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c > @@ -772,7 +772,6 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct > amdgpu_job *job, > bool cleaner_shader_needed = false; > bool pasid_mapping_needed = false; > struct dma_fence *fence = NULL; > - struct amdgpu_fence *af; > unsigned int patch; > int r; > > @@ -835,12 +834,9 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct > amdgpu_job *job, > } > > if (vm_flush_needed || pasid_mapping_needed || cleaner_shader_needed) { > - r = amdgpu_fence_emit(ring, &fence, NULL, 0); > + r = amdgpu_fence_emit(ring, &fence, job->hw_vm_fence, 0); > if (r) > return r; > - /* this is part of the job's context */ > - af = container_of(fence, struct amdgpu_fence, base); > - af->context = job->base.s_fence ? > job->base.s_fence->finished.context : 0; > } > > if (vm_flush_needed) {