All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Huang <danielhu@amd.com>
To: <amd-gfx@lists.freedesktop.org>
Cc: "Felix Kuehling" <Felix.Kuehling@amd.com>,
	"Christian König" <christian.koenig@amd.com>,
	"Alex Deucher" <alexander.deucher@amd.com>,
	"Daniel Huang" <danielhu@amd.com>
Subject: [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit
Date: Tue, 20 Oct 2026 21:29:01 +0800	[thread overview]
Message-ID: <20261020132901.23157-3-danielhu@amd.com> (raw)
In-Reply-To: <20261020132901.23157-1-danielhu@amd.com>

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


  parent reply	other threads:[~2026-10-20  5:29 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Daniel Huang [this message]
2026-10-20 18:41   ` [PATCH v3 2/2] drm/amdkfd: bound SVM ranges by the smallest GPU address limit Felix Kuehling

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261020132901.23157-3-danielhu@amd.com \
    --to=danielhu@amd.com \
    --cc=Felix.Kuehling@amd.com \
    --cc=alexander.deucher@amd.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=christian.koenig@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.