Re: [PATCH v2 2/2] drm/amdkfd: Unmap svm range from GPU set to no-access
Felix Kuehling <[email protected]> Mon, 27 Jul 2026 17:49:40 -0400
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Organization | AMD Inc. |
| Message-ID | <[email protected]> |
On 2026-07-27 15:39, Philip Yang wrote: > When KFD_IOCTL_SVM_ATTR_NO_ACCESS is applied to a GPU that has an SVM > range mapped, unmap the range from that GPU. Once no GPU maps the range, > the MMU notifier can skip queue eviction on CPU page faults. > > Replace the mapped_to_gpu boolean with bitmap_mapped to track which > GPUs currently have the range mapped. Set bits in svm_range_map_to_gpus() > and clear them in svm_range_unmap_from_gpus(). This is separate from > bitmap_access/bitmap_aip which track user-requested attributes and must > not be used to determine mapping state. > > Add bitmap_needs_unmap to svm_range, set for each GPU given no-access. > Add svm_range_needs_unmap() to unmap the range from those GPUs when the > app sets the no-access attribute. > > Bump the checkpoint timestamp on unmap so retry faults queued before the > no-access unmap are dropped instead of restoring the mapping. > > v4: > - Rename and set prange->mapping_done to false if validate and map not > complete successfully (Felix) > v3: > - Correct error handling, support app retry update mapping (Felix) > v2: > - Add bitmap_mapped to not break get_attr (Felix) > > Signed-off-by: Philip Yang <[email protected]> > --- > drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 5 +- > drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 107 ++++++++++++++++--------- > drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 7 +- > 3 files changed, 77 insertions(+), 42 deletions(-) > > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c > index b249e7d1af48..25954c2c2d91 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c > @@ -112,12 +112,11 @@ static int kfd_queue_buffer_svm_get(struct kfd_process_device *pdd, u64 addr, u6 > if (!prange) > break; > > - if (!prange->mapped_to_gpu) > - break; > - > r = kfd_process_gpuid_from_node(p, pdd->dev, &gpuid, &gpuidx); > if (r < 0) > break; > + if (!test_bit(gpuidx, prange->bitmap_mapped)) > + break; > if (!test_bit(gpuidx, prange->bitmap_access) && > !test_bit(gpuidx, prange->bitmap_aip)) > break; > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > index c10d4edc8813..1a01d089b132 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > @@ -826,8 +826,13 @@ svm_range_apply_attrs(struct kfd_process *p, struct svm_range *prange, > gpuidx = kfd_process_gpuidx_from_gpuid(p, > attrs[i].value); > if (attrs[i].type == KFD_IOCTL_SVM_ATTR_NO_ACCESS) { > + svm_range_update_checkpoint_timestamp(p); In the last version I suggested moving this into svm_range_needs_unmap so we only update the checkpoint (and drop interrupts) when anything is actually unmapped (after the bitmap_empty check for bitmap_needs_unmap). Did that not work for some reason? Regards, Felix > + > bitmap_clear(prange->bitmap_access, gpuidx, 1); > bitmap_clear(prange->bitmap_aip, gpuidx, 1); > + > + if (test_bit(gpuidx, prange->bitmap_mapped)) > + bitmap_set(prange->bitmap_needs_unmap, gpuidx, 1); > } else if (attrs[i].type == KFD_IOCTL_SVM_ATTR_ACCESS) { > bitmap_set(prange->bitmap_access, gpuidx, 1); > bitmap_clear(prange->bitmap_aip, gpuidx, 1); > @@ -1118,9 +1123,10 @@ svm_range_split_adjust(struct svm_range *new, struct svm_range *old, > new->prefetch_loc = old->prefetch_loc; > new->actual_loc = old->actual_loc; > new->granularity = old->granularity; > - new->mapped_to_gpu = old->mapped_to_gpu; > + new->mapping_done = old->mapping_done; > bitmap_copy(new->bitmap_access, old->bitmap_access, MAX_GPU_INSTANCE); > bitmap_copy(new->bitmap_aip, old->bitmap_aip, MAX_GPU_INSTANCE); > + bitmap_copy(new->bitmap_mapped, old->bitmap_mapped, MAX_GPU_INSTANCE); > atomic_set(&new->queue_refcount, atomic_read(&old->queue_refcount)); > > return 0; > @@ -1421,7 +1427,8 @@ svm_range_unmap_from_gpu(struct amdgpu_device *adev, struct amdgpu_vm *vm, > > static int > svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start, > - unsigned long last, uint32_t trigger) > + unsigned long last, unsigned long *bitmap_unmap, > + uint32_t trigger) > { > struct kfd_process_device *pdd; > struct dma_fence *fence = NULL; > @@ -1429,21 +1436,15 @@ svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start, > uint32_t gpuidx; > int r = 0; > > - if (!prange->mapped_to_gpu) { > - pr_debug("prange 0x%p [0x%lx 0x%lx] not mapped to GPU\n", > - prange, prange->start, prange->last); > - return 0; > - } > - > - if (prange->start == start && prange->last == last) { > - pr_debug("unmap svms 0x%p prange 0x%p\n", prange->svms, prange); > - prange->mapped_to_gpu = false; > - } > - > p = container_of(prange->svms, struct kfd_process, svms); > > - for_each_or_bit(gpuidx, prange->bitmap_access, prange->bitmap_aip, MAX_GPU_INSTANCE) { > - pr_debug("unmap from gpu idx 0x%x\n", gpuidx); > + for_each_set_bit(gpuidx, bitmap_unmap, MAX_GPU_INSTANCE) { > + if (prange->start == start && prange->last == last) { > + pr_debug("unmap svms 0x%p prange 0x%p from gpu_idx 0x%x\n", > + prange->svms, prange, gpuidx); > + clear_bit(gpuidx, prange->bitmap_mapped); > + } > + > pdd = kfd_process_device_from_gpuidx(p, gpuidx); > if (!pdd) { > pr_debug("failed to find device idx %d\n", gpuidx); > @@ -1596,6 +1597,8 @@ svm_range_map_to_gpus(struct svm_range *prange, unsigned long offset, > continue; > } > > + set_bit(gpuidx, prange->bitmap_mapped); > + > r = svm_range_map_to_gpu(pdd, prange, offset, npages, readonly, > prange->dma_addr[gpuidx], > bo_adev, wait ? &fence : NULL, > @@ -1741,7 +1744,9 @@ static int svm_range_validate_and_map(struct mm_struct *mm, > bitmap_zero(ctx->bitmap, MAX_GPU_INSTANCE); > bitmap_set(ctx->bitmap, gpuidx, 1); > } else if (ctx->process->xnack_enabled) { > - bitmap_copy(ctx->bitmap, prange->bitmap_aip, MAX_GPU_INSTANCE); > + /* Update mapping on already mapped or access in place GPU */ > + bitmap_or(ctx->bitmap, prange->bitmap_mapped, prange->bitmap_aip, > + MAX_GPU_INSTANCE); > > /* If prefetch range to GPU, or GPU retry fault migrate range to > * GPU, which has ACCESS attribute to the range, create mapping > @@ -1761,14 +1766,12 @@ static int svm_range_validate_and_map(struct mm_struct *mm, > } > > /* > - * If prange is already mapped or with always mapped flag, > - * update mapping on GPUs with ACCESS attribute > + * If prange with always mapped flag, update mapping on GPUs with > + * ACCESS attribute > */ > - if (bitmap_empty(ctx->bitmap, MAX_GPU_INSTANCE)) { > - if (prange->mapped_to_gpu || > - prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED) > - bitmap_copy(ctx->bitmap, prange->bitmap_access, MAX_GPU_INSTANCE); > - } > + if (prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED) > + bitmap_or(ctx->bitmap, ctx->bitmap, prange->bitmap_access, > + MAX_GPU_INSTANCE); > } else { > bitmap_or(ctx->bitmap, prange->bitmap_access, > prange->bitmap_aip, MAX_GPU_INSTANCE); > @@ -1834,6 +1837,7 @@ static int svm_range_validate_and_map(struct mm_struct *mm, > e = min(end, prange->last); > if (e >= s) > r = svm_range_unmap_from_gpus(prange, s, e, > + prange->bitmap_mapped, > KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU); > svm_range_unlock(prange); > /* If unmap returns non-zero, we'll bail on the next for loop > @@ -1896,7 +1900,9 @@ static int svm_range_validate_and_map(struct mm_struct *mm, > } > > if (!r && next == end) > - prange->mapped_to_gpu = true; > + prange->mapping_done = true; > + else > + prange->mapping_done = false; > > svm_range_unlock(prange); > > @@ -2066,10 +2072,10 @@ svm_range_evict(struct svm_range *prange, struct mm_struct *mm, > if (!p->xnack_enabled || > (prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)) { > int evicted_ranges; > - bool mapped = prange->mapped_to_gpu; > + bool mapped = !bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE); > > list_for_each_entry(pchild, &prange->child_list, child_list) { > - if (!pchild->mapped_to_gpu) > + if (bitmap_empty(pchild->bitmap_mapped, MAX_GPU_INSTANCE)) > continue; > mapped = true; > mutex_lock_nested(&pchild->lock, 1); > @@ -2118,13 +2124,14 @@ svm_range_evict(struct svm_range *prange, struct mm_struct *mm, > s = max(start, pchild->start); > l = min(last, pchild->last); > if (l >= s) > - svm_range_unmap_from_gpus(pchild, s, l, trigger); > + svm_range_unmap_from_gpus(pchild, s, l, prange->bitmap_mapped, > + trigger); > mutex_unlock(&pchild->lock); > } > s = max(start, prange->start); > l = min(last, prange->last); > if (l >= s) > - svm_range_unmap_from_gpus(prange, s, l, trigger); > + svm_range_unmap_from_gpus(prange, s, l, prange->bitmap_mapped, trigger); > } > > return r; > @@ -2154,10 +2161,11 @@ static struct svm_range *svm_range_clone(struct svm_range *old) > new->prefetch_loc = old->prefetch_loc; > new->actual_loc = old->actual_loc; > new->granularity = old->granularity; > - new->mapped_to_gpu = old->mapped_to_gpu; > + new->mapping_done = old->mapping_done; > new->vram_pages = old->vram_pages; > bitmap_copy(new->bitmap_access, old->bitmap_access, MAX_GPU_INSTANCE); > bitmap_copy(new->bitmap_aip, old->bitmap_aip, MAX_GPU_INSTANCE); > + bitmap_copy(new->bitmap_mapped, old->bitmap_mapped, MAX_GPU_INSTANCE); > atomic_set(&new->queue_refcount, atomic_read(&old->queue_refcount)); > > return new; > @@ -2277,7 +2285,7 @@ svm_range_add(struct kfd_process *p, uint64_t start, uint64_t size, > next_start = min(node->last, last) + 1; > > if (svm_range_is_same_attrs(p, prange, nattr, attrs) && > - prange->mapped_to_gpu) { > + prange->mapping_done) { > /* nothing to do */ > } else if (node->start < start || node->last > last) { > /* node intersects the update range and its attributes > @@ -2626,14 +2634,14 @@ svm_range_unmap_from_cpu(struct mm_struct *mm, struct svm_range *prange, > s = max(start, pchild->start); > l = min(last, pchild->last); > if (l >= s) > - svm_range_unmap_from_gpus(pchild, s, l, trigger); > + svm_range_unmap_from_gpus(pchild, s, l, prange->bitmap_mapped, trigger); > svm_range_unmap_split(prange, pchild, start, last); > mutex_unlock(&pchild->lock); > } > s = max(start, prange->start); > l = min(last, prange->last); > if (l >= s) > - svm_range_unmap_from_gpus(prange, s, l, trigger); > + svm_range_unmap_from_gpus(prange, s, l, prange->bitmap_mapped, trigger); > svm_range_unmap_split(prange, prange, start, last); > > if (unmap_parent) > @@ -3723,6 +3731,23 @@ static void svm_range_evict_svm_bo_worker(struct work_struct *work) > svm_range_bo_unref(svm_bo); > } > > +static bool svm_range_needs_unmap(struct kfd_process *p, struct svm_range *prange) > +{ > + if (bitmap_empty(prange->bitmap_needs_unmap, MAX_GPU_INSTANCE)) > + return false; > + > + pr_debug("prange 0x%p no access set for [0x%lx 0x%lx]\n", > + prange, prange->start, prange->last); > + > + svm_range_unmap_from_gpus(prange, prange->start, > + prange->last, prange->bitmap_needs_unmap, > + KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU); > + > + bitmap_clear(prange->bitmap_needs_unmap, 0, MAX_GPU_INSTANCE); > + > + return bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE); > +} > + > static int > svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, > uint64_t start, uint64_t size, uint32_t nattr, > @@ -3778,10 +3803,10 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, > svm_range_add_to_svms(prange); > svm_range_add_notifier_locked(mm, prange); > } > - list_for_each_entry(prange, &update_list, update_list) { > + > + list_for_each_entry(prange, &update_list, update_list) > svm_range_apply_attrs(p, prange, nattr, attrs, &update_mapping); > - /* TODO: unmap ranges from GPU that lost access */ > - } > + > update_mapping |= !p->xnack_enabled && !list_empty(&remap_list); > > list_for_each_entry_safe(prange, next, &remove_list, update_list) { > @@ -3802,6 +3827,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, > list_for_each_entry(prange, &update_list, update_list) { > bool migrated; > > + if (svm_range_needs_unmap(p, prange)) > + continue; > + > mutex_lock(&prange->migrate_mutex); > > r = svm_range_trigger_migration(mm, prange, &migrated); > @@ -3810,7 +3838,7 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, > > if (migrated && (!p->xnack_enabled || > (prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)) && > - prange->mapped_to_gpu) { > + !bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE)) { > pr_debug("restore_work will update mappings of GPUs\n"); > mutex_unlock(&prange->migrate_mutex); > continue; > @@ -3821,7 +3849,8 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, > continue; > } > > - flush_tlb = !migrated && update_mapping && prange->mapped_to_gpu; > + flush_tlb = !migrated && update_mapping && > + !bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE); > > r = svm_range_validate_and_map(mm, prange->start, prange->last, prange, > MAX_GPU_INSTANCE, true, true, flush_tlb); > @@ -3835,11 +3864,13 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, > } > > list_for_each_entry(prange, &remap_list, update_list) { > + flush_tlb = !bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE); > + > pr_debug("Remapping prange 0x%p [0x%lx 0x%lx]\n", > prange, prange->start, prange->last); > mutex_lock(&prange->migrate_mutex); > r = svm_range_validate_and_map(mm, prange->start, prange->last, prange, > - MAX_GPU_INSTANCE, true, true, prange->mapped_to_gpu); > + MAX_GPU_INSTANCE, true, true, flush_tlb); > if (r) > pr_debug("failed %d on remap svm range\n", r); > mutex_unlock(&prange->migrate_mutex); > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > index a63dfc95b602..0da635532aff 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > @@ -100,6 +100,9 @@ struct svm_work_list_item { > * @child_list: list header for split ranges which are not added to svms yet > * @bitmap_access: index bitmap of GPUs which can access the range > * @bitmap_aip: index bitmap of GPUs which can access the range in place > + * @bitmap_needs_unmap: index bitmap of GPUs which currently set NO_ACCESS > + * @bitmap_mapped: index bitmap of GPUs which currently have the range mapped > + * @mapping_done: true if range_validate_and_map complete successfully > * > * Data structure for virtual memory range shared by CPU and GPUs, it can be > * allocated from system memory ram or device vram, and migrate from ram to vram > @@ -135,7 +138,9 @@ struct svm_range { > struct list_head child_list; > DECLARE_BITMAP(bitmap_access, MAX_GPU_INSTANCE); > DECLARE_BITMAP(bitmap_aip, MAX_GPU_INSTANCE); > - bool mapped_to_gpu; > + DECLARE_BITMAP(bitmap_needs_unmap, MAX_GPU_INSTANCE); > + DECLARE_BITMAP(bitmap_mapped, MAX_GPU_INSTANCE); > + bool mapping_done; > atomic_t queue_refcount; > }; >