public inbox for dri-devel@lists.freedesktop.org
 help / color / mirror / Atom feed
From: Matthew Brost <matthew.brost@intel.com>
To: Honglei Huang <honglei1.huang@amd.com>
Cc: <Alexander.Deucher@amd.com>, <Felix.Kuehling@amd.com>,
	<Christian.Koenig@amd.com>, <Oak.Zeng@amd.com>,
	<Jenny-Jing.Liu@amd.com>, <Philip.Yang@amd.com>,
	<Xiaogang.Chen@amd.com>, <Ray.Huang@amd.com>,
	<Lingshan.Zhu@amd.com>, <Junhua.Shen@amd.com>,
	<rodrigo.vivi@intel.com>, <thomas.hellstrom@linux.intel.com>,
	<dakr@kernel.org>, <aliceryhl@google.com>,
	<amd-gfx@lists.freedesktop.org>,
	<dri-devel@lists.freedesktop.org>, <honghuan@amd.com>,
	Honghuan He <honghuan.he@amd.com>
Subject: Re: [RFC V3 11/12] drm/amdgpu: add SVM ioctl, garbage collector, and fault handler
Date: Mon, 20 Apr 2026 09:24:14 -0700	[thread overview]
Message-ID: <aeZTLi3yoZ1ADnDO@gsse-cloud1.jf.intel.com> (raw)
In-Reply-To: <20260420131307.1816671-12-honglei1.huang@amd.com>

