Skip to content

Commit 0ac822b

Browse files
srishanmSasha Levin
authored andcommitted
drm/amdgpu: Refactor amdgpu_gem_va_ioctl for Handling Last Fence Update and Timeline Management v7
[ Upstream commit efdc66f ] When GPU memory mappings are updated, the driver returns a fence so userspace knows when the update is finished. The previous refactor could pick the wrong fence or rely on checks that are not safe for GPU mappings that stay valid even when memory is missing. In some cases this could return an invalid fence or cause fence reference counting problems. Fix this by (v5,v6, per Christian): - Starting from the VM’s existing last update fence, so a valid and meaningful fence is always returned even when no new work is required. - Selecting the VM-level fence only for always-valid / PRT mappings using the required combined bo_va + bo guard. - Using the per-BO page table update fence for normal MAP and REPLACE operations. - For UNMAP and CLEAR, returning the fence provided by amdgpu_vm_clear_freed(), which may remain unchanged when nothing needs clearing. - Keeping fence reference counting balanced. v7: Drop the extra bo_va/bo NULL guard since amdgpu_vm_is_bo_always_valid() handles NULL BOs correctly (including PRT). (Christian) This makes VM timeline fences correct and prevents crashes caused by incorrect fence handling. Fixes: bd8150a ("drm/amdgpu: Refactor amdgpu_gem_va_ioctl for Handling Last Fence Update and Timeline Management v4") Suggested-by: Christian König <christian.koenig@amd.com> Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com> Reviewed-by: Christian König <christian.koenig@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent f1ba620 commit 0ac822b

1 file changed

Lines changed: 37 additions & 36 deletions

File tree

drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -720,15 +720,23 @@ amdgpu_gem_va_update_vm(struct amdgpu_device *adev,
720720
struct amdgpu_bo_va *bo_va,
721721
uint32_t operation)
722722
{
723-
struct dma_fence *clear_fence = dma_fence_get_stub();
724-
struct dma_fence *last_update = NULL;
725-
int r;
723+
struct dma_fence *fence;
724+
int r = 0;
725+
726+
/* Always start from the VM's existing last update fence. */
727+
fence = dma_fence_get(vm->last_update);
726728

727729
if (!amdgpu_vm_ready(vm))
728-
return clear_fence;
730+
return fence;
729731

730-
/* First clear freed BOs and get a fence for that work, if any. */
731-
r = amdgpu_vm_clear_freed(adev, vm, &clear_fence);
732+
/*
733+
* First clean up any freed mappings in the VM.
734+
*
735+
* amdgpu_vm_clear_freed() may replace @fence with a new fence if it
736+
* schedules GPU work. If nothing needs clearing, @fence can remain as
737+
* the original vm->last_update.
738+
*/
739+
r = amdgpu_vm_clear_freed(adev, vm, &fence);
732740
if (r)
733741
goto error;
734742

@@ -746,53 +754,46 @@ amdgpu_gem_va_update_vm(struct amdgpu_device *adev,
746754
goto error;
747755

748756
/*
749-
* Decide which fence represents the "last update" for this VM/BO:
757+
* Decide which fence best represents the last update:
758+
*
759+
* MAP/REPLACE:
760+
* - For always-valid mappings, use vm->last_update.
761+
* - Otherwise, export bo_va->last_pt_update.
750762
*
751-
* - For MAP/REPLACE we want the PT update fence, which is tracked as
752-
* either vm->last_update (for always-valid BOs) or bo_va->last_pt_update
753-
* (for per-BO updates).
763+
* UNMAP/CLEAR:
764+
* Keep the fence returned by amdgpu_vm_clear_freed(). If no work was
765+
* needed, it can remain as vm->last_pt_update.
754766
*
755-
* - For UNMAP/CLEAR we rely on the fence returned by
756-
* amdgpu_vm_clear_freed(), which already covers the page table work
757-
* for the removed mappings.
767+
* The VM and BO update fences are always initialized to a valid value.
768+
* vm->last_update and bo_va->last_pt_update always start as valid fences.
769+
* and are never expected to be NULL.
758770
*/
759771
switch (operation) {
760772
case AMDGPU_VA_OP_MAP:
761773
case AMDGPU_VA_OP_REPLACE:
762-
if (bo_va && bo_va->base.bo) {
763-
if (amdgpu_vm_is_bo_always_valid(vm, bo_va->base.bo)) {
764-
if (vm->last_update)
765-
last_update = dma_fence_get(vm->last_update);
766-
} else {
767-
if (bo_va->last_pt_update)
768-
last_update = dma_fence_get(bo_va->last_pt_update);
769-
}
770-
}
774+
/*
775+
* For MAP/REPLACE, return the page table update fence for the
776+
* mapping we just modified. bo_va is expected to be valid here.
777+
*/
778+
dma_fence_put(fence);
779+
780+
if (amdgpu_vm_is_bo_always_valid(vm, bo_va->base.bo))
781+
fence = dma_fence_get(vm->last_update);
782+
else
783+
fence = dma_fence_get(bo_va->last_pt_update);
771784
break;
772785
case AMDGPU_VA_OP_UNMAP:
773786
case AMDGPU_VA_OP_CLEAR:
774-
if (clear_fence)
775-
last_update = dma_fence_get(clear_fence);
776-
break;
777787
default:
788+
/* keep @fence as returned by amdgpu_vm_clear_freed() */
778789
break;
779790
}
780791

781792
error:
782793
if (r && r != -ERESTARTSYS)
783794
DRM_ERROR("Couldn't update BO_VA (%d)\n", r);
784795

785-
/*
786-
* If we managed to pick a more specific last-update fence, prefer it
787-
* over the generic clear_fence and drop the extra reference to the
788-
* latter.
789-
*/
790-
if (last_update) {
791-
dma_fence_put(clear_fence);
792-
return last_update;
793-
}
794-
795-
return clear_fence;
796+
return fence;
796797
}
797798

798799
int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,

0 commit comments

Comments
 (0)