* [PATCH v3 0/2] drm/amdkfd: bound SVM mappings by the GPU address limits @ 2026-10-20 13:28 Daniel Huang 2026-10-20 13:29 ` [PATCH v3 1/2] drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses Daniel Huang 2026-10-20 13:29 ` [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit Daniel Huang 0 siblings, 2 replies; 5+ messages in thread From: Daniel Huang @ 2026-10-20 13:28 UTC (permalink / raw) To: amd-gfx; +Cc: Felix Kuehling, Christian König, Alex Deucher, Daniel Huang KFD SVM maps memory in GPUVM at the same virtual address used by the CPU, but no code bounds that address against what the GPU can actually represent. kfd_ioctl_svm() validates only page alignment and a non-zero size, and svm_range_map_to_gpu() passes the PFNs straight to amdgpu_vm_update_range(). The BO mapping path, by contrast, validates through amdgpu_vm_verify_parameters(). Patch 1 is the crash reported in [1]: with an explicit amdgpu.vm_size override the GPUVM can be smaller than the CPU address range, and the page-table walker indexes past the root page-table entries. It disables SVM when the GPUVM cannot cover AMDGPU_GMC_HOLE_START. Patch 2 addresses a gap in that check, found by the Sashiko AI review bot [3]. The comparison is against the GPU's own address hole, so on a system whose CPU address space is larger than the GPU's it succeeds and SVM stays enabled, while individual addresses can still fall outside the GPUVM. Every current GMC generation uses a 4-level, 48-bit GPUVM except one gfx12 variant, so a 5-level paging CPU reaches this whenever userspace passes an mmap hint above DEFAULT_MAP_WINDOW. Changes since v2 [2]: - Dropped the Fixes: tag and the stable Cc from both patches, and dropped stable@vger.kernel.org from the Cc list of this posting. The crash requires an explicit amdgpu.vm_size override, which exists for driver testing rather than normal end-user configuration, so this is not a conventional bug fix (Christian). - Patch 1 now states in its changelog why GMC v6/v7/v8 cannot be affected: kgd2kfd_init_zone_device() returns early for GC IP versions below 9.0.1, leaving kfd.pgmap.type at zero, so those parts never advertised HSA_CAP_SVMAPI_SUPPORTED to begin with and the new condition cannot change their behaviour. Thanks to Felix for confirming that KFD SVM has never been supported before GFXv9. - Patch 2 rewritten following Felix's review. v2 checked every range against every GPU at map time, and svm_range_map_to_gpus() skipped a GPU that could not represent the range without reporting anything to userspace. v3 instead determines the smallest GPUVM among the SVM capable GPUs of the process in svm_range_list_init(), stores it in svm_range_list, and validates where ranges are created. - Canonicalised the Closes: URL in patch 1; the previous repository redirects to [1]. Three points in patch 2 where I did not follow the suggestion exactly, flagged here so they are easy to object to: - The set-attribute check went into svm_range_is_valid() rather than svm_range_add(). svm_range_set_attr() calls it immediately before svm_range_add(), it already takes (p, start, size) in pages and already computes the inclusive last page, it runs before anything is allocated, and it additionally covers svm_range_get_attr(). Happy to move it into svm_range_add() if you prefer the literal placement. - svm_range_create_unregistered_range() clamps rather than rejects. Its range boundaries widen the fault address to the granularity, so a fault that is itself in range can produce a last page that is not. Rejecting the widened range would fail a legitimate fault, so only the fault address itself is rejected and the widened range is clamped. - One late check is kept, the hard -EINVAL in svm_range_map_to_gpu(), immediately before amdgpu_vm_update_range() walks the page tables. With the creation-time validation in place it should be unreachable; it is there as a backstop on the exact path that produced the GPF in [1]. Say the word and I will drop it. Note that a process wide minimum makes a GPU with a larger GPUVM unusable above the smallest limit in the process, even for ranges that only that GPU would map. Given the 4-level, 48-bit uniformity noted above this should not be observable on real systems, but it is a behaviour change and I would rather state it than have it found later. [1] https://github.com/ROCm/TheRock/issues/9302 [2] https://lore.kernel.org/amd-gfx/20261016170546.88570-1-danielhu@amd.com/ [3] https://sashiko.dev/#/patchset/20261016145129.69645-1-danielhu%40amd.com Per Documentation/process/generated-content.rst: an AI coding assistant was used throughout this series, as recorded by the Assisted-by tags. It diagnosed the fault path ordering that determines where patch 1 emits its warning, designed the creation-time validation and its call sites in patch 2, and drafted both changelogs and this cover letter. I reviewed and tested the result and take responsibility for it. Patch 1 is tested on a Radeon 780M as described in its notes. Patch 2 is built for CONFIG_HSA_AMD_SVM=y and =n and otherwise compile-tested only; I do not have a 5-level paging host with a 4-level GPU to exercise the out-of-range path directly, so review of the bounds arithmetic would be welcome. Daniel Huang (2): drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses drm/amdkfd: bound SVM ranges by the smallest GPU address limit drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 4 +++ drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 44 +++++++++++++++++++++-- drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 23 ++++++++++-- drivers/gpu/drm/amd/amdkfd/kfd_topology.c | 11 +++++- 4 files changed, 76 insertions(+), 6 deletions(-) base-commit: fe9f3ed36c83dcc24b4ca3e8913edd6e33a89150 -- 2.53.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 1/2] drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses 2026-10-20 13:28 [PATCH v3 0/2] drm/amdkfd: bound SVM mappings by the GPU address limits Daniel Huang @ 2026-10-20 13:29 ` Daniel Huang 2026-10-20 18:30 ` Felix Kuehling 2026-10-20 13:29 ` [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit Daniel Huang 1 sibling, 1 reply; 5+ messages in thread From: Daniel Huang @ 2026-10-20 13:29 UTC (permalink / raw) To: amd-gfx; +Cc: Felix Kuehling, Christian König, Alex Deucher, Daniel Huang KFD SVM maps memory in GPUVM at the same virtual address used by the CPU. However, svm_range_map_to_gpu() passes the corresponding PFNs to amdgpu_vm_update_range() without checking them against vm_manager.max_pfn. If the GPUVM address range is smaller than the CPU address range, the page-table walker can index beyond the allocated root page-table entries. Unlike the SVM path, the BO mapping path validates the address range through amdgpu_vm_verify_parameters(). Only an explicit amdgpu.vm_size override configures a GPUVM smaller than the driver would select on its own, and that parameter is meant for driver testing rather than normal end-user configuration. Reproduced on a Radeon 780M (gfx1103) with amdgpu.vm_size=64. Repeated hipMallocManaged() allocations attempted to map CPU address 0x74bd48000000, well beyond the 64 GiB GPUVM limit. The out-of-bounds root entry contained SLUB poison with slub_debug=FZPU and recycled heap data without it, resulting in a general protection fault: RIP: 0010:amdgpu_vm_cpu_update+0x2f/0x140 [amdgpu] amdgpu_vm_pte_update_flags+0xfa/0x1e0 [amdgpu] amdgpu_vm_ptes_update+0x38b/0xa00 [amdgpu] amdgpu_vm_update_range+0x279/0x930 [amdgpu] svm_range_map_to_gpu+0x13d/0x400 [amdgpu] svm_range_validate_and_map+0x8d3/0xaa0 [amdgpu] svm_ioctl+0x66/0x80 [amdgpu] kfd_ioctl+0x40a/0x650 [amdgpu] As discussed in [1], rejecting out-of-range PFNs in amdgpu_vm_update_range() is too late. SVM must not be initialized when the GPUVM cannot represent the required CPU addresses. Prevent this by disabling KFD SVM support when the GPUVM address range is smaller than AMDGPU_GMC_HOLE_START. This prevents affected devices from advertising SVM support and excludes them from SVM mappings. Warn from kfd_update_svm_support_properties(), which runs after the device memory pgmap is registered, so that only devices that would otherwise have advertised SVM report the loss of the capability. KFD SVM has never been supported before GFXv9: kgd2kfd_init_zone_device() returns early for GC IP versions below 9.0.1, leaving kfd.pgmap.type at zero, so KFD_IS_SVM_API_SUPPORTED() is already false and HSA_CAP_SVMAPI_SUPPORTED is never advertised on GMC v6/v7/v8. The new condition therefore cannot change behaviour on those generations. The BO and userptr paths are unaffected. Their GPU virtual address is chosen by user space independently of the CPU address and validated by amdgpu_vm_verify_parameters() in amdgpu_vm_bo_map(), so an out of range address is rejected at map time instead of reaching the page tables. SVM is the only path whose address is dictated by the CPU mapping. [1] https://lore.kernel.org/amd-gfx/da43b0e3-5bd9-4aa1-86d3-b733f3bad108@amd.com/ Closes: https://github.com/ROCm/TheRock/issues/9302 Assisted-by: Claude:claude-opus-5 Signed-off-by: Daniel Huang <danielhu@amd.com> --- drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 15 +++++++++++++-- drivers/gpu/drm/amd/amdkfd/kfd_topology.c | 11 ++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h index c7d7adae4476..f61030408f45 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h @@ -203,11 +203,22 @@ struct kfd_process_device * svm_range_get_pdd_by_node(struct svm_range *prange, struct kfd_node *node); void svm_range_list_lock_and_flush_work(struct svm_range_list *svms, struct mm_struct *mm); +/* SVM uses CPU virtual addresses for GPU mappings. Disable SVM if the GPUVM + * address range cannot cover the required CPU address range. The parameter + * must be named adev, AMDGPU_GMC_HOLE_START dereferences it. + */ +static inline bool kfd_svm_vm_covers_user_va(struct amdgpu_device *adev) +{ + return (adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT) >= + AMDGPU_GMC_HOLE_START; +} + /* SVM API and HMM page migration work together, device memory type * is initialized to not 0 when page migration register device memory. */ -#define KFD_IS_SVM_API_SUPPORTED(adev) ((adev)->kfd.pgmap.type != 0 ||\ - ((adev)->apu_prefer_gtt)) +#define KFD_IS_SVM_API_SUPPORTED(adev) \ + (((adev)->kfd.pgmap.type != 0 || (adev)->apu_prefer_gtt) && \ + kfd_svm_vm_covers_user_va(adev)) void svm_range_bo_unref_async(struct svm_range_bo *svm_bo); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 35b3abe57b80..5d12446b8ea8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -2471,8 +2471,17 @@ void kfd_update_svm_support_properties(struct amdgpu_device *adev) sys_props.generation_count++; else dev_err(adev->dev, "Failed to update SVM support properties. ret=%d\n", ret); - } else + } else { +#if IS_ENABLED(CONFIG_HSA_AMD_SVM) + if ((adev->kfd.pgmap.type || adev->apu_prefer_gtt) && + !kfd_svm_vm_covers_user_va(adev)) + dev_warn_once(adev->dev, + "GPUVM size 0x%llx is too small for KFD SVM; SVM disabled (amdgpu.vm_size=%d)\n", + adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT, + amdgpu_vm_size); +#endif dev->node_props.capability &= ~HSA_CAP_SVMAPI_SUPPORTED; + } } up_write(&topology_lock); } -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 1/2] drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses 2026-10-20 13:29 ` [PATCH v3 1/2] drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses Daniel Huang @ 2026-10-20 18:30 ` Felix Kuehling 0 siblings, 0 replies; 5+ messages in thread From: Felix Kuehling @ 2026-10-20 18:30 UTC (permalink / raw) To: Daniel Huang, amd-gfx; +Cc: Christian König, Alex Deucher On 2026-10-20 09:29, Daniel Huang wrote: > KFD SVM maps memory in GPUVM at the same virtual address used by > the CPU. However, svm_range_map_to_gpu() passes the corresponding > PFNs to amdgpu_vm_update_range() without checking them against > vm_manager.max_pfn. > > If the GPUVM address range is smaller than the CPU address range, > the page-table walker can index beyond the allocated root page-table > entries. Unlike the SVM path, the BO mapping path validates the > address range through amdgpu_vm_verify_parameters(). > > Only an explicit amdgpu.vm_size override configures a GPUVM smaller > than the driver would select on its own, and that parameter is meant > for driver testing rather than normal end-user configuration. > > Reproduced on a Radeon 780M (gfx1103) with amdgpu.vm_size=64. > Repeated hipMallocManaged() allocations attempted to map CPU > address 0x74bd48000000, well beyond the 64 GiB GPUVM limit. > The out-of-bounds root entry contained SLUB poison with > slub_debug=FZPU and recycled heap data without it, resulting in > a general protection fault: > > RIP: 0010:amdgpu_vm_cpu_update+0x2f/0x140 [amdgpu] > amdgpu_vm_pte_update_flags+0xfa/0x1e0 [amdgpu] > amdgpu_vm_ptes_update+0x38b/0xa00 [amdgpu] > amdgpu_vm_update_range+0x279/0x930 [amdgpu] > svm_range_map_to_gpu+0x13d/0x400 [amdgpu] > svm_range_validate_and_map+0x8d3/0xaa0 [amdgpu] > svm_ioctl+0x66/0x80 [amdgpu] > kfd_ioctl+0x40a/0x650 [amdgpu] > > As discussed in [1], rejecting out-of-range PFNs in > amdgpu_vm_update_range() is too late. SVM must not be initialized > when the GPUVM cannot represent the required CPU addresses. > > Prevent this by disabling KFD SVM support when the GPUVM address > range is smaller than AMDGPU_GMC_HOLE_START. This prevents > affected devices from advertising SVM support and excludes them > from SVM mappings. Warn from kfd_update_svm_support_properties(), > which runs after the device memory pgmap is registered, so that > only devices that would otherwise have advertised SVM report the > loss of the capability. > > KFD SVM has never been supported before GFXv9: kgd2kfd_init_zone_device() > returns early for GC IP versions below 9.0.1, leaving kfd.pgmap.type at > zero, so KFD_IS_SVM_API_SUPPORTED() is already false and > HSA_CAP_SVMAPI_SUPPORTED is never advertised on GMC v6/v7/v8. The new > condition therefore cannot change behaviour on those generations. > > The BO and userptr paths are unaffected. Their GPU virtual address is > chosen by user space independently of the CPU address and validated by > amdgpu_vm_verify_parameters() in amdgpu_vm_bo_map(), so an out of range > address is rejected at map time instead of reaching the page tables. > SVM is the only path whose address is dictated by the CPU mapping. > > [1] https://lore.kernel.org/amd-gfx/da43b0e3-5bd9-4aa1-86d3-b733f3bad108@amd.com/ > > Closes: https://github.com/ROCm/TheRock/issues/9302 > Assisted-by: Claude:claude-opus-5 > Signed-off-by: Daniel Huang <danielhu@amd.com> Reviewed-by: Felix Kuehling <felix.kuehling@amd.com> > --- > drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 15 +++++++++++++-- > drivers/gpu/drm/amd/amdkfd/kfd_topology.c | 11 ++++++++++- > 2 files changed, 23 insertions(+), 3 deletions(-) > > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > index c7d7adae4476..f61030408f45 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > @@ -203,11 +203,22 @@ struct kfd_process_device * > svm_range_get_pdd_by_node(struct svm_range *prange, struct kfd_node *node); > void svm_range_list_lock_and_flush_work(struct svm_range_list *svms, struct mm_struct *mm); > > +/* SVM uses CPU virtual addresses for GPU mappings. Disable SVM if the GPUVM > + * address range cannot cover the required CPU address range. The parameter > + * must be named adev, AMDGPU_GMC_HOLE_START dereferences it. > + */ > +static inline bool kfd_svm_vm_covers_user_va(struct amdgpu_device *adev) > +{ > + return (adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT) >= > + AMDGPU_GMC_HOLE_START; > +} > + > /* SVM API and HMM page migration work together, device memory type > * is initialized to not 0 when page migration register device memory. > */ > -#define KFD_IS_SVM_API_SUPPORTED(adev) ((adev)->kfd.pgmap.type != 0 ||\ > - ((adev)->apu_prefer_gtt)) > +#define KFD_IS_SVM_API_SUPPORTED(adev) \ > + (((adev)->kfd.pgmap.type != 0 || (adev)->apu_prefer_gtt) && \ > + kfd_svm_vm_covers_user_va(adev)) > > void svm_range_bo_unref_async(struct svm_range_bo *svm_bo); > > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c > index 35b3abe57b80..5d12446b8ea8 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c > @@ -2471,8 +2471,17 @@ void kfd_update_svm_support_properties(struct amdgpu_device *adev) > sys_props.generation_count++; > else > dev_err(adev->dev, "Failed to update SVM support properties. ret=%d\n", ret); > - } else > + } else { > +#if IS_ENABLED(CONFIG_HSA_AMD_SVM) > + if ((adev->kfd.pgmap.type || adev->apu_prefer_gtt) && > + !kfd_svm_vm_covers_user_va(adev)) > + dev_warn_once(adev->dev, > + "GPUVM size 0x%llx is too small for KFD SVM; SVM disabled (amdgpu.vm_size=%d)\n", > + adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT, > + amdgpu_vm_size); > +#endif > dev->node_props.capability &= ~HSA_CAP_SVMAPI_SUPPORTED; > + } > } > up_write(&topology_lock); > } ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit 2026-10-20 13:28 [PATCH v3 0/2] drm/amdkfd: bound SVM mappings by the GPU address limits Daniel Huang 2026-10-20 13:29 ` [PATCH v3 1/2] drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses Daniel Huang @ 2026-10-20 13:29 ` Daniel Huang 2026-10-20 18:41 ` Felix Kuehling 1 sibling, 1 reply; 5+ messages in thread From: Daniel Huang @ 2026-10-20 13:29 UTC (permalink / raw) To: amd-gfx; +Cc: Felix Kuehling, Christian König, Alex Deucher, Daniel Huang KFD SVM maps memory in GPUVM at the same virtual address used by the CPU, but nothing bounds that address against what the GPU can represent. kfd_ioctl_svm() checks only page alignment and a non-zero size, so the PFNs reach amdgpu_vm_update_range() unvalidated. Unlike the SVM path, the BO mapping path validates the address range through amdgpu_vm_verify_parameters(). Disabling SVM when the GPUVM cannot cover AMDGPU_GMC_HOLE_START closes the case where the GPUVM is globally too small, but it compares the GPUVM against the GPU's own address hole. When the CPU address space is larger than the GPU's, the comparison succeeds and SVM stays enabled while individual addresses can still fall outside the GPUVM. On a 5-level paging CPU with a 4-level GPU, mmap() returns addresses above the GPU limit when user space passes a hint above DEFAULT_MAP_WINDOW. Passing such an address to KFD SVM indexes beyond the allocated root page-table entries in amdgpu_vm_pt_descendant(). This gap was found by the Sashiko AI review bot, during review of the first patch in this series. Determine the smallest GPUVM among the SVM capable GPUs of the process in svm_range_list_init(), where the pdds are already final, and store it in svm_range_list as the first CPU page index that cannot be mapped. Reuse shared_resources.gpuvm_size, which the KFD already derives from vm_manager.max_pfn and the GPUVM address hole, and reports to user space as pdd->gpuvm_limit. Validate against that limit where ranges are created, so that an address the GPUs cannot map is rejected as early as possible and never results in a range that is silently left unmapped: - svm_range_is_valid() rejects out of range set_attr and get_attr ioctls with -EINVAL, before any range is allocated. - svm_range_create_unregistered_range() rejects an out of range fault address, which svm_range_restore_pages() reports as -EFAULT. The range boundaries widen the fault address to the granularity, so the widened range is clamped rather than the fault refused. svm_range_map_to_gpu() keeps a hard check so that no out of range PFN can reach amdgpu_vm_update_range() through any caller. A process wide minimum makes a GPU with a larger GPUVM unusable above the smallest limit in the process, even for ranges only that GPU would map. Every SVM capable GMC generation uses a 4-level, 48-bit GPUVM except one gfx12 variant, so the minimum is uniform in practice. Link: https://sashiko.dev/#/patchset/20261016145129.69645-1-danielhu%40amd.com Assisted-by: Claude:claude-opus-5 Signed-off-by: Daniel Huang <danielhu@amd.com> --- drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 4 +++ drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 44 +++++++++++++++++++++++++-- drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 8 +++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index b9a31489b1c8..a800515baf4a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -907,6 +907,10 @@ struct svm_range_list { atomic_t drain_pagefaults; struct delayed_work restore_work; DECLARE_BITMAP(bitmap_supported, MAX_GPU_INSTANCE); + /* First CPU page index beyond the smallest GPUVM supported by the + * SVM capable GPUs of this process. Zero if none support SVM. + */ + uint64_t max_va_pfn; struct task_struct *faulting_task; /* check point ts decides if page fault recovery need be dropped */ atomic64_t checkpoint_ts[MAX_GPU_INSTANCE]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index cfb428c72686..3fa0d6ff7236 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1504,6 +1504,15 @@ svm_range_map_to_gpu(struct kfd_process_device *pdd, struct svm_range *prange, return -EINVAL; } + /* Backstop before amdgpu_vm_update_range() walks the page tables. + * Ranges are bounded when created, so this is not expected to trigger. + */ + if (npages && last_start + npages - 1 >= kfd_svm_gpu_va_limit_pfn(pdd)) { + pr_debug("CPU[0x%lx 0x%lx] exceeds GPUVM, canceling map\n", + last_start, last_start + npages - 1); + return -EINVAL; + } + for (i = offset; i < offset + npages; i++) { uint64_t gpu_start; uint64_t gpu_end; @@ -2960,6 +2969,15 @@ svm_range *svm_range_create_unregistered_range(struct kfd_node *node, &is_heap_stack)) return NULL; + if (p->svms.max_va_pfn) { + if (addr >= p->svms.max_va_pfn) { + pr_debug("fault address 0x%llx exceeds GPUVM limit 0x%llx\n", + addr, p->svms.max_va_pfn); + return NULL; + } + last = min_t(unsigned long, last, p->svms.max_va_pfn - 1); + } + r = svm_range_check_vm(p, start, last, &bo_s, &bo_l); if (r != -EADDRINUSE) r = svm_range_check_vm_userptr(p, start, last, &bo_s, &bo_l); @@ -3410,9 +3428,18 @@ int svm_range_list_init(struct kfd_process *p) INIT_LIST_HEAD(&svms->criu_svm_metadata_list); spin_lock_init(&svms->deferred_list_lock); - for (i = 0; i < p->n_pdds; i++) - if (KFD_IS_SVM_API_SUPPORTED(p->pdds[i]->dev->adev)) - bitmap_set(svms->bitmap_supported, i, 1); + /* The ranges of this process must fit in every GPU that can map them, + * so bound them by the smallest GPUVM among the SVM capable GPUs. + */ + for (i = 0; i < p->n_pdds; i++) { + struct kfd_process_device *pdd = p->pdds[i]; + + if (!KFD_IS_SVM_API_SUPPORTED(pdd->dev->adev)) + continue; + bitmap_set(svms->bitmap_supported, i, 1); + svms->max_va_pfn = min_not_zero(svms->max_va_pfn, + kfd_svm_gpu_va_limit_pfn(pdd)); + } /* Value of default granularity cannot exceed 0x1B, the * number of pages supported by a 4-level paging table @@ -3511,6 +3538,17 @@ svm_range_is_valid(struct kfd_process *p, uint64_t start, uint64_t size) if (check_add_overflow(start, size << PAGE_SHIFT, &end)) return -EOVERFLOW; + /* Reject up front what the GPUs of this process cannot map, rather + * than letting the mapping fail later on. + */ + if (p->svms.max_va_pfn && + (end - 1) >> PAGE_SHIFT >= p->svms.max_va_pfn) { + pr_debug("CPU[0x%lx 0x%lx] exceeds GPUVM limit 0x%llx\n", + start_unchg, (end - 1) >> PAGE_SHIFT, + p->svms.max_va_pfn); + return -EINVAL; + } + do { vma = vma_lookup(p->mm, start); if (!vma || (vma->vm_flags & device_vma)) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h index f61030408f45..268a545302bd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h @@ -213,6 +213,14 @@ static inline bool kfd_svm_vm_covers_user_va(struct amdgpu_device *adev) AMDGPU_GMC_HOLE_START; } +/* First CPU page index that this GPU's GPUVM cannot represent. An SVM range + * is mappable on pdd only if its last page lies below this. + */ +static inline uint64_t kfd_svm_gpu_va_limit_pfn(struct kfd_process_device *pdd) +{ + return pdd->dev->kfd->shared_resources.gpuvm_size >> PAGE_SHIFT; +} + /* SVM API and HMM page migration work together, device memory type * is initialized to not 0 when page migration register device memory. */ -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit 2026-10-20 13:29 ` [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit Daniel Huang @ 2026-10-20 18:41 ` Felix Kuehling 0 siblings, 0 replies; 5+ messages in thread From: Felix Kuehling @ 2026-10-20 18:41 UTC (permalink / raw) To: Daniel Huang, amd-gfx; +Cc: Christian König, Alex Deucher On 2026-10-20 09:29, Daniel Huang wrote: > KFD SVM maps memory in GPUVM at the same virtual address used by the > CPU, but nothing bounds that address against what the GPU can > represent. kfd_ioctl_svm() checks only page alignment and a non-zero > size, so the PFNs reach amdgpu_vm_update_range() unvalidated. Unlike > the SVM path, the BO mapping path validates the address range through > amdgpu_vm_verify_parameters(). > > Disabling SVM when the GPUVM cannot cover AMDGPU_GMC_HOLE_START closes > the case where the GPUVM is globally too small, but it compares the > GPUVM against the GPU's own address hole. When the CPU address space is > larger than the GPU's, the comparison succeeds and SVM stays enabled > while individual addresses can still fall outside the GPUVM. > > On a 5-level paging CPU with a 4-level GPU, mmap() returns addresses > above the GPU limit when user space passes a hint above > DEFAULT_MAP_WINDOW. Passing such an address to KFD SVM indexes beyond > the allocated root page-table entries in amdgpu_vm_pt_descendant(). > > This gap was found by the Sashiko AI review bot, during review of the > first patch in this series. > > Determine the smallest GPUVM among the SVM capable GPUs of the process > in svm_range_list_init(), where the pdds are already final, and store > it in svm_range_list as the first CPU page index that cannot be mapped. > Reuse shared_resources.gpuvm_size, which the KFD already derives from > vm_manager.max_pfn and the GPUVM address hole, and reports to user > space as pdd->gpuvm_limit. > > Validate against that limit where ranges are created, so that an > address the GPUs cannot map is rejected as early as possible and never > results in a range that is silently left unmapped: > > - svm_range_is_valid() rejects out of range set_attr and get_attr > ioctls with -EINVAL, before any range is allocated. > - svm_range_create_unregistered_range() rejects an out of range fault > address, which svm_range_restore_pages() reports as -EFAULT. The > range boundaries widen the fault address to the granularity, so the > widened range is clamped rather than the fault refused. > > svm_range_map_to_gpu() keeps a hard check so that no out of range PFN > can reach amdgpu_vm_update_range() through any caller. > > A process wide minimum makes a GPU with a larger GPUVM unusable above > the smallest limit in the process, even for ranges only that GPU would > map. Every SVM capable GMC generation uses a 4-level, 48-bit GPUVM > except one gfx12 variant, so the minimum is uniform in practice. > > Link: https://sashiko.dev/#/patchset/20261016145129.69645-1-danielhu%40amd.com > Assisted-by: Claude:claude-opus-5 > Signed-off-by: Daniel Huang <danielhu@amd.com> > --- > drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 4 +++ > drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 44 +++++++++++++++++++++++++-- > drivers/gpu/drm/amd/amdkfd/kfd_svm.h | 8 +++++ > 3 files changed, 53 insertions(+), 3 deletions(-) > > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h > index b9a31489b1c8..a800515baf4a 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h > @@ -907,6 +907,10 @@ struct svm_range_list { > atomic_t drain_pagefaults; > struct delayed_work restore_work; > DECLARE_BITMAP(bitmap_supported, MAX_GPU_INSTANCE); > + /* First CPU page index beyond the smallest GPUVM supported by the > + * SVM capable GPUs of this process. Zero if none support SVM. > + */ > + uint64_t max_va_pfn; > struct task_struct *faulting_task; > /* check point ts decides if page fault recovery need be dropped */ > atomic64_t checkpoint_ts[MAX_GPU_INSTANCE]; > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > index cfb428c72686..3fa0d6ff7236 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c > @@ -1504,6 +1504,15 @@ svm_range_map_to_gpu(struct kfd_process_device *pdd, struct svm_range *prange, > return -EINVAL; > } > > + /* Backstop before amdgpu_vm_update_range() walks the page tables. > + * Ranges are bounded when created, so this is not expected to trigger. > + */ > + if (npages && last_start + npages - 1 >= kfd_svm_gpu_va_limit_pfn(pdd)) { > + pr_debug("CPU[0x%lx 0x%lx] exceeds GPUVM, canceling map\n", > + last_start, last_start + npages - 1); > + return -EINVAL; > + } If this cannot happen, then either drop it, or turn it into a WARN or WARN_ONCE (the closest thing we have to "assert"). Other than that, this patch looks good to me. Regards, Felix > + > for (i = offset; i < offset + npages; i++) { > uint64_t gpu_start; > uint64_t gpu_end; > @@ -2960,6 +2969,15 @@ svm_range *svm_range_create_unregistered_range(struct kfd_node *node, > &is_heap_stack)) > return NULL; > > + if (p->svms.max_va_pfn) { > + if (addr >= p->svms.max_va_pfn) { > + pr_debug("fault address 0x%llx exceeds GPUVM limit 0x%llx\n", > + addr, p->svms.max_va_pfn); > + return NULL; > + } > + last = min_t(unsigned long, last, p->svms.max_va_pfn - 1); > + } > + > r = svm_range_check_vm(p, start, last, &bo_s, &bo_l); > if (r != -EADDRINUSE) > r = svm_range_check_vm_userptr(p, start, last, &bo_s, &bo_l); > @@ -3410,9 +3428,18 @@ int svm_range_list_init(struct kfd_process *p) > INIT_LIST_HEAD(&svms->criu_svm_metadata_list); > spin_lock_init(&svms->deferred_list_lock); > > - for (i = 0; i < p->n_pdds; i++) > - if (KFD_IS_SVM_API_SUPPORTED(p->pdds[i]->dev->adev)) > - bitmap_set(svms->bitmap_supported, i, 1); > + /* The ranges of this process must fit in every GPU that can map them, > + * so bound them by the smallest GPUVM among the SVM capable GPUs. > + */ > + for (i = 0; i < p->n_pdds; i++) { > + struct kfd_process_device *pdd = p->pdds[i]; > + > + if (!KFD_IS_SVM_API_SUPPORTED(pdd->dev->adev)) > + continue; > + bitmap_set(svms->bitmap_supported, i, 1); > + svms->max_va_pfn = min_not_zero(svms->max_va_pfn, > + kfd_svm_gpu_va_limit_pfn(pdd)); > + } > > /* Value of default granularity cannot exceed 0x1B, the > * number of pages supported by a 4-level paging table > @@ -3511,6 +3538,17 @@ svm_range_is_valid(struct kfd_process *p, uint64_t start, uint64_t size) > if (check_add_overflow(start, size << PAGE_SHIFT, &end)) > return -EOVERFLOW; > > + /* Reject up front what the GPUs of this process cannot map, rather > + * than letting the mapping fail later on. > + */ > + if (p->svms.max_va_pfn && > + (end - 1) >> PAGE_SHIFT >= p->svms.max_va_pfn) { > + pr_debug("CPU[0x%lx 0x%lx] exceeds GPUVM limit 0x%llx\n", > + start_unchg, (end - 1) >> PAGE_SHIFT, > + p->svms.max_va_pfn); > + return -EINVAL; > + } > + > do { > vma = vma_lookup(p->mm, start); > if (!vma || (vma->vm_flags & device_vma)) > diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > index f61030408f45..268a545302bd 100644 > --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h > @@ -213,6 +213,14 @@ static inline bool kfd_svm_vm_covers_user_va(struct amdgpu_device *adev) > AMDGPU_GMC_HOLE_START; > } > > +/* First CPU page index that this GPU's GPUVM cannot represent. An SVM range > + * is mappable on pdd only if its last page lies below this. > + */ > +static inline uint64_t kfd_svm_gpu_va_limit_pfn(struct kfd_process_device *pdd) > +{ > + return pdd->dev->kfd->shared_resources.gpuvm_size >> PAGE_SHIFT; > +} > + > /* SVM API and HMM page migration work together, device memory type > * is initialized to not 0 when page migration register device memory. > */ ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-20 18:42 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-20 13:28 [PATCH v3 0/2] drm/amdkfd: bound SVM mappings by the GPU address limits Daniel Huang 2026-10-20 13:29 ` [PATCH v3 1/2] drm/amdkfd: disable SVM when the GPUVM cannot cover CPU addresses Daniel Huang 2026-10-20 18:30 ` Felix Kuehling 2026-10-20 13:29 ` [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit Daniel Huang 2026-10-20 18:41 ` Felix Kuehling
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.