On Mon, Apr 20, 2026 at 09:13:06PM +0800, Honglei Huang wrote:
> From: Honglei Huang <honghuan@amd.com>
> 
> Add the ioctl entry point and garbage collector to amdgpu_svm.c,
> and introduce amdgpu_svm_fault.c and amdgpu_svm_fault.h as a
> dedicated fault handler module.
> 
> Ioctl (amdgpu_svm.c):
> - amdgpu_svm_copy_attrs(): copy and validate user attribute array
>   from userspace with size and alignment checks
> - amdgpu_gem_svm_ioctl(): handle DRM_AMDGPU_GEM_SVM dispatching
>   to SET_ATTR or GET_ATTR with copy_to_user for GET results
> 
> Garbage collector (amdgpu_svm.c):
> - amdgpu_svm_garbage_collector(): dequeue and remove GC-listed
>   ranges under svm_lock, clear corresponding attributes
> - amdgpu_svm_range_clean_queue(): batch cleanup for dequeued
>   work items
> - amdgpu_svm_garbage_collector_work_func(): GC work handler
> - amdgpu_svm_gc_init/fini/flush(): lifecycle management for
>   the GC workqueue
> 
> Fault handler (amdgpu_svm_fault.c):
> - AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING: 2ms dedup threshold
> - amdgpu_svm_range_get_unregistered_attrs(): derive default
>   attributes for faulting addresses without explicit registration,
>   using VMA properties and GPU IP capabilities
> - svm_check_fault_allowed(): validate fault access against
>   attribute permissions and read-only enforcement
> - amdgpu_svm_range_map_fault(): core fault mapping that finds or
>   creates a gpusvm range, gets pages, maps into GPU page tables,
>   retries on -EAGAIN up to 3 times
> - amdgpu_svm_handle_fault(): main entry called from
>   amdgpu_vm_handle_fault(). Looks up SVM by PASID, acquires
>   mmap_read_lock and svm_lock, runs garbage collector, resolves
>   attributes from the tree or derives defaults, uses timestamp
>   deduplication to skip stale faults, dispatches to map_fault
> 
> Fault header (amdgpu_svm_fault.h):
> - Forward declarations and amdgpu_svm_handle_fault() prototype
> 
> Signed-off-by: Honghuan He <honghuan.he@amd.com>
> ---
>  drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c       | 149 +++++++
>  drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c | 368 ++++++++++++++++++
>  drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.h |  39 ++
>  3 files changed, 556 insertions(+)
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
>  create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.h
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c
> index 5fbed9b9f..a672deede 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c
> @@ -316,3 +316,152 @@ bool amdgpu_svm_is_enabled(struct amdgpu_vm *vm)
>  	return vm->svm != NULL;
>  }
>  
> +static int amdgpu_svm_copy_attrs(const struct drm_amdgpu_gem_svm *args,
> +					   struct drm_amdgpu_svm_attribute **attrs,
> +					   size_t *size)
> +{
> +	if (!args->nattr || args->nattr > AMDGPU_SVM_MAX_ATTRS)
> +		return -EINVAL;
> +	if (!args->attrs_ptr)
> +		return -EINVAL;
> +
> +	*size = args->nattr * sizeof(**attrs);
> +	*attrs = memdup_user(u64_to_user_ptr(args->attrs_ptr), *size);
> +
> +	return PTR_ERR_OR_ZERO(*attrs);
> +}
> +
> +int amdgpu_svm_garbage_collector(struct amdgpu_svm *svm)
> +{
> +	int ret;
> +	struct amdgpu_svm_range_op_ctx op_ctx;
> +
> +	lockdep_assert_held_write(&svm->svm_lock);
> +
> +	spin_lock(&svm->work_lock);
> +	while (amdgpu_svm_range_dequeue_locked(svm, &svm->gc.list, &op_ctx)) {
> +		spin_unlock(&svm->work_lock);
> +
> +		if (UNMAP_WORK(op_ctx.pending_ops)) {
> +			ret = amdgpu_svm_attr_clear_pages(
> +				svm->attr_tree, op_ctx.start_page, op_ctx.last_page);
> +			if (ret)
> +				return ret;
> +
> +			drm_gpusvm_range_remove(&svm->gpusvm,
> +						&op_ctx.range->base);
> +		}
> +
> +		amdgpu_svm_range_put_if_dequeued(svm, op_ctx.range);
> +		spin_lock(&svm->work_lock);
> +	}
> +	spin_unlock(&svm->work_lock);
> +	return 0;
> +}
> +
> +void
> +amdgpu_svm_range_clean_queue(struct amdgpu_svm *svm,
> +			     struct list_head *work_list)
> +{
> +	struct amdgpu_svm_range_op_ctx op_ctx;
> +
> +	spin_lock(&svm->work_lock);
> +	while (amdgpu_svm_range_dequeue_locked(svm, work_list,
> +				    &op_ctx)) {
> +		spin_unlock(&svm->work_lock);
> +		amdgpu_svm_range_put_if_dequeued(svm, op_ctx.range);
> +		spin_lock(&svm->work_lock);
> +	}
> +	spin_unlock(&svm->work_lock);
> +}
> +
> +static void amdgpu_svm_garbage_collector_work_func(struct work_struct *w)
> +{
> +	struct amdgpu_svm_gc *gc = container_of(w, struct amdgpu_svm_gc, work);
> +	struct amdgpu_svm *svm = container_of(gc, struct amdgpu_svm, gc);
> +
> +	down_write(&svm->svm_lock);
> +	amdgpu_svm_garbage_collector(svm);
> +	up_write(&svm->svm_lock);
> +}
> +
> +int amdgpu_svm_gc_init(struct amdgpu_svm *svm)
> +{
> +	svm->gc.wq = alloc_workqueue(AMDGPU_SVM_GC_WQ_NAME,
> +					WQ_UNBOUND | WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
> +	if (!svm->gc.wq)
> +		return -ENOMEM;
> +
> +	INIT_LIST_HEAD(&svm->gc.list);
> +	INIT_WORK(&svm->gc.work, amdgpu_svm_garbage_collector_work_func);
> +
> +	return 0;
> +}
> +
> +void amdgpu_svm_gc_fini(struct amdgpu_svm *svm)
> +{
> +	flush_work(&svm->gc.work);
> +	amdgpu_svm_range_clean_queue(svm, &svm->gc.list);
> +	destroy_workqueue(svm->gc.wq);
> +	svm->gc.wq = NULL;
> +}
> +
> +void amdgpu_svm_gc_flush(struct amdgpu_svm *svm)
> +{
> +	flush_work(&svm->gc.work);
> +}
> +
> +int amdgpu_gem_svm_ioctl(struct drm_device *dev, void *data,
> +			 struct drm_file *filp)
> +{
> +	struct amdgpu_fpriv *fpriv = filp->driver_priv;
> +	struct amdgpu_device *adev = drm_to_adev(dev);
> +	struct drm_amdgpu_gem_svm *args = data;
> +	struct drm_amdgpu_svm_attribute *attrs = NULL;
> +	struct amdgpu_vm *vm;
> +	size_t attrs_size = 0;
> +	int ret = 0;
> +
> +	AMDGPU_SVM_TRACE("ioctl op=%u va:[0x%llx-0x%llx)-0x%llx nattr=%u\n",
> +			 args->operation, args->start_addr, args->start_addr + args->size,
> +			 args->size, args->nattr);
> +
> +	vm = &fpriv->vm;
> +	if (!amdgpu_svm_is_enabled(vm)) {
> +		ret = amdgpu_svm_init(adev, vm);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if ((args->start_addr & ~PAGE_MASK) || (args->size & ~PAGE_MASK))
> +		return -EINVAL;
> +
> +	if (!args->start_addr || !args->size)
> +		return -EINVAL;
> +
> +	ret = amdgpu_svm_copy_attrs(args, &attrs, &attrs_size);
> +	if (ret)
> +		return ret;
> +
> +	switch (args->operation) {
> +	case AMDGPU_SVM_OP_SET_ATTR:
> +		ret = amdgpu_svm_set_attr(vm, args->start_addr, args->size,
> +					 args->nattr, attrs);
> +		break;
> +	case AMDGPU_SVM_OP_GET_ATTR:
> +		ret = amdgpu_svm_get_attr(vm, args->start_addr, args->size,
> +					 args->nattr, attrs);
> +		if (!ret && copy_to_user(u64_to_user_ptr(args->attrs_ptr),
> +					 attrs, attrs_size))
> +			ret = -EFAULT;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	kvfree(attrs);
> +	return ret;
> +}
> +
> +#endif /* CONFIG_DRM_AMDGPU_SVM */
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
> new file mode 100644
> index 000000000..968fb402b
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
> @@ -0,0 +1,368 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> +/*
> + * Copyright 2026 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +#include "amdgpu_svm.h"
> +#include "amdgpu_svm_attr.h"
> +#include "amdgpu_svm_fault.h"
> +#include "amdgpu_svm_range.h"
> +#include "amdgpu.h"
> +#include "amdgpu_vm.h"
> +#include "amdgpu_gmc.h"
> +#include "amdgpu_ih.h"
> +
> +#include <drm/drm_exec.h>
> +#include <drm/drm_gpusvm.h>
> +
> +#include <linux/delay.h>
> +#include <linux/mm.h>
> +#include <linux/sched/mm.h>
> +
> +#if IS_ENABLED(CONFIG_DRM_AMDGPU_SVM)
> +
> +#define AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING	(2UL * NSEC_PER_MSEC)
> +
> +static int amdgpu_svm_range_get_unregistered_attrs(struct amdgpu_svm *svm,
> +					    unsigned long fault_addr,
> +					    unsigned long attr_start_page,
> +					    unsigned long attr_last_page,
> +					    struct amdgpu_svm_attr_range **out)
> +{
> +	struct amdgpu_svm_attr_tree *attr_tree = svm->attr_tree;
> +	struct amdgpu_svm_attr_range *range;
> +	struct amdgpu_svm_attrs attrs;
> +	struct mm_struct *mm = svm->gpusvm.mm;
> +	struct vm_area_struct *vma;
> +	unsigned long fault_page = fault_addr >> PAGE_SHIFT;
> +	unsigned long start_page, last_page;
> +	unsigned long vma_start_page, vma_last_page;
> +
> +	amdgpu_svm_attr_set_default(svm, &attrs);
> +
> +	mmap_read_lock(mm);
> +
> +	vma = amdgpu_svm_check_vma(mm, fault_addr);
> +	if (IS_ERR(vma)) {
> +		mmap_read_unlock(mm);
> +		AMDGPU_SVM_ERR("get_unregistered_attrs: invalid VMA for fault_addr=0x%lx\n",
> +		       fault_addr);
> +		return PTR_ERR(vma);
> +	}
> +	vma_start_page = vma->vm_start >> PAGE_SHIFT;
> +	vma_last_page = (vma->vm_end >> PAGE_SHIFT) - 1;
> +
> +	if (vma_is_initial_heap(vma) || vma_is_initial_stack(vma))
> +		attrs.preferred_loc = AMDGPU_SVM_LOCATION_SYSMEM;
> +
> +	mmap_read_unlock(mm);
> +
> +	start_page = max(vma_start_page,
> +		    (unsigned long)ALIGN_DOWN(fault_page, 1UL << attrs.granularity));
> +	last_page = min(vma_last_page,
> +		   (unsigned long)ALIGN(fault_page + 1, 1UL << attrs.granularity) - 1);
> +
> +	start_page = max(start_page, attr_start_page);
> +	last_page = min(last_page, attr_last_page);
> +
> +	mutex_lock(&attr_tree->lock);
> +	range = amdgpu_svm_attr_range_alloc(start_page, last_page, &attrs);
> +	if (!range) {
> +		mutex_unlock(&attr_tree->lock);
> +		return -ENOMEM;
> +	}
> +	amdgpu_svm_attr_range_insert_locked(attr_tree, range);
> +	mutex_unlock(&attr_tree->lock);
> +
> +	AMDGPU_SVM_TRACE(
> +		"Created unregistered range for fault_addr=0x%lx: attr range=[0x%lx-0x%lx] size: 0x%lx attrs={preferred_loc=%d, prefetch_loc=%d, flags=0x%x, granularity=%u, access=%u}\n",
> +		fault_addr, amdgpu_svm_attr_start_page(range),
> +		amdgpu_svm_attr_last_page(range) + 1,
> +		amdgpu_svm_attr_last_page(range) -
> +			amdgpu_svm_attr_start_page(range) + 1,
> +		range->attrs.preferred_loc, range->attrs.prefetch_loc,
> +		range->attrs.flags, range->attrs.granularity,
> +		range->attrs.access);
> +
> +	*out = range;
> +	return 0;
> +}
> +
> +static int svm_check_fault_allowed(struct amdgpu_svm *svm,
> +				   unsigned long fault_addr, bool write_fault)
> +{
> +	struct mm_struct *mm = svm->gpusvm.mm;
> +	struct vm_area_struct *vma;
> +	unsigned long requested = VM_READ;
> +	int ret = 0;
> +
> +	if (write_fault)
> +		requested |= VM_WRITE;
> +
> +	mmap_read_lock(mm);
> +	vma = vma_lookup(mm, fault_addr);
> +	if (vma && (vma->vm_flags & requested) != requested) {
> +		AMDGPU_SVM_ERR("fault addr 0x%lx no %s permission\n",
> +			 fault_addr, write_fault ? "write" : "read");
> +		ret = -EPERM;
> +	}
> +	mmap_read_unlock(mm);
> +
> +	return ret;
> +}
> +
> +static int amdgpu_svm_range_map_fault(struct amdgpu_svm *svm,
> +			       unsigned long fault_addr,
> +			       const struct amdgpu_svm_attr_range *attr_range,
> +			       bool write_fault)
> +{
> +	const struct amdgpu_svm_attrs *attrs = &attr_range->attrs;
> +	bool devmem_possible = amdgpu_svm_attr_devmem_possible(svm, attrs);
> +	bool need_vram_migration = amdgpu_svm_attr_prefer_vram(svm, attrs);
> +	devmem_possible = false; /* TODO: add migration */
> +	struct drm_gpusvm_ctx map_ctx = {
> +		.read_only = !!(attrs->flags & AMDGPU_SVM_FLAG_GPU_RO),
> +		.devmem_possible = devmem_possible,
> +		.check_pages_threshold = devmem_possible ? SZ_64K : 0,
> +		.devmem_only = need_vram_migration && devmem_possible,
> +		.timeslice_ms = need_vram_migration && devmem_possible ? 5 : 0,
> +	};
> +	struct amdgpu_svm_range *range;
> +	ktime_t timestamp = ktime_get_boottime();
> +	uint64_t range_pte_flags;
> +	int retry_count = 3;
> +	int ret;
> +
> +	lockdep_assert_held_write(&svm->svm_lock);
> +	WARN_ON(!svm->xnack_enabled);
> +
> +retry:
> +	ret = amdgpu_svm_garbage_collector(svm);
> +	if (ret) {
> +		AMDGPU_SVM_ERR(
> +			"fault garbage collector failed: ret=%d, fault_addr=0x%lx\n",
> +			ret, fault_addr);
> +		return ret;
> +	}
> +
> +	ret = svm_check_fault_allowed(svm, fault_addr, write_fault);
> +	if (ret)
> +		return ret;
> +
> +	range = amdgpu_svm_range_find_or_insert(svm, fault_addr,
> +						 attr_range, &map_ctx);
> +	if (IS_ERR(range)) {
> +		ret = PTR_ERR(range);
> +		AMDGPU_SVM_ERR("map_fault: range_find_or_insert failed: fault=0x%lx ret=%d\n",
> +				 fault_addr, ret);
> +		/*
> +		 * -EINVAL: fault_addr out of gpusvm range, or no chunk size
> +		 *          fits within VMA/notifier/attr_range bounds.
> +		 * -EFAULT: mmget_not_zero failed.
> +		 * -ENOENT: No VMA at fault_addr.
> +		 * -ENOMEM: Notifier or range allocation failed.
> +		 */

Just a drive-by comment: as we’re getting to multiple users of GPU SVM,
and each driver is making decisions based on the error codes returned by
the common layer, it may be time to update the GPU SVM kernel
documentation to clearly define what each return code means for every
call.

There may also be some inconsistency in the return codes due to the
ad-hoc nature of how this evolved. If we need to clean up any return
values, this is probably something we should do now—before we end up in
a situation where we change a return value and then have to fix multiple
drivers.

Please let us know if, while you’re working in this area, you notice any
GPU SVM return values that don’t make sense or could use adjustment.

Matt

> +		if (ret == -EFAULT || ret == -ENOENT) {
> +			AMDGPU_SVM_ERR("no vma or mm is dying: 0x%lx, ret=%d\n",
> +					 fault_addr, ret);
> +			ret = 0;
> +		}
> +
> +		return ret;
> +	}
> +
> +	if (ktime_before(timestamp, ktime_add_ns(range->validate_timestamp,
> +					 AMDGPU_SVM_RANGE_RETRY_FAULT_PENDING))) {
> +		AMDGPU_SVM_TRACE("already restored, skip: fault=0x%lx range=[0x%lx-0x%lx)\n",
> +				 fault_addr, drm_gpusvm_range_start(&range->base),
> +				 drm_gpusvm_range_end(&range->base));
> +		goto out;
> +	}
> +
> +	range_pte_flags = amdgpu_svm_range_attr_pte_flags(
> +					svm, attrs, map_ctx.read_only);
> +
> +	if (!(write_fault && map_ctx.read_only) &&
> +	    amdgpu_svm_range_is_valid(svm, range, attrs, range_pte_flags)) {
> +		AMDGPU_SVM_TRACE("valid range, skip: fault=0x%lx range=[0x%lx-0x%lx)\n",
> +				 fault_addr, drm_gpusvm_range_start(&range->base),
> +				 drm_gpusvm_range_end(&range->base));
> +		goto out;
> +	}
> +
> +	AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT");
> +	/* TODO: add migration*/
> +
> +	AMDGPU_SVM_RANGE_DEBUG(range, "GET PAGES");
> +	ret = amdgpu_svm_range_get_pages(svm, &range->base, &map_ctx);
> +	if (ret == -EOPNOTSUPP || ret == -EFAULT) {
> +		/*
> +		* -EOPNOTSUPP  Mixed page types within range.
> +		* -EFAULT      (a) mm is dying.
> +		*              (b) range was unmapped.
> +		*              (c) DMA mapping failed.
> +		*              (d) devmem_only requested but system page encountered.
> +		*              (e) hmm_range_fault: no VMA, page fault error, bad pte/pmd.
> +		* -EBUSY       HMM retry loop timed out.
> +		* -ENOMEM      PFN or DMA address array allocation failed.
> +		* -EINVAL      hmm_range_fault: invalid VMA type.
> +		*/
> +		map_ctx.timeslice_ms <<= 1;
> +		if (!map_ctx.devmem_only && --retry_count > 0) {
> +			AMDGPU_SVM_ERR("start retry: get_pages failed with %d, retries_left=%d: fault=0x%lx range=[0x%lx-0x%lx)\n",
> +					 ret, retry_count, fault_addr,
> +					 drm_gpusvm_range_start(&range->base),
> +					 drm_gpusvm_range_end(&range->base));
> +			goto retry;
> +		} else {
> +			AMDGPU_SVM_ERR("map_fault: get_pages failed with %d, devmem fallback allowed, but no devmem pages: fault=0x%lx range=[0x%lx-0x%lx)\n",
> +					 ret, fault_addr, drm_gpusvm_range_start(&range->base),
> +					 drm_gpusvm_range_end(&range->base));
> +		}
> +	}
> +
> +	if (ret == -EPERM) {
> +		AMDGPU_SVM_ERR("get_pages -EPERM: fault=0x%lx range=[0x%lx-0x%lx)\n",
> +			       fault_addr, drm_gpusvm_range_start(&range->base),
> +				       drm_gpusvm_range_end(&range->base));
> +		return ret;
> +	}
> +
> +	if (ret) {
> +		AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT - FAIL PAGE COLLECT");
> +		goto out;
> +	}
> +
> +	AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT - GPU MAP");
> +
> +	ret = amdgpu_svm_range_update_mapping(svm, range,
> +					      range_pte_flags, attrs->flags,
> +					      false, false, false);
> +
> +	if (ret)
> +		goto err_out;
> +
> +out:
> +	return 0;
> +
> +err_out:
> +	if (ret == -EAGAIN && --retry_count > 0) {
> +		map_ctx.timeslice_ms <<= 1;
> +		AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT - RETRY GPU MAP");
> +		goto retry;
> +	}
> +
> +	return ret;
> +}
> +
> +int amdgpu_svm_handle_fault(struct amdgpu_device *adev, uint32_t pasid,
> +			    uint64_t fault_addr, uint64_t ts,
> +			    bool write_fault)
> +{
> +	struct amdgpu_svm *svm;
> +	struct amdgpu_svm_attr_range *attr_range;
> +	unsigned long attr_start_page, attr_last_page;
> +	unsigned long fault_page;
> +	uint64_t ckpt;
> +	int ret;
> +
> +	fault_addr = fault_addr << PAGE_SHIFT;
> +	fault_page = fault_addr >> PAGE_SHIFT;
> +
> +	svm = amdgpu_svm_lookup_by_pasid(adev, pasid);
> +	if (!svm) {
> +		AMDGPU_SVM_ERR("handle_fault: no SVM context for pasid %u\n", pasid);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (atomic_read(&svm->exiting)) {
> +		AMDGPU_SVM_ERR("handle_fault: SVM context is exiting for pasid %u\n", pasid);
> +		ret = -EAGAIN;
> +		goto out_put;
> +	}
> +
> +	if (!svm->xnack_enabled) {
> +		AMDGPU_SVM_ERR("handle_fault: SVM context does not have xnack enabled for pasid %u\n", pasid);
> +		ret = -EOPNOTSUPP;
> +		goto out_put;
> +	}
> +
> +	ckpt = READ_ONCE(svm->checkpoint_ts);
> +	if (ckpt != 0) {
> +		if (amdgpu_ih_ts_after_or_equal(ts, ckpt)) {
> +			AMDGPU_SVM_TRACE(
> +			"handle_fault: draining stale retry fault, drop fault 0x%llx ts=%llu checkpoint=%llu\n",
> +				fault_addr, ts, ckpt);
> +			amdgpu_gmc_filter_faults_remove(
> +				adev, fault_addr >> PAGE_SHIFT, pasid);
> +			ret = 0;
> +			goto out_put;
> +		} else {
> +			WRITE_ONCE(svm->checkpoint_ts, 0);
> +		}
> +	}
> +
> +	down_write(&svm->svm_lock);
> +
> +retry:
> +	mutex_lock(&svm->attr_tree->lock);
> +	attr_range = amdgpu_svm_attr_get_bounds_locked(svm->attr_tree,
> +						       fault_page,
> +						       &attr_start_page, &attr_last_page);
> +	mutex_unlock(&svm->attr_tree->lock);
> +	if (!attr_range) {
> +		ret = amdgpu_svm_range_get_unregistered_attrs(svm, fault_addr,
> +							      attr_start_page,
> +							      attr_last_page,
> +							      &attr_range);
> +		if (ret) {
> +			if (ret == -EFAULT)
> +				goto out_no_vma;
> +			goto out_unlock;
> +		}
> +	}
> +	ret = amdgpu_svm_range_map_fault(svm, fault_addr, attr_range,
> +					 write_fault);
> +
> +	if (ret == -EAGAIN) {
> +		AMDGPU_SVM_ERR("handle_fault: got -EAGAIN: fault=0x%llx\n",
> +			       fault_addr);
> +		amdgpu_gmc_filter_faults_remove(adev, fault_addr>>PAGE_SHIFT, pasid);
> +		goto retry;
> +	}
> +
> +	goto out_unlock;
> +
> +out_no_vma:
> +	AMDGPU_SVM_ERR("handle_fault: no VMA for fault=0x%llx (stale retry or GPU NULL deref)\n",
> +		 fault_addr);
> +	ret = 0;
> +
> +out_unlock:
> +	up_write(&svm->svm_lock);
> +
> +out_put:
> +	amdgpu_svm_put(svm);
> +	return ret;
> +}
> +
> +#endif /* CONFIG_DRM_AMDGPU_SVM */
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.h
> new file mode 100644
> index 000000000..1c8f6c15e
> --- /dev/null
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> +/*
> + * Copyright 2026 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + */
> +
> +#ifndef __AMDGPU_SVM_FAULT_H__
> +#define __AMDGPU_SVM_FAULT_H__
> +
> +#include <linux/types.h>
> +
> +struct amdgpu_device;
> +struct amdgpu_svm;
> +struct amdgpu_svm_attr_range;
> +struct amdgpu_svm_attrs;
> +
> +int amdgpu_svm_handle_fault(struct amdgpu_device *adev, uint32_t pasid,
> +			    uint64_t fault_addr, uint64_t ts,
> +			    bool write_fault);
> +
> +#endif /* __AMDGPU_SVM_FAULT_H__ */
> -- 
> 2.34.1
> 

  reply	other threads:[~2026-04-20 16:24 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-20 13:12 [RFC V3 00/12] drm/amdgpu: SVM implementation based on drm_gpusvm Honglei Huang
2026-04-20 13:12 ` [RFC V3 01/12] drm/amdgpu: define SVM UAPI for GPU shared virtual memory Honglei Huang
2026-04-20 13:12 ` [RFC V3 02/12] drm/amdgpu: introduce SVM core header and VM integration Honglei Huang
2026-04-20 13:12 ` [RFC V3 03/12] drm/amdgpu: define SVM attribute subsystem types Honglei Huang
2026-04-20 13:12 ` [RFC V3 04/12] drm/amdgpu: implement SVM attribute tree and helper functions Honglei Huang
2026-04-20 13:13 ` [RFC V3 05/12] drm/amdgpu: implement SVM attribute set, get, and clear Honglei Huang
2026-04-20 13:13 ` [RFC V3 06/12] drm/amdgpu: define SVM range types and work queue interface Honglei Huang
2026-04-20 13:13 ` [RFC V3 07/12] drm/amdgpu: implement SVM range GPU mapping core Honglei Huang
2026-04-20 13:13 ` [RFC V3 08/12] drm/amdgpu: implement SVM range notifier and GC helpers Honglei Huang
2026-04-20 13:13 ` [RFC V3 09/12] drm/amdgpu: implement SVM attribute change and invalidation callback Honglei Huang
2026-04-20 13:13 ` [RFC V3 10/12] drm/amdgpu: implement SVM initialization and lifecycle Honglei Huang
2026-04-20 13:13 ` [RFC V3 11/12] drm/amdgpu: add SVM ioctl, garbage collector, and fault handler Honglei Huang
2026-04-20 16:24   ` Matthew Brost [this message]
2026-04-20 13:13 ` [RFC V3 12/12] drm/amdgpu: integrate SVM into build system and VM fault path Honglei Huang
2026-04-21  2:31 ` [RFC V3 00/12] drm/amdgpu: SVM implementation based on drm_gpusvm Huang Rui

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=aeZTLi3yoZ1ADnDO@gsse-cloud1.jf.intel.com \
    --to=matthew.brost@intel.com \
    --cc=Alexander.Deucher@amd.com \
    --cc=Christian.Koenig@amd.com \
    --cc=Felix.Kuehling@amd.com \
    --cc=Jenny-Jing.Liu@amd.com \
    --cc=Junhua.Shen@amd.com \
    --cc=Lingshan.Zhu@amd.com \
    --cc=Oak.Zeng@amd.com \
    --cc=Philip.Yang@amd.com \
    --cc=Ray.Huang@amd.com \
    --cc=Xiaogang.Chen@amd.com \
    --cc=aliceryhl@google.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=dakr@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=honghuan.he@amd.com \
    --cc=honghuan@amd.com \
    --cc=honglei1.huang@amd.com \
    --cc=rodrigo.vivi@intel.com \
    --cc=thomas.hellstrom@linux.intel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox