FAILED: patch "[PATCH] drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x b4f7b4459b1b155e4c4977a6482b5df2cf08758c
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092916-yearling-rise-4267@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From b4f7b4459b1b155e4c4977a6482b5df2cf08758c Mon Sep 17 00:00:00 2001
From: Wentao Liang <[email protected]>
Date: Wed, 16 Sep 2026 09:55:36 +0000
Subject: [PATCH] drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()
amdgpu_vm_init() initializes vm->last_update, vm->last_unlocked and vm->last_tlb_flush with references to the stub fence taken via dma_fence_get_stub(). The error label at the end of the function releases the last_unlocked and last_tlb_flush references with dma_fence_put(), but the reference stored in vm->last_update is never dropped, so whenever the page table root creation, the reservation of the root BO or the PASID registration fails, the stub fence reference leaks.
Drop the vm->last_update reference together with the other stub fence references on the error path.
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -0
@@ -2678,6 +2678,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,amdgpu_bo_unref (&root_bo); error_free_delayed: +dma_fence_put (vm->last_update);dma_fence_put (vm->last_tlb_flush);dma_fence_put (vm->last_unlocked);ttm_lru_bulk_move_fini (&adev->mman.bdev, &vm->lru_bulk_move);
Sasha Levin
maintainerPATCH 1/4 drm/amdgpu: simplify VM update tracking a bit
From: Christian König <[email protected]>
[ Upstream commit e997b82745a5b2419bc5b72735811d7162ac994a ]
Store the 64bit sequence directly. Makes it simpler to use and saves a bit of fence reference counting overhead.
Stable-dep-of: b4f7b4459b1b ("drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()")
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 40 +++++++----------------- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 9 ++---- drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h | 2 +- 4 files changed, 16 insertions(+), 37 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +11 -29
@@ -276,19 +276,15 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm,struct amdgpu_device *adev = ring->adev;unsigned vmhub = ring->funcs->vmhub;uint64_t fence_context = adev->fence_context + ring->idx; -struct dma_fence *updates = sync->last_vm_update;bool needs_flush = vm->use_cpu_for_update; -int r =0 ; +uint64_t updates = sync->last_vm_update; +int r; *id = vm->reserved_vmid[vmhub]; -if (updates && (*id)->flushed_updates && - updates->context == (*id)->flushed_updates->context && - !dma_fence_is_later (updates, (*id)->flushed_updates)) - updates =NULL ; -if ((*id)->owner != vm->immediate.fence_context || - job->vm_pd_addr != (*id)->pd_gpu_addr || - updates || !(*id)->last_flush || + (*id)->pd_gpu_addr != job->vm_pd_addr || + (*id)->flushed_updates < updates || + !(*id)->last_flush || ((*id)->last_flush->context != fence_context && !dma_fence_is_signaled ((*id)->last_flush))) {struct dma_fence *tmp;
@@ -302,8 +298,7 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm, tmp =amdgpu_sync_peek_fence (&(*id)->active, ring);if (tmp) { *id =NULL ; - r =amdgpu_sync_fence (sync, tmp); -return r; +return amdgpu_sync_fence (sync, tmp); } needs_flush = true; }
@@ -315,10 +310,7 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm,if (r)return r; -if (updates) { -dma_fence_put ((*id)->flushed_updates); - (*id)->flushed_updates =dma_fence_get (updates); - } + (*id)->flushed_updates = updates; job->vm_needs_flush = needs_flush;return 0 ; }
@@ -346,7 +338,7 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm,unsigned vmhub = ring->funcs->vmhub;struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];uint64_t fence_context = adev->fence_context + ring->idx; -struct dma_fence *updates = sync->last_vm_update; +uint64_t updates = sync->last_vm_update;int r; job->vm_needs_flush = vm->use_cpu_for_update;
@@ -354,7 +346,6 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm,/* Check if we can use a VMID already assigned to this VM */ list_for_each_entry_reverse ((*id), &id_mgr->ids_lru, list) {bool needs_flush = vm->use_cpu_for_update; -struct dma_fence *flushed;/* Check all the prerequisites to using this VMID */ if ((*id)->owner != vm->immediate.fence_context)
@@ -368,8 +359,7 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm, !dma_fence_is_signaled((*id)->last_flush))) needs_flush = true; - flushed = (*id)->flushed_updates; -if (updates && (!flushed ||dma_fence_is_later (updates, flushed))) +if ((*id)->flushed_updates < updates) needs_flush = true;if (needs_flush && !adev->vm_manager.concurrent_flush)
@@ -382,11 +372,7 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm,if (r)return r; -if (updates && (!flushed ||dma_fence_is_later (updates, flushed))) { -dma_fence_put ((*id)->flushed_updates); - (*id)->flushed_updates =dma_fence_get (updates); - } - + (*id)->flushed_updates = updates; job->vm_needs_flush |= needs_flush;return 0 ; }
@@ -432,8 +418,6 @@ int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring, goto error;if (!id) { -struct dma_fence *updates = sync->last_vm_update; -/* Still no ID to use? Then use the idle one found earlier */ id = idle;
@@ -442,8 +426,7 @@ int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring,if (r) goto error; -dma_fence_put (id->flushed_updates); - id->flushed_updates =dma_fence_get (updates); + id->flushed_updates = sync->last_vm_update; job->vm_needs_flush = true; }
@@ -610,7 +593,6 @@ void amdgpu_vmid_mgr_fini(struct amdgpu_device *adev)struct amdgpu_vmid *id = &id_mgr->ids[j];amdgpu_sync_free (&id->active); -dma_fence_put (id->flushed_updates);dma_fence_put (id->last_flush);dma_fence_put (id->pasid_mapping); }
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h +1 -1
@@ -47,7 +47,7 @@ struct amdgpu_vmid {uint64_t pd_gpu_addr;/* last flushed PD/PT update */ -struct dma_fence *flushed_updates; +uint64_t flushed_updates;uint32_t current_gpu_reset_count;
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +3 -6
@@ -51,7 +51,7 @@ static struct kmem_cache *amdgpu_sync_slab;void amdgpu_sync_create (struct amdgpu_sync *sync) {hash_init (sync->fences); - sync->last_vm_update =NULL ; + sync->last_vm_update =0 ; }/**
@@ -184,7 +184,7 @@ int amdgpu_sync_vm_fence(struct amdgpu_sync *sync, struct dma_fence *fence)if (!fence)return 0 ; -amdgpu_sync_keep_later (&sync->last_vm_update, fence); + sync->last_vm_update =max (sync->last_vm_update, fence->seqno);return amdgpu_sync_fence (sync, fence); }
@@ -394,8 +394,7 @@ int amdgpu_sync_clone(struct amdgpu_sync *source, struct amdgpu_sync *clone) } } -dma_fence_put (clone->last_vm_update); - clone->last_vm_update =dma_fence_get (source->last_vm_update); + clone->last_vm_update = source->last_vm_update;return 0 ; }
@@ -437,8 +436,6 @@ void amdgpu_sync_free(struct amdgpu_sync *sync)dma_fence_put (e->fence);kmem_cache_free (amdgpu_sync_slab, e); } - -dma_fence_put (sync->last_vm_update); }/**
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h +1 -1
@@ -43,7 +43,7 @@ enum amdgpu_sync_mode {*/ struct amdgpu_sync { DECLARE_HASHTABLE(fences, 4); -struct dma_fence *last_vm_update; +uint64_t last_vm_update; };void amdgpu_sync_create (struct amdgpu_sync *sync);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 drm/amdgpu: rework TLB flushing
From: Christian König <[email protected]>
[ Upstream commit 5255e146c99a677d4d55fdb988544bd20c539a0b ]
Instead of tracking the VM updates through the dependencies just use a sequence counter for page table updates which indicates the need to flush the TLB.
This reduces the need to flush the TLB drastically.
v2: squash in NULL check fix (Christian)
Stable-dep-of: b4f7b4459b1b ("drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()")
--- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 8 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 6 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 20 --------- drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h | 2 - drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 57 ++++++++++++++++++++++-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 15 +++++++ 6 files changed, 76 insertions(+), 32 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +4 -4
@@ -793,7 +793,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)if (r)return r; - r =amdgpu_sync_vm_fence (&p->job->sync, fpriv->prt_va->last_pt_update); + r =amdgpu_sync_fence (&p->job->sync, fpriv->prt_va->last_pt_update);if (r)return r;
@@ -804,7 +804,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)if (r)return r; - r =amdgpu_sync_vm_fence (&p->job->sync, bo_va->last_pt_update); + r =amdgpu_sync_fence (&p->job->sync, bo_va->last_pt_update);if (r)return r; }
@@ -823,7 +823,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)if (r)return r; - r =amdgpu_sync_vm_fence (&p->job->sync, bo_va->last_pt_update); + r =amdgpu_sync_fence (&p->job->sync, bo_va->last_pt_update);if (r)return r; }
@@ -836,7 +836,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)if (r)return r; - r =amdgpu_sync_vm_fence (&p->job->sync, vm->last_update); + r =amdgpu_sync_fence (&p->job->sync, vm->last_update);if (r)return r;
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +3 -3
@@ -277,7 +277,7 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm,unsigned vmhub = ring->funcs->vmhub;uint64_t fence_context = adev->fence_context + ring->idx;bool needs_flush = vm->use_cpu_for_update; -uint64_t updates = sync->last_vm_update; +uint64_t updates =amdgpu_vm_tlb_seq (vm);int r; *id = vm->reserved_vmid[vmhub];
@@ -338,7 +338,7 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm,unsigned vmhub = ring->funcs->vmhub;struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];uint64_t fence_context = adev->fence_context + ring->idx; -uint64_t updates = sync->last_vm_update; +uint64_t updates =amdgpu_vm_tlb_seq (vm);int r; job->vm_needs_flush = vm->use_cpu_for_update;
@@ -426,7 +426,7 @@ int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring,if (r) goto error; - id->flushed_updates = sync->last_vm_update; + id->flushed_updates =amdgpu_vm_tlb_seq (vm); job->vm_needs_flush = true; }
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +0 -20
@@ -51,7 +51,6 @@ static struct kmem_cache *amdgpu_sync_slab;void amdgpu_sync_create (struct amdgpu_sync *sync) {hash_init (sync->fences); - sync->last_vm_update =0 ; }/**
@@ -171,23 +170,6 @@ int amdgpu_sync_fence(struct amdgpu_sync *sync, struct dma_fence *f)return 0 ; } -/** -* amdgpu_sync_vm_fence - remember to sync to this VM fence -* -* @sync: sync object to add fence to -* @fence: the VM fence to add -* -* Add the fence to the sync object and remember it as VM update. -*/ -int amdgpu_sync_vm_fence (struct amdgpu_sync *sync,struct dma_fence *fence) -{ -if (!fence) -return 0 ; - - sync->last_vm_update =max (sync->last_vm_update, fence->seqno); -return amdgpu_sync_fence (sync, fence); -} -/* Determine based on the owner and mode if we should sync to a fence or not */ static bool amdgpu_sync_test_fence(struct amdgpu_device *adev, enum amdgpu_sync_mode mode,
@@ -394,8 +376,6 @@ int amdgpu_sync_clone(struct amdgpu_sync *source, struct amdgpu_sync *clone) } } - clone->last_vm_update = source->last_vm_update; -return 0 ; }
drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h +0 -2
@@ -43,12 +43,10 @@ enum amdgpu_sync_mode {*/ struct amdgpu_sync { DECLARE_HASHTABLE(fences, 4); -uint64_t last_vm_update; };void amdgpu_sync_create (struct amdgpu_sync *sync);int amdgpu_sync_fence (struct amdgpu_sync *sync,struct dma_fence *f); -int amdgpu_sync_vm_fence (struct amdgpu_sync *sync,struct dma_fence *fence);int amdgpu_sync_resv (struct amdgpu_device *adev,struct amdgpu_sync *sync,struct dma_resv *resv,enum amdgpu_sync_mode mode,void *owner);
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +54 -3
108 lines not on this page · raw message
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +15 -0
@@ -284,6 +284,9 @@ struct amdgpu_vm {struct drm_sched_entity immediate;struct drm_sched_entity delayed; +/* Last finished delayed update */ +atomic64_t tlb_seq; +/* Last unlocked submission to the scheduler entities */ struct dma_fence *last_unlocked;
@@ -462,4 +465,16 @@ void amdgpu_vm_get_memory(struct amdgpu_vm *vm, uint64_t *vram_mem,void amdgpu_debugfs_vm_bo_info (struct amdgpu_vm *vm,struct seq_file *m);#endif +/** +* amdgpu_vm_tlb_seq - return tlb flush sequence number +* @vm: the amdgpu_vm structure to query +* +* Returns the tlb flush sequence number which indicates that the VM TLBs needs +* to be invalidated whenever the sequence number change. +*/ +static inline uint64_t amdgpu_vm_tlb_seq (struct amdgpu_vm *vm) +{ +return atomic64_read (&vm->tlb_seq); +} +#endif
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 drm/amdgpu: Fix one use-after-free of VM
In reply to Sasha LevinFrom: xinhui pan <[email protected]>
[ Upstream commit 7c703a7d3f2b50a6187267420a4d3d7e62fa3206 ]
VM might already be freed when amdgpu_vm_tlb_seq_cb() is called. We see the calltrace below.
Fix it by keeping the last flush fence around and wait for it to signal
BUG kmalloc-4k (Not tainted): Poison overwritten
0xffff9c88630414e8-0xffff9c88630414e8 @offset=5352. First byte 0x6c instead of 0x6b Allocated in amdgpu_driver_open_kms+0x9d/0x360 [amdgpu] age=44 cpu=0 pid=2343
__slab_alloc.isra.0+0x4f/0x90 kmem_cache_alloc_trace+0x6b8/0x7a0 amdgpu_driver_open_kms+0x9d/0x360 [amdgpu] drm_file_alloc+0x222/0x3e0 [drm] drm_open+0x11d/0x410 [drm] Freed in amdgpu_driver_postclose_kms+0x3e9/0x550 [amdgpu] age=22 cpu=1 pid=2485 kfree+0x4a2/0x580 amdgpu_driver_postclose_kms+0x3e9/0x550 [amdgpu] drm_file_free+0x24e/0x3c0 [drm] drm_close_helper.isra.0+0x90/0xb0 [drm] drm_release+0x97/0x1a0 [drm] __fput+0xb6/0x280 ____fput+0xe/0x10 task_work_run+0x64/0xb0
Stable-dep-of: b4f7b4459b1b ("drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()")
--- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 18 +++++++++++++++--- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +15 -3
@@ -1843,10 +1843,14 @@ int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,if (!unlocked && (!(flags &AMDGPU_PTE_VALID ) || params.table_freed)) { tlb_cb->vm = vm; -if (!fence || !*fence || -dma_fence_add_callback (*fence, &tlb_cb->cb, - amdgpu_vm_tlb_seq_cb)) +if (fence && *fence && + !dma_fence_add_callback (*fence, &tlb_cb->cb, + amdgpu_vm_tlb_seq_cb)) { +dma_fence_put (vm->last_tlb_flush); + vm->last_tlb_flush =dma_fence_get (*fence); + }else {amdgpu_vm_tlb_seq_cb (NULL , &tlb_cb->cb); + } tlb_cb =NULL ; }
@@ -3046,6 +3050,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) vm->update_funcs = &amdgpu_vm_sdma_funcs; vm->last_update =NULL ; vm->last_unlocked =dma_fence_get_stub (); + vm->last_tlb_flush =dma_fence_get_stub ();mutex_init (&vm->eviction_lock); vm->evicting = false;
@@ -3084,6 +3089,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) vm->root.bo =NULL ; error_free_delayed: +dma_fence_put (vm->last_tlb_flush);dma_fence_put (vm->last_unlocked);drm_sched_entity_destroy (&vm->delayed);
@@ -3228,6 +3234,7 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)struct amdgpu_bo_va_mapping *mapping, *tmp;bool prt_fini_needed = !!adev->gmc.gmc_funcs->set_prt;struct amdgpu_bo *root; +unsigned long flags;int i;amdgpu_amdkfd_gpuvm_destroy_cb (adev, vm);
@@ -3237,6 +3244,11 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)amdgpu_vm_set_pasid (adev, vm,0 );dma_fence_wait (vm->last_unlocked, false);dma_fence_put (vm->last_unlocked); +dma_fence_wait (vm->last_tlb_flush, false); +/* Make sure that all fence callbacks have completed */ +spin_lock_irqsave (vm->last_tlb_flush->lock, flags); +spin_unlock_irqrestore (vm->last_tlb_flush->lock, flags); +dma_fence_put (vm->last_tlb_flush);list_for_each_entry_safe (mapping, tmp, &vm->freed, list) {if (mapping->flags &AMDGPU_PTE_PRT && prt_fini_needed) {
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +1 -0
@@ -286,6 +286,7 @@ struct amdgpu_vm {/* Last finished delayed update */ atomic64_t tlb_seq; +struct dma_fence *last_tlb_flush;/* Last unlocked submission to the scheduler entities */ struct dma_fence *last_unlocked;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 drm/amdgpu: Fix last_update fence leak in amdgpu_vm_init()
In reply to Sasha LevinFrom: Wentao Liang <[email protected]>
[ Upstream commit b4f7b4459b1b155e4c4977a6482b5df2cf08758c ]
amdgpu_vm_init() initializes vm->last_update, vm->last_unlocked and vm->last_tlb_flush with references to the stub fence taken via dma_fence_get_stub(). The error label at the end of the function releases the last_unlocked and last_tlb_flush references with dma_fence_put(), but the reference stored in vm->last_update is never dropped, so whenever the page table root creation, the reservation of the root BO or the PASID registration fails, the stub fence reference leaks.
Drop the vm->last_update reference together with the other stub fence references on the error path.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -0
@@ -3089,6 +3089,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) vm->root.bo =NULL ; error_free_delayed: +dma_fence_put (vm->last_update);dma_fence_put (vm->last_tlb_flush);dma_fence_put (vm->last_unlocked);drm_sched_entity_destroy (&vm->delayed);
Signature · 1 line
2.53.0