From 74f28db2db69777cd2f059d50fe34e365ddd5add Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 29 Jul 2026 15:56:46 +0800 Subject: [PATCH 01/13] drm/amd/pm: hide pp_table sysfs on APUs APUs use firmware-owned DPM tables and do not support replacement through pp_table. Generic callbacks can nevertheless expose the sysfs file and accept an upload before resetting the power management stack. Treat pp_table as unsupported on APUs. Use the same platform check in the get and set paths to hide the file and reject uploads. Fixes: 289921b03fe5 ("drm/amd/powerplay: implement sysfs of pp_table for smu11 (v2)") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 8f59ea12fe0e..c3688b3b12cc 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1183,6 +1183,13 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } +static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev) +{ + return !amdgpu_sriov_vf(adev) && + !(adev->flags & AMD_IS_APU) && + !adev->scpm_enabled; +} + int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, size_t size) { @@ -1193,7 +1200,8 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, if ((!table && size) || (table && !size)) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->get_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); @@ -1717,7 +1725,8 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev, if (!buf || !size) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->set_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); From 6aabceae65e777f8e1ecac6beed171f2626015ce Mon Sep 17 00:00:00 2001 From: Philip Yang Date: Fri, 17 Jul 2026 20:54:51 -0400 Subject: [PATCH 02/13] drm/amdkfd: Remove svm_bo eviction fence SVM BOs are now migrated back to system memory synchronously from the TTM eviction path (svm_range_evict_svm_bo), so the per-svm_bo eviction fence is no longer used. Remove the eviction fence from svm_range_bo, drop the amdgpu_amdkfd_fence->svm_bo back pointer and the amdgpu_amdkfd_evict_svm_bo() helper, and stop special-casing svm_bo fences in the KFD fence enable_signaling and check_mm paths. Embed struct amdgpu_bo directly in svm_range_bo with a dedicated svm_range_bo_destroy() callback, and keep the owning mm via mmgrab()/mmdrop() instead of through the fence. Signed-off-by: Philip Yang Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 21 ---- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 3 - .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c | 15 +-- .../gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 4 +- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 4 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 6 +- drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 4 +- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 109 +++++++----------- drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 13 ++- 9 files changed, 64 insertions(+), 115 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index be764b6802b5..816d8817f0b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -973,24 +973,3 @@ int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev, return kgd2kfd_reset_mes_queue(adev->kfd.dev, node_id, queue_type, pipe, queue, db); } - -int amdgpu_amdkfd_evict_svm_bo(struct amdgpu_bo *bo) -{ - struct dma_resv_iter cursor; - struct dma_fence *fence; - int r = 0; - - dma_resv_iter_begin(&cursor, bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP); - dma_resv_for_each_fence_unlocked(&cursor, fence) { - struct amdgpu_amdkfd_fence *f = to_amdgpu_amdkfd_fence(fence); - - if (f && f->svm_bo) { - r = svm_range_evict_svm_bo(f->svm_bo); - if (r) - break; - } - } - dma_resv_iter_end(&cursor); - - return r; -} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index b4840ee36f2b..1b7dc0d3963b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -99,7 +99,6 @@ struct amdgpu_amdkfd_fence { struct mm_struct *mm; spinlock_t lock; char timeline_name[TASK_COMM_LEN]; - struct svm_range_bo *svm_bo; uint16_t context_id; }; @@ -194,7 +193,6 @@ int amdgpu_queue_mask_bit_to_set_resource_bit(struct amdgpu_device *adev, struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context, struct mm_struct *mm, - struct svm_range_bo *svm_bo, u16 context_id); int amdgpu_amdkfd_drm_client_create(struct amdgpu_device *adev); @@ -286,7 +284,6 @@ int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev, int queue_type, int pipe, int queue, unsigned int db); -int amdgpu_amdkfd_evict_svm_bo(struct amdgpu_bo *bo); /* Read user wptr from a specified user address space with page fault * disabled. The memory must be pinned and mapped to the hardware when diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c index 553d26c2744e..9b10d015671c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c @@ -62,7 +62,6 @@ static atomic_t fence_seq = ATOMIC_INIT(0); struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context, struct mm_struct *mm, - struct svm_range_bo *svm_bo, u16 context_id) { struct amdgpu_amdkfd_fence *fence; @@ -76,7 +75,6 @@ struct amdgpu_amdkfd_fence *amdgpu_amdkfd_fence_create(u64 context, fence->mm = mm; get_task_comm(fence->timeline_name, current); spin_lock_init(&fence->lock); - fence->svm_bo = svm_bo; fence->context_id = context_id; dma_fence_init(&fence->base, &amdkfd_fence_ops, &fence->lock, context, atomic_inc_return(&fence_seq)); @@ -128,14 +126,8 @@ static bool amdkfd_fence_enable_signaling(struct dma_fence *f) if (dma_fence_is_signaled(f)) return true; - /* if fence->svm_bo is NULL, means this fence is created through - * init_kfd_vm() or amdgpu_amdkfd_gpuvm_restore_process_bos(). - * Therefore, this fence is amdgpu_amdkfd_fence->eviction_fence. - */ - if (!fence->svm_bo) { - if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f)) - return true; - } + if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f)) + return true; return false; } @@ -169,7 +161,6 @@ static void amdkfd_fence_release(struct dma_fence *f) * * Check if @mm is same as that of the fence @f, if same return TRUE else * return FALSE. - * For svm bo, which support vram overcommitment, always return FALSE. */ bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm) { @@ -177,7 +168,7 @@ bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm) if (!fence) return false; - else if (fence->mm == mm && !fence->svm_bo) + else if (fence->mm == mm) return true; return false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 1217fb516807..34481ee7065a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1423,7 +1423,7 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info, info->eviction_fence = amdgpu_amdkfd_fence_create(dma_fence_context_alloc(1), current->mm, - NULL, process->context_id); + process->context_id); if (!info->eviction_fence) { pr_err("Failed to create eviction fence\n"); ret = -ENOMEM; @@ -3093,7 +3093,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * amdgpu_amdkfd_fence_create( process_info->eviction_fence->base.context, process_info->eviction_fence->mm, - NULL, process_info->context_id); + process_info->context_id); if (!new_fence) { pr_err("Failed to create eviction fence\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index d4a9d5e8fb42..5d9d137209b6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -43,6 +43,7 @@ #include "amdgpu_vram_mgr.h" #include "amdgpu_vm.h" #include "amdgpu_dma_buf.h" +#include "kfd_svm.h" /** * DOC: amdgpu_object @@ -93,7 +94,8 @@ static void amdgpu_bo_user_destroy(struct ttm_buffer_object *tbo) bool amdgpu_bo_is_amdgpu_bo(struct ttm_buffer_object *bo) { if (bo->destroy == &amdgpu_bo_destroy || - bo->destroy == &amdgpu_bo_user_destroy) + bo->destroy == &amdgpu_bo_user_destroy || + bo->destroy == &svm_range_bo_destroy) return true; return false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 91b7bf6e65f5..c4094edbdff1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -60,6 +60,7 @@ #include "amdgpu_atomfirmware.h" #include "amdgpu_res_cursor.h" #include "bif/bif_4_1_d.h" +#include "kfd_svm.h" MODULE_IMPORT_NS("DMA_BUF"); @@ -1499,7 +1500,8 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, return true; abo = ttm_to_amdgpu_bo(bo); - if (abo->flags & AMDGPU_GEM_CREATE_DISCARDABLE) { + if ((abo->flags & AMDGPU_GEM_CREATE_DISCARDABLE) && + bo->destroy == &svm_range_bo_destroy) { /* * SVM BOs are migrated to system memory synchronously in this * TTM eviction context. The migration needs the owning @@ -1509,7 +1511,7 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, * if the eviction fails for any reason, we return false so TTM * skips this BO instead of risking a deadlock. */ - if (amdgpu_amdkfd_evict_svm_bo(abo) < 0) + if (svm_range_evict_svm_bo(abo) < 0) return false; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index ed3649a81332..f5af1dd3b70e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -954,12 +954,12 @@ static vm_fault_t svm_migrate_to_ram(struct vm_fault *vmf) pr_debug("failed get device page at addr 0x%lx\n", addr); return VM_FAULT_SIGBUS; } - if (!mmget_not_zero(svm_bo->eviction_fence->mm)) { + if (!mmget_not_zero(svm_bo->mm)) { pr_debug("addr 0x%lx of process mm is destroyed\n", addr); return VM_FAULT_SIGBUS; } - mm = svm_bo->eviction_fence->mm; + mm = svm_bo->mm; if (mm != vmf->vma->vm_mm) pr_debug("addr 0x%lx is COW mapping in child process\n", addr); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 4c6700c6e88d..18abef8918ce 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -390,8 +390,10 @@ static bool svm_bo_ref_unless_zero(struct svm_range_bo *svm_bo) static void svm_range_bo_release(struct kref *kref) { struct svm_range_bo *svm_bo; + struct amdgpu_bo *bo; svm_bo = container_of(kref, struct svm_range_bo, kref); + bo = &svm_bo->bo; pr_debug("svm_bo 0x%p\n", svm_bo); spin_lock(&svm_bo->list_lock); @@ -417,12 +419,12 @@ static void svm_range_bo_release(struct kref *kref) } spin_unlock(&svm_bo->list_lock); - if (mmget_not_zero(svm_bo->eviction_fence->mm)) { + if (mmget_not_zero(svm_bo->mm)) { struct kfd_process_device *pdd; struct kfd_process *p; struct mm_struct *mm; - mm = svm_bo->eviction_fence->mm; + mm = svm_bo->mm; /* * The forked child process takes svm_bo device pages ref, svm_bo could be * released after parent process is gone. @@ -431,18 +433,13 @@ static void svm_range_bo_release(struct kref *kref) if (p) { pdd = kfd_get_process_device_data(svm_bo->node, p); if (pdd) - atomic64_sub(amdgpu_bo_size(svm_bo->bo), &pdd->vram_usage); + atomic64_sub(amdgpu_bo_size(bo), &pdd->vram_usage); kfd_unref_process(p); } mmput(mm); } - if (!dma_fence_is_signaled(&svm_bo->eviction_fence->base)) - /* We're not in the eviction worker. Signal the fence. */ - dma_fence_signal(&svm_bo->eviction_fence->base); - dma_fence_put(&svm_bo->eviction_fence->base); - amdgpu_bo_unref(&svm_bo->bo); - kfree(svm_bo); + amdgpu_bo_unref(&bo); } static void svm_range_bo_wq_release(struct work_struct *work) @@ -504,20 +501,11 @@ svm_range_validate_svm_bo(struct kfd_node *node, struct svm_range *prange) return false; } if (READ_ONCE(prange->svm_bo->evicting)) { - struct dma_fence *f; - struct svm_range_bo *svm_bo; /* The BO is getting evicted, * we need to get a new one */ mutex_unlock(&prange->lock); - svm_bo = prange->svm_bo; - f = dma_fence_get(&svm_bo->eviction_fence->base); svm_range_bo_unref(prange->svm_bo); - /* wait for the fence to avoid long spin-loop - * at list_empty_careful - */ - dma_fence_wait(f, false); - dma_fence_put(f); } else { /* The BO was still around and we got * a new reference to it @@ -526,7 +514,7 @@ svm_range_validate_svm_bo(struct kfd_node *node, struct svm_range *prange) pr_debug("reuse old bo svms 0x%p [0x%lx 0x%lx]\n", prange->svms, prange->start, prange->last); - prange->ttm_res = prange->svm_bo->bo->tbo.resource; + prange->ttm_res = prange->svm_bo->bo.tbo.resource; return true; } @@ -545,19 +533,22 @@ svm_range_validate_svm_bo(struct kfd_node *node, struct svm_range *prange) return false; } -static struct svm_range_bo *svm_range_bo_new(void) +#define to_svm_range_bo(bo) container_of((bo), struct svm_range_bo, bo) + +void svm_range_bo_destroy(struct ttm_buffer_object *tbo) { - struct svm_range_bo *svm_bo; + struct amdgpu_bo *bo = ttm_to_amdgpu_bo(tbo); + struct svm_range_bo *svm_bo = to_svm_range_bo(bo); - svm_bo = kzalloc_obj(*svm_bo); - if (!svm_bo) - return NULL; - - kref_init(&svm_bo->kref); - INIT_LIST_HEAD(&svm_bo->range_list); - spin_lock_init(&svm_bo->list_lock); - - return svm_bo; + drm_gem_object_release(&bo->tbo.base); + /* + * svm_bo->mm is only set once the BO is fully created. If + * ttm_bo_init_reserved() fails (e.g. no VRAM could be evicted), it + * calls this destroy callback with mm still NULL, so guard the drop. + */ + if (svm_bo->mm) + mmdrop(svm_bo->mm); + kvfree(svm_bo); } int @@ -567,7 +558,6 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, struct kfd_process_device *pdd; struct amdgpu_bo_param bp; struct svm_range_bo *svm_bo; - struct amdgpu_bo_user *ubo; struct amdgpu_bo *bo; struct kfd_process *p; struct mm_struct *mm; @@ -581,26 +571,16 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, if (svm_range_validate_svm_bo(node, prange)) return 0; - svm_bo = svm_range_bo_new(); - if (!svm_bo) { - pr_debug("failed to alloc svm bo\n"); - return -ENOMEM; - } mm = get_task_mm(p->lead_thread); if (!mm) { pr_debug("failed to get mm\n"); - kfree(svm_bo); return -ESRCH; } - svm_bo->node = node; - svm_bo->eviction_fence = - amdgpu_amdkfd_fence_create(dma_fence_context_alloc(1), - mm, - svm_bo, p->context_id); - mmput(mm); - svm_bo->evicting = 0; + memset(&bp, 0, sizeof(bp)); bp.size = prange->npages * PAGE_SIZE; + bp.bo_ptr_size = sizeof(struct svm_range_bo); + bp.destroy = svm_range_bo_destroy; bp.byte_align = PAGE_SIZE; bp.domain = AMDGPU_GEM_DOMAIN_VRAM; bp.flags = AMDGPU_GEM_CREATE_NO_CPU_ACCESS; @@ -611,12 +591,23 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, if (node->xcp) bp.xcp_id_plus1 = node->xcp->id + 1; - r = amdgpu_bo_create_user(node->adev, &bp, &ubo); + r = amdgpu_bo_create(node->adev, &bp, &bo); if (r) { pr_debug("failed %d to create bo\n", r); + mmput(mm); goto create_bo_failed; } - bo = &ubo->bo; + + svm_bo = to_svm_range_bo(bo); + svm_bo->evicting = 0; + kref_init(&svm_bo->kref); + INIT_LIST_HEAD(&svm_bo->range_list); + spin_lock_init(&svm_bo->list_lock); + + svm_bo->node = node; + svm_bo->mm = mm; + mmgrab(svm_bo->mm); + mmput(mm); pr_debug("alloc bo at offset 0x%lx size 0x%lx on partition %d\n", bo->tbo.resource->start << PAGE_SHIFT, bp.size, @@ -637,16 +628,8 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, } } - r = dma_resv_reserve_fences(bo->tbo.base.resv, TTM_NUM_MOVE_FENCES); - if (r) { - amdgpu_bo_unreserve(bo); - goto reserve_bo_failed; - } - amdgpu_bo_fence(bo, &svm_bo->eviction_fence->base, true); - amdgpu_bo_unreserve(bo); - svm_bo->bo = bo; prange->svm_bo = svm_bo; prange->ttm_res = bo->tbo.resource; prange->offset = 0; @@ -664,9 +647,6 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, reserve_bo_failed: amdgpu_bo_unref(&bo); create_bo_failed: - dma_fence_put(&svm_bo->eviction_fence->base); - kfree(svm_bo); - prange->ttm_res = NULL; return r; } @@ -3640,19 +3620,20 @@ svm_range_trigger_migration(struct mm_struct *mm, struct svm_range *prange, return 0; } -int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo) +int svm_range_evict_svm_bo(struct amdgpu_bo *bo) { + struct svm_range_bo *svm_bo = to_svm_range_bo(bo); struct mm_struct *mm; int r = 0; if (!svm_bo_ref_unless_zero(svm_bo)) return 0; - if (!mmget_not_zero(svm_bo->eviction_fence->mm)) { + if (!mmget_not_zero(svm_bo->mm)) { svm_range_bo_unref(svm_bo); return 0; } - mm = svm_bo->eviction_fence->mm; + mm = svm_bo->mm; /* * Called with the BO reserved; lock order is mmap_lock -> BO @@ -3721,14 +3702,6 @@ int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo) /* Defer mmput: exit_mmap() must not run under the BO reservation. */ mmput_async(mm); - /* - * Only signal the eviction fence once the ranges have been processed. - * On -EBUSY we bailed out without migrating; leave the BO in VRAM and - * let TTM retry later. - */ - if (r != -EBUSY) - dma_fence_signal(&svm_bo->eviction_fence->base); - /* This is the last reference to svm_bo, after svm_range_vram_node_free * has been called in svm_migrate_vram_to_ram */ diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h index 4232c47422e6..284c4cd5aa0f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h @@ -39,11 +39,11 @@ ((adev)->hive ? (void *)(adev)->hive : (void *)(adev)) struct svm_range_bo { - struct amdgpu_bo *bo; + struct amdgpu_bo bo; struct kref kref; struct list_head range_list; /* all svm ranges shared this bo */ spinlock_t list_lock; - struct amdgpu_amdkfd_fence *eviction_fence; + struct mm_struct *mm; uint32_t evicting; struct work_struct release_work; struct kfd_node *node; @@ -168,13 +168,14 @@ struct svm_range *svm_range_from_addr(struct svm_range_list *svms, struct svm_range **parent); struct kfd_node *svm_range_get_node_by_id(struct svm_range *prange, uint32_t gpu_id); +void svm_range_bo_destroy(struct ttm_buffer_object *tbo); int svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, bool clear); void svm_range_vram_node_free(struct svm_range *prange); int svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid, uint32_t vmid, uint32_t node_id, uint64_t addr, uint64_t ts, bool write_fault); -int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo); +int svm_range_evict_svm_bo(struct amdgpu_bo *bo); void svm_range_add_list_work(struct svm_range_list *svms, struct svm_range *prange, struct mm_struct *mm, @@ -229,7 +230,11 @@ static inline int svm_range_restore_pages(struct amdgpu_device *adev, return -EFAULT; } -static inline int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo) +static inline void svm_range_bo_destroy(struct ttm_buffer_object *tbo) +{ +} + +static inline int svm_range_evict_svm_bo(struct amdgpu_bo *bo) { return 0; } From 05984e29520a28c27f5a2388742c957a6a87ee7a Mon Sep 17 00:00:00 2001 From: Leo Li Date: Tue, 28 Jul 2026 13:02:47 -0400 Subject: [PATCH 03/13] drm/amd/display: Exit idle optimizations before programming [Why] We need to exit PSR/IPS before programming. Before calling DC for programming in amdgpu_dm_commit_planes(), there's a vblank_control_workqueue flush. This waits for IPS and PSR exit. (See drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()-> amdgpu_dm_crtc_vblank_control_worker()) Prior to the tagged "Fixes:" change, drm_vblank_get() was called before the workqueue flush. This ordering ensures that PSR exit occurred before programming. After the "Fixes:" change, drm_vblank_get() is called after the workqueue flush, leading to programming while idle optimizations are still active. This can lead to incorrect flip_pending detection used by vblank event delivery. [How] Split the vblank_get() component of `dm_arm_vblank_event()` into `dm_arm_vblank_event_pre_programming()`, which is called before programming. Call it before the vblank_control_workqueue flush. Includes a drive-by cleanup of prepare_flip_isr(): the only caller is dm_arm_vblank_event() and it's simple enough to roll-in. v2: Fix checkpatch formatting warning on drm_arm_vblank_event_pre_programming() arg alignment. Fixes: f64a9be56536 ("drm/amd/display: check GRPH_FLIP status before sending event") Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527 Assisted-by: Codex:gpt-5.6-sol Assisted-by: Claude:opus-5 Suggested-by: David Weber Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index f22388e57e98..e421c3c81778 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3576,25 +3576,6 @@ static void remove_stream(struct amdgpu_device *adev, acrtc->enabled = false; } -static void prepare_flip_isr(struct amdgpu_crtc *acrtc) -{ - - assert_spin_locked(&acrtc->base.dev->event_lock); - WARN_ON(acrtc->event); - - acrtc->event = acrtc->base.state->event; - - /* Set the flip status */ - acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; - - /* Mark this event as consumed */ - acrtc->base.state->event = NULL; - - drm_dbg_state(acrtc->base.dev, - "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", - acrtc->crtc_id); -} - static void amdgpu_dm_commit_cursors(struct drm_atomic_commit *state) { struct drm_plane *plane; @@ -3740,17 +3721,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, return; if (pflip_update) { - drm_crtc_vblank_get(&acrtc->base); WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); - /* Arm flip completion handling and event delivery after programming. */ - prepare_flip_isr(acrtc); + WARN_ON(acrtc->event); + + acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; + acrtc->event = acrtc->base.state->event; + acrtc->base.state->event = NULL; + + drm_dbg_state(acrtc->base.dev, + "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", + acrtc->crtc_id); } else if (cursor_update) { - drm_crtc_vblank_get(&acrtc->base); acrtc->event = acrtc->base.state->event; acrtc->base.state->event = NULL; } } +/** + * dm_arm_vblank_event_pre_programming - Prepare for programming + * @acrtc: The amdgpu CRTC to prepare + * @acrtc_state: The new CRTC state + * @pflip_update: Whether a page flip is being programmed + * @cursor_update: Whether a cursor update is being programmed + * + * Grab a reference on the vblank counter if a page flip or cursor update is to + * be programmed. Do this before programming so the HW is not in any + * idle-optimized state (such as PSR). + */ +static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc, + struct dm_crtc_state *acrtc_state, + bool pflip_update, + bool cursor_update) +{ + assert_spin_locked(&acrtc->base.dev->event_lock); + + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) + return; + + if (pflip_update || cursor_update) + drm_crtc_vblank_get(&acrtc->base); +} + static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, struct drm_device *dev, struct amdgpu_display_manager *dm, @@ -4013,16 +4024,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } } - /* - * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for - * event delivery. Only GRPH_FLIP handler can send pflip events, and it - * only fires if HW latched to the flip. Maintain legacy behavior by - * arming event before programming. - */ - if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { - scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state, + pflip_present, + cursor_update); + /* + * DCE depends on a combination of GRPH_FLIP, VLINE0, and + * VUPDATE for event delivery. Only GRPH_FLIP handler can send + * pflip events, and it only fires if HW latched to the flip. + * Maintain legacy behavior by arming event before programming. + */ + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { dm_arm_vblank_event(acrtc_attach, acrtc_state, - pflip_present, cursor_update); + pflip_present, cursor_update); } } From 9fe8bb9bd7fabfecbe3d9f61bcf87c4e30dac4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:14 +0200 Subject: [PATCH 04/13] drm/amdgpu/gfx7: Make amdgpu_gfx_mqd_sw_init() usable on GFX7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GFX7 supports KIQ, but amdgpu doesn't use it. Change amdgpu_gfx_mqd_sw_init() to only allocate the MQD BO for the KIQ on GFX8 and newer (that is, TOPAZ and newer). This makes amdgpu_gfx_mqd_sw_init() usable on GFX7 without any further changes to its functionality. Signed-off-by: Timur Kristóf Reviewed-by: Tvrtko Ursulin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 9d3b40c385c9..d763f0baf0e8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -419,8 +419,8 @@ int amdgpu_gfx_mqd_sw_init(struct amdgpu_device *adev, domain |= AMDGPU_GEM_DOMAIN_VRAM; #endif - /* create MQD for KIQ */ - if (!adev->enable_mes_kiq && !ring->mqd_obj) { + /* create MQD for KIQ (on GFX8+ where we use KIQ) */ + if (adev->asic_type >= CHIP_TOPAZ && !adev->enable_mes_kiq && !ring->mqd_obj) { /* originaly the KIQ MQD is put in GTT domain, but for SRIOV VRAM domain is a must * otherwise hypervisor trigger SAVE_VF fail after driver unloaded which mean MQD * deallocated and gart_unbind, to strict diverage we decide to use VRAM domain for From 8107ad93b6b26aaec692cca044107e480d662b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:15 +0200 Subject: [PATCH 05/13] drm/amdgpu/gfx7: Refactor MQD initialization and finalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call amdgpu_gfx_mqd_sw_init()/_fini() on GFX7 to initialize and finalize MQD BOs, just like GFX8 and newer; instead of doing an ad-hoc BO allocation. Introduce the possibility of backing up the MQD instead of trying to reinitialize every time. This solves an issue with GFX IP block soft reset where all compute rings would hang after the reset. Rename gfx_v7_0_mqd_deactivate() to gfx_v7_0_deactivate_hqd() to more closely reflect what it does and for consistency with the GFX8 code. Signed-off-by: Timur Kristóf Reviewed-by: Tvrtko Ursulin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 104 +++++++++++++------------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index b93bad1d2a6f..e2a07f9c8b48 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2699,25 +2699,6 @@ static int gfx_v7_0_cp_compute_load_microcode(struct amdgpu_device *adev) return 0; } -/** - * gfx_v7_0_cp_compute_fini - stop the compute queues - * - * @adev: amdgpu_device pointer - * - * Stop the compute queues and tear down the driver queue - * info. - */ -static void gfx_v7_0_cp_compute_fini(struct amdgpu_device *adev) -{ - int i; - - for (i = 0; i < adev->gfx.num_compute_rings; i++) { - struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; - - amdgpu_bo_free_kernel(&ring->mqd_obj, NULL, NULL); - } -} - static void gfx_v7_0_mec_fini(struct amdgpu_device *adev) { amdgpu_bo_free_kernel(&adev->gfx.mec.hpd_eop_obj, NULL, NULL); @@ -2789,28 +2770,29 @@ static void gfx_v7_0_compute_pipe_init(struct amdgpu_device *adev, mutex_unlock(&adev->srbm_mutex); } -static int gfx_v7_0_mqd_deactivate(struct amdgpu_device *adev) +static int gfx_v7_0_deactivate_hqd(struct amdgpu_device *adev, u32 req) { - int i; + int i, r = 0; /* disable the queue if it's active */ - if (RREG32(mmCP_HQD_ACTIVE) & 1) { - WREG32(mmCP_HQD_DEQUEUE_REQUEST, 1); + if (RREG32(mmCP_HQD_ACTIVE) & CP_HQD_ACTIVE__ACTIVE_MASK) { + WREG32_FIELD(CP_HQD_DEQUEUE_REQUEST, DEQUEUE_REQ, req); for (i = 0; i < adev->usec_timeout; i++) { - if (!(RREG32(mmCP_HQD_ACTIVE) & 1)) + if (!(RREG32(mmCP_HQD_ACTIVE) & CP_HQD_ACTIVE__ACTIVE_MASK)) break; udelay(1); } if (i == adev->usec_timeout) - return -ETIMEDOUT; + r = -ETIMEDOUT; - WREG32(mmCP_HQD_DEQUEUE_REQUEST, 0); - WREG32(mmCP_HQD_PQ_RPTR, 0); - WREG32(mmCP_HQD_PQ_WPTR, 0); } - return 0; + WREG32(mmCP_HQD_DEQUEUE_REQUEST, 0); + WREG32(mmCP_HQD_PQ_RPTR, 0); + WREG32(mmCP_HQD_PQ_WPTR, 0); + + return r; } static void gfx_v7_0_mqd_init(struct amdgpu_device *adev, @@ -2965,31 +2947,42 @@ static int gfx_v7_0_mqd_commit(struct amdgpu_device *adev, struct cik_mqd *mqd) static int gfx_v7_0_compute_queue_init(struct amdgpu_device *adev, int ring_id) { - int r; - u64 mqd_gpu_addr; - struct cik_mqd *mqd; struct amdgpu_ring *ring = &adev->gfx.compute_ring[ring_id]; + struct cik_mqd *mqd = ring->mqd_ptr; + int mqd_idx = ring - &adev->gfx.compute_ring[0]; - r = amdgpu_bo_create_reserved(adev, sizeof(struct cik_mqd), PAGE_SIZE, - AMDGPU_GEM_DOMAIN_GTT, &ring->mqd_obj, - &mqd_gpu_addr, (void **)&mqd); - if (r) { - dev_warn(adev->dev, "(%d) create MQD bo failed\n", r); - return r; + if (!amdgpu_in_reset(adev) && !adev->in_suspend) { + memset((void *)mqd, 0, ring->mqd_size); + mutex_lock(&adev->srbm_mutex); + cik_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); + gfx_v7_0_mqd_init(adev, mqd, ring->mqd_gpu_addr, ring); + gfx_v7_0_deactivate_hqd(adev, 1); + gfx_v7_0_mqd_commit(adev, mqd); + cik_srbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + + if (adev->gfx.mec.mqd_backup[mqd_idx]) + memcpy(adev->gfx.mec.mqd_backup[mqd_idx], mqd, ring->mqd_size); + } else { + /* restore MQD to a clean status */ + if (adev->gfx.mec.mqd_backup[mqd_idx]) + memcpy(mqd, adev->gfx.mec.mqd_backup[mqd_idx], ring->mqd_size); + + /* Re-commit the restored backup */ + mutex_lock(&adev->srbm_mutex); + cik_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); + gfx_v7_0_deactivate_hqd(adev, 2); + gfx_v7_0_mqd_commit(adev, mqd); + cik_srbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + + /* reset ring buffer */ + ring->wptr = 0; + atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0); + atomic64_set((atomic64_t *)ring->rptr_cpu_addr, 0); + amdgpu_ring_clear_ring(ring); } - mutex_lock(&adev->srbm_mutex); - cik_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - - gfx_v7_0_mqd_init(adev, mqd, mqd_gpu_addr, ring); - gfx_v7_0_mqd_deactivate(adev); - gfx_v7_0_mqd_commit(adev, mqd); - - cik_srbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - - amdgpu_bo_kunmap(ring->mqd_obj); - amdgpu_bo_unreserve(ring->mqd_obj); return 0; } @@ -3021,10 +3014,8 @@ static int gfx_v7_0_cp_compute_resume(struct amdgpu_device *adev) /* init the queues */ for (i = 0; i < adev->gfx.num_compute_rings; i++) { r = gfx_v7_0_compute_queue_init(adev, i); - if (r) { - gfx_v7_0_cp_compute_fini(adev); + if (r) return r; - } } gfx_v7_0_cp_compute_enable(adev, true); @@ -4431,6 +4422,11 @@ static int gfx_v7_0_sw_init(struct amdgpu_ip_block *ip_block) } } + /* create MQD for all compute queues */ + r = amdgpu_gfx_mqd_sw_init(adev, sizeof(struct cik_mqd), 0); + if (r) + return r; + adev->gfx.ce_ram_size = 0x8000; gfx_v7_0_gpu_early_init(adev); @@ -4453,7 +4449,7 @@ static int gfx_v7_0_sw_fini(struct amdgpu_ip_block *ip_block) for (i = 0; i < adev->gfx.num_compute_rings; i++) amdgpu_ring_fini(&adev->gfx.compute_ring[i]); - gfx_v7_0_cp_compute_fini(adev); + amdgpu_gfx_mqd_sw_fini(adev, 0); amdgpu_gfx_rlc_fini(adev); gfx_v7_0_mec_fini(adev); amdgpu_bo_free_kernel(&adev->gfx.rlc.clear_state_obj, From c93962a688ca5c664e3f55f599ca926222492326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:16 +0200 Subject: [PATCH 06/13] drm/amdgpu/gfx7: Return error code when compute ring tests fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gfx_v7_0_cp_compute_resume() function should only return success when all compute rings are actually functional. This will be especially important for soft reset which needs this to know whether the reset was successful. Note that the gfx_v8_0_cp_test_all_rings() function already does this on GFX8, here we just follow the same idea. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index e2a07f9c8b48..b7d1e111c2e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -3020,12 +3020,14 @@ static int gfx_v7_0_cp_compute_resume(struct amdgpu_device *adev) gfx_v7_0_cp_compute_enable(adev, true); + r = 0; + for (i = 0; i < adev->gfx.num_compute_rings; i++) { ring = &adev->gfx.compute_ring[i]; - amdgpu_ring_test_helper(ring); + r |= amdgpu_ring_test_helper(ring); } - return 0; + return r; } static void gfx_v7_0_cp_enable(struct amdgpu_device *adev, bool enable) From 3c6eba1c14ec646d142add768ebf8eb51daa8442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:17 +0200 Subject: [PATCH 07/13] drm/amdgpu/gfx7: Return error code when failing to start GFX ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return an error code instead of silently failing. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index b7d1e111c2e3..5c1acb5bd05a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2577,7 +2577,10 @@ static int gfx_v7_0_cp_gfx_resume(struct amdgpu_device *adev) WREG32(mmCP_RB0_BASE_HI, upper_32_bits(rb_addr)); /* start the ring */ - gfx_v7_0_cp_gfx_start(adev); + r = gfx_v7_0_cp_gfx_start(adev); + if (r) + return r; + r = amdgpu_ring_test_helper(ring); if (r) return r; From 90163c8f3b73aaf2241583503ba40e58247e54ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:18 +0200 Subject: [PATCH 08/13] drm/amdgpu/gfx7: Fixup emitting SWITCH_BUFFER packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This packet is interpreted by the CE (constant engine). The reason why this packet is emitted is basically to make sure the CE can't start executing packets from the next job submission until the current one is finished. (Note that CE is not utilized by any maintained userspace driver and is discontinued in new GPUs. It is now also deprecated in the kernel.) Implement the emit_switch_buffer() function instead of emitting them duing emit_ib, emit_pipeline_sync and emit_vm_flush. It isn't necessary to emit these in both emit_pipeline_sync() and emit_vm_flush() because amdgpu_vm_flush() already calls these when calling either of those functions. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 7 +++++- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 32 ++++++++------------------ 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index aac8ace9d7a6..4c90e88e2e30 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -892,7 +892,12 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, amdgpu_ring_patch_cond_exec(ring, patch); - /* the double SWITCH_BUFFER here *cannot* be skipped by COND_EXEC */ + /* + * Sync CE with ME to prevent CE from fetching the next CE IB + * before the context switch is done. This is emitted before + * the first IB of a job submission after a context switch. + * The double SWITCH_BUFFER here *cannot* be skipped by COND_EXEC. + */ if (ring->funcs->emit_switch_buffer) { amdgpu_ring_emit_switch_buffer(ring); amdgpu_ring_emit_switch_buffer(ring); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 5c1acb5bd05a..dac520eb0553 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2202,12 +2202,6 @@ static void gfx_v7_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, unsigned vmid = AMDGPU_JOB_GET_VMID(job); u32 header, control = 0; - /* insert SWITCH_BUFFER packet before first IB in the ring frame */ - if (flags & AMDGPU_HAVE_CTX_SWITCH) { - amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); - amdgpu_ring_write(ring, 0); - } - if (ib->flags & AMDGPU_IB_FLAG_CE) header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2); else @@ -2259,6 +2253,12 @@ static void gfx_v7_0_ring_emit_ib_compute(struct amdgpu_ring *ring, amdgpu_ring_write(ring, control); } +static void gfx_v7_0_ring_emit_sb(struct amdgpu_ring *ring) +{ + amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + amdgpu_ring_write(ring, 0); +} + static void gfx_v7_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags) { uint32_t dw2 = 0; @@ -3112,14 +3112,6 @@ static void gfx_v7_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring) amdgpu_ring_write(ring, seq); amdgpu_ring_write(ring, 0xffffffff); amdgpu_ring_write(ring, 4); /* poll interval */ - - if (usepfp) { - /* sync CE with ME to prevent CE fetch CEIB before context switch done */ - amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); - amdgpu_ring_write(ring, 0); - amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); - amdgpu_ring_write(ring, 0); - } } /* @@ -3161,12 +3153,6 @@ static void gfx_v7_0_ring_emit_vm_flush(struct amdgpu_ring *ring, /* sync PFP to ME, otherwise we might get invalid PFP reads */ amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); amdgpu_ring_write(ring, 0x0); - - /* synce CE with ME to prevent CE fetch CEIB before context switch done */ - amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); - amdgpu_ring_write(ring, 0); - amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); - amdgpu_ring_write(ring, 0); } } @@ -4955,8 +4941,9 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_gfx = { 7 + /* gfx_v7_0_ring_emit_hdp_flush */ 5 + /* hdp invalidate */ 12 + 12 + 12 + /* gfx_v7_0_ring_emit_fence_gfx x3 for user fence, vm fence */ - 7 + 4 + /* gfx_v7_0_ring_emit_pipeline_sync */ - CIK_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 6 + /* gfx_v7_0_ring_emit_vm_flush */ + 7 + /* gfx_v7_0_ring_emit_pipeline_sync */ + CIK_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v7_0_ring_emit_vm_flush */ + 3 * 2 + /* gfx_v7_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */ 3 + 4 + /* gfx_v7_ring_emit_cntxcntl including vgt flush*/ 5, /* SURFACE_SYNC */ .emit_ib_size = 4, /* gfx_v7_0_ring_emit_ib_gfx */ @@ -4970,6 +4957,7 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_gfx = { .test_ib = gfx_v7_0_ring_test_ib, .insert_nop = amdgpu_ring_insert_nop, .pad_ib = amdgpu_ring_generic_pad_ib, + .emit_switch_buffer = gfx_v7_0_ring_emit_sb, .emit_cntxcntl = gfx_v7_ring_emit_cntxcntl, .emit_wreg = gfx_v7_0_ring_emit_wreg, .soft_recovery = gfx_v7_0_ring_soft_recovery, From e97157ebab19151728be59f8d0da9036104cc79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:19 +0200 Subject: [PATCH 09/13] drm/amdgpu/gfx7: Clean up gfx ring during reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear the WPTR and RPTR at ring initialization. Additionally clear the ring contents during reset. After a reset, the ring contents could be "dirty" and contain packets emitted before the reset. and thus need to be cleared to prevent the command processor from executing packets left over in the ring from before the reset. Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index dac520eb0553..7a3b7272d093 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2547,8 +2547,12 @@ static int gfx_v7_0_cp_gfx_resume(struct amdgpu_device *adev) WREG32(mmSCRATCH_ADDR, 0); /* ring 0 - compute and gfx */ - /* Set ring buffer size */ ring = &adev->gfx.gfx_ring[0]; + *ring->wptr_cpu_addr = 0; + *ring->rptr_cpu_addr = 0; + amdgpu_ring_clear_ring(ring); + + /* Set ring buffer size */ rb_bufsz = order_base_2(ring->ring_size / 8); tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; #ifdef __BIG_ENDIAN @@ -2560,6 +2564,7 @@ static int gfx_v7_0_cp_gfx_resume(struct amdgpu_device *adev) WREG32(mmCP_RB0_CNTL, tmp | CP_RB0_CNTL__RB_RPTR_WR_ENA_MASK); ring->wptr = 0; WREG32(mmCP_RB0_WPTR, lower_32_bits(ring->wptr)); + WREG32(mmCP_RB0_RPTR, lower_32_bits(ring->wptr)); /* set the wb address whether it's enabled or not */ rptr_addr = ring->rptr_gpu_addr; From ba004e4536f9091b0c0a326cdd51d3eb84f34a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:20 +0200 Subject: [PATCH 10/13] drm/amdgpu/gfx7: Use COND_EXEC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit COND_EXEC tells the CP to discard the dwords following it when its condition is zero (false). This is useful for GPU recovery because it can help reduce collateral damage during GFX IP block soft reset, meaning that it reduces the likelyhood that we fail some jobs which are not guilty of the hang as the IP block soft reset mechanism clears the condition before doing the reset. Signed-off-by: Timur Kristóf Reviewed-by: Tvrtko Ursulin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 7a3b7272d093..b7dc019fee3a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -3161,6 +3161,22 @@ static void gfx_v7_0_ring_emit_vm_flush(struct amdgpu_ring *ring, } } +static unsigned int gfx_v7_0_ring_emit_init_cond_exec(struct amdgpu_ring *ring, + uint64_t gpu_addr) +{ + unsigned int ret; + + /* Discard following DWs after this packet when gpu_addr==0 */ + amdgpu_ring_write(ring, PACKET3(PACKET3_COND_EXEC, 3)); + amdgpu_ring_write(ring, lower_32_bits(gpu_addr)); + amdgpu_ring_write(ring, upper_32_bits(gpu_addr)); + amdgpu_ring_write(ring, 0); + ret = ring->wptr & ring->buf_mask; + /* patch dummy value later */ + amdgpu_ring_write(ring, 0); + return ret; +} + static void gfx_v7_0_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, uint32_t val) { @@ -4942,6 +4958,8 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_gfx = { .get_wptr = gfx_v7_0_ring_get_wptr_gfx, .set_wptr = gfx_v7_0_ring_set_wptr_gfx, .emit_frame_size = + 5 + /* gfx_v7_0_ring_emit_init_cond_exec (from amdgpu_ib_schedule) */ + 5 + /* gfx_v7_0_ring_emit_init_cond_exec (from amdgpu_vm_flush) */ 20 + /* gfx_v7_0_ring_emit_gds_switch */ 7 + /* gfx_v7_0_ring_emit_hdp_flush */ 5 + /* hdp invalidate */ @@ -4964,6 +4982,7 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_gfx = { .pad_ib = amdgpu_ring_generic_pad_ib, .emit_switch_buffer = gfx_v7_0_ring_emit_sb, .emit_cntxcntl = gfx_v7_ring_emit_cntxcntl, + .init_cond_exec = gfx_v7_0_ring_emit_init_cond_exec, .emit_wreg = gfx_v7_0_ring_emit_wreg, .soft_recovery = gfx_v7_0_ring_soft_recovery, .emit_mem_sync = gfx_v7_0_emit_mem_sync, @@ -4978,6 +4997,8 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_compute = { .get_wptr = gfx_v7_0_ring_get_wptr_compute, .set_wptr = gfx_v7_0_ring_set_wptr_compute, .emit_frame_size = + 5 + /* gfx_v7_0_ring_emit_init_cond_exec (from amdgpu_ib_schedule) */ + 5 + /* gfx_v7_0_ring_emit_init_cond_exec (from amdgpu_vm_flush) */ 20 + /* gfx_v7_0_ring_emit_gds_switch */ 7 + /* gfx_v7_0_ring_emit_hdp_flush */ 5 + /* hdp invalidate */ @@ -4996,6 +5017,7 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_compute = { .test_ib = gfx_v7_0_ring_test_ib, .insert_nop = amdgpu_ring_insert_nop, .pad_ib = amdgpu_ring_generic_pad_ib, + .init_cond_exec = gfx_v7_0_ring_emit_init_cond_exec, .emit_wreg = gfx_v7_0_ring_emit_wreg, .soft_recovery = gfx_v7_0_ring_soft_recovery, .emit_mem_sync = gfx_v7_0_emit_mem_sync_compute, From 9fb7ee45d69133bae634da9aee6f39be58ac9f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:21 +0200 Subject: [PATCH 11/13] drm/amdgpu/gfx7: Fixup IP block soft reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use basically the same implementation as GFX8, except for the GFX7 specific MQD functions. Reset every block using the GRBM, then proceed to reset the GRBM and SEM blocks using the SRBM. Remove the redundant gfx_v7_0_update_cg() function. The soft reset now calls the clock and powergating functions of the IP block instead. Signed-off-by: Timur Kristóf Reviewed-by: Tvrtko Ursulin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 164 +++++++++++++------------- 1 file changed, 84 insertions(+), 80 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index b7dc019fee3a..a057bba5c3a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -3617,21 +3617,6 @@ static void gfx_v7_0_enable_mgcg(struct amdgpu_device *adev, bool enable) } } -static void gfx_v7_0_update_cg(struct amdgpu_device *adev, - bool enable) -{ - gfx_v7_0_enable_gui_idle_interrupt(adev, false); - /* order matters! */ - if (enable) { - gfx_v7_0_enable_mgcg(adev, true); - gfx_v7_0_enable_cgcg(adev, true); - } else { - gfx_v7_0_enable_cgcg(adev, false); - gfx_v7_0_enable_mgcg(adev, false); - } - gfx_v7_0_enable_gui_idle_interrupt(adev, true); -} - static void gfx_v7_0_enable_sclk_slowdown_on_pu(struct amdgpu_device *adev, bool enable) { @@ -4550,80 +4535,99 @@ static int gfx_v7_0_wait_for_idle(struct amdgpu_ip_block *ip_block) static int gfx_v7_0_soft_reset(struct amdgpu_ip_block *ip_block) { + struct amdgpu_device *adev = ip_block->adev; u32 grbm_soft_reset = 0, srbm_soft_reset = 0; u32 tmp; - struct amdgpu_device *adev = ip_block->adev; + int i; + int r; - /* GRBM_STATUS */ - tmp = RREG32(mmGRBM_STATUS); - if (tmp & (GRBM_STATUS__PA_BUSY_MASK | GRBM_STATUS__SC_BUSY_MASK | - GRBM_STATUS__BCI_BUSY_MASK | GRBM_STATUS__SX_BUSY_MASK | - GRBM_STATUS__TA_BUSY_MASK | GRBM_STATUS__VGT_BUSY_MASK | - GRBM_STATUS__DB_BUSY_MASK | GRBM_STATUS__CB_BUSY_MASK | - GRBM_STATUS__GDS_BUSY_MASK | GRBM_STATUS__SPI_BUSY_MASK | - GRBM_STATUS__IA_BUSY_MASK | GRBM_STATUS__IA_BUSY_NO_DMA_MASK)) - grbm_soft_reset |= GRBM_SOFT_RESET__SOFT_RESET_CP_MASK | - GRBM_SOFT_RESET__SOFT_RESET_GFX_MASK; + grbm_soft_reset = + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_RLC, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_GFX, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CP, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPF, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPC, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPG, 1); - if (tmp & (GRBM_STATUS__CP_BUSY_MASK | GRBM_STATUS__CP_COHERENCY_BUSY_MASK)) { - grbm_soft_reset |= GRBM_SOFT_RESET__SOFT_RESET_CP_MASK; - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_GRBM_MASK; - } + srbm_soft_reset = + REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1) | + REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_SEM, 1); - /* GRBM_STATUS2 */ - tmp = RREG32(mmGRBM_STATUS2); - if (tmp & GRBM_STATUS2__RLC_BUSY_MASK) - grbm_soft_reset |= GRBM_SOFT_RESET__SOFT_RESET_RLC_MASK; + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; - /* SRBM_STATUS */ - tmp = RREG32(mmSRBM_STATUS); - if (tmp & SRBM_STATUS__GRBM_RQ_PENDING_MASK) - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_GRBM_MASK; + mutex_lock(&adev->srbm_mutex); + cik_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); + gfx_v7_0_deactivate_hqd(adev, 2); + cik_srbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); - if (grbm_soft_reset || srbm_soft_reset) { - /* disable CG/PG */ - gfx_v7_0_fini_pg(adev); - gfx_v7_0_update_cg(adev, false); - - /* stop the rlc */ - adev->gfx.rlc.funcs->stop(adev); - - /* Disable GFX parsing/prefetching */ - WREG32(mmCP_ME_CNTL, CP_ME_CNTL__ME_HALT_MASK | CP_ME_CNTL__PFP_HALT_MASK | CP_ME_CNTL__CE_HALT_MASK); - - /* Disable MEC parsing/prefetching */ - WREG32(mmCP_MEC_CNTL, CP_MEC_CNTL__MEC_ME1_HALT_MASK | CP_MEC_CNTL__MEC_ME2_HALT_MASK); - - if (grbm_soft_reset) { - tmp = RREG32(mmGRBM_SOFT_RESET); - tmp |= grbm_soft_reset; - dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32(mmGRBM_SOFT_RESET, tmp); - tmp = RREG32(mmGRBM_SOFT_RESET); - - udelay(50); - - tmp &= ~grbm_soft_reset; - WREG32(mmGRBM_SOFT_RESET, tmp); - tmp = RREG32(mmGRBM_SOFT_RESET); - } - - if (srbm_soft_reset) { - tmp = RREG32(mmSRBM_SOFT_RESET); - tmp |= srbm_soft_reset; - dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - udelay(50); - - tmp &= ~srbm_soft_reset; - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - } - /* Wait a little for things to settle down */ udelay(50); } + + ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_UNGATE); + ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_UNGATE); + ip_block->version->funcs->suspend(ip_block); + + if (grbm_soft_reset || srbm_soft_reset) { + tmp = RREG32(mmGMCON_DEBUG); + tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 1); + tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 1); + WREG32(mmGMCON_DEBUG, tmp); + + udelay(100); + } + + if (grbm_soft_reset) { + tmp = RREG32(mmGRBM_SOFT_RESET); + tmp |= grbm_soft_reset; + dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(mmGRBM_SOFT_RESET, tmp); + tmp = RREG32(mmGRBM_SOFT_RESET); + + udelay(100); + + tmp &= ~grbm_soft_reset; + WREG32(mmGRBM_SOFT_RESET, tmp); + tmp = RREG32(mmGRBM_SOFT_RESET); + + udelay(100); + } + + if (srbm_soft_reset) { + tmp = RREG32(mmSRBM_SOFT_RESET); + tmp |= srbm_soft_reset; + dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(mmSRBM_SOFT_RESET, tmp); + tmp = RREG32(mmSRBM_SOFT_RESET); + + udelay(100); + + tmp &= ~srbm_soft_reset; + WREG32(mmSRBM_SOFT_RESET, tmp); + tmp = RREG32(mmSRBM_SOFT_RESET); + + udelay(100); + } + + if (grbm_soft_reset || srbm_soft_reset) { + tmp = RREG32(mmGMCON_DEBUG); + tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 0); + tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 0); + WREG32(mmGMCON_DEBUG, tmp); + } + + /* Wait a little for things to settle down */ + udelay(100); + + r = ip_block->version->funcs->resume(ip_block); + r |= ip_block->version->funcs->late_init(ip_block); + if (r) + return r; + + ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_GATE); + ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_GATE); + return 0; } From d8b517d3749e77adf2493ccd579b327a97ea13ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 21 Jul 2026 13:43:22 +0200 Subject: [PATCH 12/13] drm/amdgpu/gfx7: Enable IP block soft reset as a GPU recovery method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable IP block soft reset as a GPU recovery method for GFX7 graphics and compute rings. This improves current user experience on all GFX7 chips: * On Kaveri and Kabini there is currently no working GPU recovery method so those chips currently require the user to manually reset the computer when there was a hang. * On Hawaii and Bonaire, the current GPU recovery method always clears the contents of VRAM, which means that a buggy (hanging) app can crash the whole graphical session, which is less than ideal. Using GFX IP block soft reset means that we can now have a working recovery on GFX7 APUs and we can also move on from GFX hangs on dGPUs without crashing the whole system. Tested with the "hard_reset_cp_wait" test case from the Hang Test Suite created by Natalie Vock and Konstantin Seurer. This Vulkan testcase waits for an event that never occurs, effectively a WAIT_REG_MEM packet that intentionally hangs. IP block soft reset can resolve that hang and allow the rest of the system to move on and keep functioning without needing a full ASIC reset. Tested on the following chips: Bonaire (Radeon HD 7790) Hawaii (Radeon R9 390X) Kaveri (A10-7850K) Signed-off-by: Timur Kristóf Reviewed-by: Tvrtko Ursulin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index a057bba5c3a1..5fc19fefca81 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -4433,6 +4433,11 @@ static int gfx_v7_0_sw_init(struct amdgpu_ip_block *ip_block) adev->gfx.compute_supported_reset = amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]); + if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_ip_block_soft_reset) { + adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET; + adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET; + } + return r; } From 9243cf4777fc780157602603caa52c285908f26a Mon Sep 17 00:00:00 2001 From: Ulisses Paixao Date: Wed, 29 Jul 2026 10:22:26 -0400 Subject: [PATCH 13/13] drm/amd/amdgpu: remove duplicated code in gfx_v11 and gfx_v12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions gfx_v11_0_handle_priv_fault and gfx_v12_0_handle_priv_fault share the same logic for searching and triggering a scheduler fault on a ring. This patch moves the shared ring-searching logic to a common function, amdgpu_gfx_handle_priv_fault, in amdgpu_gfx.c. The hardware-specific decoding of ring IDs remains in the version-specific files to maintain proper architectural separation. Signed-off-by: Ulisses Paixao Co-developed-by: Felipe Sousa Signed-off-by: Felipe Sousa Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 54 +++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h | 3 ++ drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 48 +++------------------- drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c | 49 +++------------------- 4 files changed, 67 insertions(+), 87 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index d763f0baf0e8..4d21d83451a4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -34,6 +34,7 @@ #include "amdgpu_xcp.h" #include "amdgpu_xgmi.h" #include "amdgpu_mes.h" +#include "amdgpu_userq.h" #include "mes_userqueue.h" #include "nvd.h" @@ -855,6 +856,59 @@ int amdgpu_gfx_enable_kgq(struct amdgpu_device *adev, int xcc_id) return r; } +/** + * amdgpu_gfx_handle_priv_fault - Handle privileged instruction fault + * + * @adev: amdgpu_device pointer + * @entry: interrupt vector entry containing fault information + * @me_id: micro-engine ID of the faulty ring + * @pipe_id: pipe ID of the faulty ring + * @queue_id: queue ID of the faulty ring + * + * This function handles privileged instruction faults by identifying + * the faulty ring (gfx or compute) and triggering a scheduler fault + */ +void amdgpu_gfx_handle_priv_fault(struct amdgpu_device *adev, + struct amdgpu_iv_entry *entry, + u8 me_id, u8 pipe_id, u8 queue_id) +{ + struct amdgpu_ring *ring; + u32 doorbell_offset; + int i; + + /* + * Try KQ first by ring_id (HW slot is authoritative). The + * KMD compute_hqd_mask contract guarantees KCQ and user queues + * never share a HW slot. + */ + if (!adev->gfx.disable_kq) { + for (i = 0; i < adev->gfx.num_gfx_rings; i++) { + ring = &adev->gfx.gfx_ring[i]; + if (ring->me == me_id && ring->pipe == pipe_id && + ring->queue == queue_id) { + drm_sched_fault(&ring->sched); + return; + } + } + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + if (ring->me == me_id && ring->pipe == pipe_id && + ring->queue == queue_id) { + drm_sched_fault(&ring->sched); + return; + } + } + } + + doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK; + + /* No KQ matched: HW slot is a MES-scheduled user queue. */ + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_reset_irq(adev, entry->pasid, + doorbell_offset); +} + static void amdgpu_gfx_do_off_ctrl(struct amdgpu_device *adev, bool enable, bool no_delay) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index aefd4f03b443..c15f45a7347c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -620,6 +620,9 @@ bool amdgpu_gfx_is_high_priority_graphics_queue(struct amdgpu_device *adev, struct amdgpu_ring *ring); bool amdgpu_gfx_is_me_queue_enabled(struct amdgpu_device *adev, int me, int pipe, int queue); +void amdgpu_gfx_handle_priv_fault(struct amdgpu_device *adev, + struct amdgpu_iv_entry *entry, + u8 me_id, u8 pipe_id, u8 queue_id); void amdgpu_gfx_off_ctrl(struct amdgpu_device *adev, bool enable); void amdgpu_gfx_off_ctrl_immediate(struct amdgpu_device *adev, bool enable); int amdgpu_get_gfx_off_status(struct amdgpu_device *adev, uint32_t *value); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 3b203961abb9..9faf31421a15 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -6714,51 +6714,13 @@ static int gfx_v11_0_set_priv_inst_fault_state(struct amdgpu_device *adev, static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { - u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK; + u8 me_id, pipe_id, queue_id; - /* - * Try KQ first by ring_id (HW slot is authoritative). The - * KMD compute_hqd_mask contract guarantees KCQ and user queues - * never share a HW slot. - */ - if (!adev->gfx.disable_kq) { - u8 me_id = (entry->ring_id & 0x0c) >> 2; - u8 pipe_id = (entry->ring_id & 0x03) >> 0; - u8 queue_id = (entry->ring_id & 0x70) >> 4; - struct amdgpu_ring *ring; - int i; + me_id = (entry->ring_id & 0x0c) >> 2; + pipe_id = (entry->ring_id & 0x03) >> 0; + queue_id = (entry->ring_id & 0x70) >> 4; - switch (me_id) { - case 0: - for (i = 0; i < adev->gfx.num_gfx_rings; i++) { - ring = &adev->gfx.gfx_ring[i]; - if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) { - drm_sched_fault(&ring->sched); - return; - } - } - break; - case 1: - case 2: - for (i = 0; i < adev->gfx.num_compute_rings; i++) { - ring = &adev->gfx.compute_ring[i]; - if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) { - drm_sched_fault(&ring->sched); - return; - } - } - break; - default: - break; - } - } - - /* No KQ matched: HW slot is a MES-scheduled user queue. */ - if (adev->enable_mes && doorbell_offset) - amdgpu_userq_process_reset_irq(adev, entry->pasid, - doorbell_offset); + amdgpu_gfx_handle_priv_fault(adev, entry, me_id, pipe_id, queue_id); } static int gfx_v11_0_priv_reg_irq(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index 5f0a24951f4a..45a47078f876 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -5045,52 +5045,13 @@ static int gfx_v12_0_set_priv_inst_fault_state(struct amdgpu_device *adev, static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { - u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK; + u8 me_id, pipe_id, queue_id; - /* - * Try KQ first by ring_id; UQ as fallback. KCQ and UQ never share - * a HW slot (compute_hqd_mask contract). - */ - if (!adev->gfx.disable_kq) { - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; + me_id = (entry->ring_id & 0x0c) >> 2; + pipe_id = (entry->ring_id & 0x03) >> 0; + queue_id = (entry->ring_id & 0x70) >> 4; - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; - - switch (me_id) { - case 0: - for (i = 0; i < adev->gfx.num_gfx_rings; i++) { - ring = &adev->gfx.gfx_ring[i]; - if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) { - drm_sched_fault(&ring->sched); - return; - } - } - break; - case 1: - case 2: - for (i = 0; i < adev->gfx.num_compute_rings; i++) { - ring = &adev->gfx.compute_ring[i]; - if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) { - drm_sched_fault(&ring->sched); - return; - } - } - break; - default: - break; - } - } - - /* No KQ matched: HW slot is a MES-scheduled user queue. */ - if (adev->enable_mes && doorbell_offset) - amdgpu_userq_process_reset_irq(adev, entry->pasid, - doorbell_offset); + amdgpu_gfx_handle_priv_fault(adev, entry, me_id, pipe_id, queue_id); } static int gfx_v12_0_priv_reg_irq(struct amdgpu_device *adev,