From: Huang Rui <ray.huang@amd.com>
To: "Christian König" <christian.koenig@amd.com>,
"Philip Yang" <Philip.Yang@amd.com>,
"Alex Deucher" <alexander.deucher@amd.com>,
"Felix Kuehling" <felix.kuehling@amd.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Matthew Brost" <matthew.brost@intel.com>,
"Rodrigo Vivi" <rodrigo.vivi@intel.com>,
"Thomas Hellström" <thomas.hellstrom@linux.intel.com>,
"Danilo Krummrich" <dakr@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
amd-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Cc: Xiaogang Chen <xiaogang.chen@amd.com>,
Oak Zeng <Oak.Zeng@amd.com>, "Jenny Liu" <Jenny-Jing.Liu@amd.com>,
Zhu Lingshan <lingshan.zhu@amd.com>,
"Honglei Huang" <honglei1.huang@amd.com>,
Junhua Shen <Junhua.Shen@amd.com>, Yiru Ma <yiru.ma@amd.com>,
Huang Rui <ray.huang@amd.com>, Honglei Huang <honghuan@amd.com>
Subject: [PATCH v9 08/18] drm/amdgpu: implement SVM range notifier and GC helpers
Date: Tue, 4 Aug 2026 17:42:34 +0800 [thread overview]
Message-ID: <20260804094246.1719318-9-ray.huang@amd.com> (raw)
In-Reply-To: <20260804094246.1719318-1-ray.huang@amd.com>
From: Honglei Huang <honghuan@amd.com>
Add MMU notifier event handling and garbage collection infrastructure:
- amdgpu_svm_range_remove: unmap pages and remove from gpusvm
- amdgpu_svm_range_notifier_event_begin: compute the [start_page,
last_page] window from the notifier range and the gpusvm range,
then zap PTEs via amdgpu_svm_range_zap_ptes and invalidate the
GPU mapping
- amdgpu_svm_gc_enqueue: queue range for GC worker and set queue state
- amdgpu_svm_gc_add_range: mark range unmapped and enqueue for GC
- amdgpu_svm_range_notifier_event_end: DMA unmap and GC on MMU_UNMAP
- amdgpu_svm_range_invalidate_interval: cursor based PTE clearing
across notifiers/ranges; clears PTEs through
amdgpu_svm_range_zap_ptes
- amdgpu_svm_range_dequeue_locked: dequeue work item with atomic
pending state transfer
- amdgpu_svm_range_put_if_dequeued: release range ref after dequeue,
re-dispatch if new work was enqueued during processing
- amdgpu_svm_range_unqueue: locked context helper that pulls a range
off the GC list
Signed-off-by: Honglei Huang <honghuan@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c | 311 ++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h | 16 +
2 files changed, 327 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
index 335fe5a551f0f..9e348390795a6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
@@ -417,3 +417,314 @@ amdgpu_svm_range_map_attrs(struct amdgpu_svm *svm,
return 0;
}
+void amdgpu_svm_range_remove(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range,
+ struct drm_gpusvm_ctx *ctx)
+{
+ struct drm_gpusvm_range *base = &range->base;
+
+ amdgpu_svm_assert_locked(svm);
+
+ if (!base->pages.flags.unmapped && !base->pages.flags.partial_unmap)
+ drm_gpusvm_range_unmap_pages(&svm->gpusvm, base, ctx);
+
+ amdgpu_svm_range_invalidate_gpu_mapping(range);
+ drm_gpusvm_range_remove(&svm->gpusvm, base);
+}
+
+/**
+ * amdgpu_svm_range_notifier_event_begin() - First MMU notifier phase
+ * @svm: The SVM context.
+ * @range: The GPU SVM range covered by the notifier event.
+ * @mmu_range: The MMU notifier range describing the invalidation window.
+ *
+ * Invalidate the GPU view of @range over the notifier window: zap the
+ * affected PTEs and clear the gpu_mapped flag. The range is not removed
+ * here; structural removal if needed happens in the event-end phase.
+ *
+ * Return: true if PTEs were zapped, false otherwise.
+ */
+bool
+amdgpu_svm_range_notifier_event_begin(struct amdgpu_svm *svm,
+ struct drm_gpusvm_range *range,
+ const struct mmu_notifier_range *mmu_range)
+{
+ struct amdgpu_svm_range *svm_range = to_amdgpu_svm_range(range);
+ unsigned long start_page, last_page;
+
+ amdgpu_svm_assert_in_notifier(svm);
+
+ AMDGPU_SVM_RANGE_TRACE(svm_range, "NOTIFIER");
+
+ if (range->pages.flags.unmapped || !svm_range->gpu_mapped)
+ return false;
+
+ AMDGPU_SVM_RANGE_TRACE(svm_range, "NOTIFIER - EXECUTE");
+
+ start_page = max(drm_gpusvm_range_start(range),
+ mmu_range->start) >> PAGE_SHIFT;
+ last_page = (min(drm_gpusvm_range_end(range),
+ mmu_range->end) >> PAGE_SHIFT) - 1;
+
+ amdgpu_svm_range_zap_ptes(svm, svm_range, start_page, last_page);
+ amdgpu_svm_range_invalidate_gpu_mapping(svm_range);
+
+ return true;
+}
+
+/**
+ * amdgpu_svm_gc_enqueue() - Queue a range for work queue processing
+ * @svm: The SVM context.
+ * @range: The SVM range to queue.
+ * @start_page: First page of the pending invalidation interval.
+ * @last_page: Last page of the pending invalidation interval.
+ *
+ * Queue @range on the GC list and kick the GC worker. A range reference
+ * is taken when it first enters IN_GC and dropped only when it returns
+ * to NOT_QUEUED, so the range stays alive while on the GC list or being
+ * processed. All transitions are serialized by svm->work_lock.
+ */
+static void
+amdgpu_svm_gc_enqueue(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range,
+ unsigned long start_page, unsigned long last_page)
+{
+ if (atomic_read(&svm->exiting))
+ return;
+
+ spin_lock(&svm->work_lock);
+ if (range->queue_state == AMDGPU_SVM_RANGE_NOT_QUEUED) {
+ drm_gpusvm_range_get(&range->base);
+ range->queue_state = AMDGPU_SVM_RANGE_IN_GC;
+ }
+
+ range->pending_start_page = min(range->pending_start_page, start_page);
+ range->pending_last_page = max(range->pending_last_page, last_page);
+ if (range->pending_ops == AMDGPU_SVM_RANGE_OP_NONE)
+ list_add_tail(&range->work_node, &svm->gc.list);
+ range->pending_ops |= AMDGPU_SVM_RANGE_OP_UNMAP;
+ spin_unlock(&svm->work_lock);
+
+ queue_work(svm->gc.wq, &svm->gc.work);
+}
+
+static void
+amdgpu_svm_gc_add_range(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *svm_range,
+ const struct mmu_notifier_range *mmu_range)
+{
+ unsigned long start_page = max(drm_gpusvm_range_start(&svm_range->base),
+ mmu_range->start) >> PAGE_SHIFT;
+ unsigned long last_page = (min(drm_gpusvm_range_end(&svm_range->base),
+ mmu_range->end) >> PAGE_SHIFT) - 1;
+
+ AMDGPU_SVM_RANGE_TRACE(svm_range, "GARBAGE COLLECTOR ADD");
+
+ drm_gpusvm_range_set_unmapped(&svm_range->base, mmu_range);
+ amdgpu_svm_gc_enqueue(svm, svm_range, start_page, last_page);
+}
+
+/**
+ * amdgpu_svm_range_notifier_event_end() - Second MMU notifier phase
+ * @svm: The SVM context.
+ * @range: The GPU SVM range covered by the notifier event.
+ * @mmu_range: The MMU notifier range describing the event.
+ *
+ * DMA-unmap the pages for @range. On an actual unmap event, also queue the
+ * range to the GC so it gets structurally removed from the gpusvm trees.
+ */
+static void
+amdgpu_svm_range_notifier_event_end(struct amdgpu_svm *svm,
+ struct drm_gpusvm_range *range,
+ const struct mmu_notifier_range *mmu_range)
+{
+ struct drm_gpusvm_ctx ctx = { .in_notifier = true, };
+
+ amdgpu_svm_assert_in_notifier(svm);
+
+ drm_gpusvm_range_unmap_pages(&svm->gpusvm, range, &ctx);
+ if (mmu_range->event == MMU_NOTIFY_UNMAP)
+ amdgpu_svm_gc_add_range(svm, to_amdgpu_svm_range(range),
+ mmu_range);
+}
+
+int
+amdgpu_svm_range_invalidate_interval(struct amdgpu_svm *svm,
+ unsigned long start_page,
+ unsigned long last_page)
+{
+ unsigned long start = start_page << PAGE_SHIFT;
+ unsigned long end = (last_page + 1) << PAGE_SHIFT;
+ struct drm_gpusvm_notifier *notifier, *next_notifier;
+ struct drm_gpusvm_ctx ctx = { .in_notifier = false };
+ struct drm_exec exec;
+ bool needs_flush = false;
+ int ret;
+
+ amdgpu_svm_assert_locked(svm);
+
+ ret = amdgpu_svm_range_lock_vm_pd(svm, &exec, true);
+ if (ret)
+ return ret;
+
+ drm_gpusvm_for_each_notifier_safe(notifier, next_notifier, &svm->gpusvm,
+ start, end) {
+ struct drm_gpusvm_range *range, *next_range;
+
+ drm_gpusvm_for_each_range_safe(range, next_range, notifier,
+ start, end) {
+ struct amdgpu_svm_range *svm_range = to_amdgpu_svm_range(range);
+ bool crosses_boundary = start > drm_gpusvm_range_start(range) ||
+ end < drm_gpusvm_range_end(range);
+
+ if (svm_range->gpu_mapped) {
+ AMDGPU_SVM_RANGE_TRACE(svm_range,
+ crosses_boundary ? "ATTR DESTROY" :
+ "ATTR ZAP PTE");
+
+ ret = amdgpu_svm_range_zap_ptes(svm, svm_range,
+ drm_gpusvm_range_start(range) >> PAGE_SHIFT,
+ (drm_gpusvm_range_end(range) >> PAGE_SHIFT) - 1);
+ if (ret < 0) {
+ drm_exec_fini(&exec);
+ return ret;
+ }
+
+ amdgpu_svm_range_invalidate_gpu_mapping(svm_range);
+ needs_flush = true;
+ }
+
+ if (crosses_boundary) {
+ /* remove ranges crossing the boundary so GPU fault
+ * creates new ranges bounded by the updated
+ * attr_range boundaries.
+ * Evict devmem-backed pages back to sysmem first
+ * so VRAM-resident data is not lost when the range
+ * is destroyed. No-op for sysmem-only ranges.
+ */
+ drm_gpusvm_range_get(range);
+
+ amdgpu_svm_range_evict(svm, range);
+ amdgpu_svm_range_remove(svm, svm_range, &ctx);
+
+ amdgpu_svm_range_unqueue(svm, svm_range);
+
+ drm_gpusvm_range_put(range);
+ }
+ }
+ }
+
+ drm_exec_fini(&exec);
+
+ if (needs_flush)
+ amdgpu_svm_flush_tlb(svm);
+
+ AMDGPU_SVM_DBG("attr invalidate done [0x%lx-0x%lx]-0x%lx needs_flush=%d\n",
+ start_page, last_page, last_page - start_page + 1,
+ needs_flush ? 1 : 0);
+
+ return 0;
+}
+
+/**
+ * amdgpu_svm_range_dequeue_locked() - Pull the next range off a work list
+ * @svm: The SVM context.
+ * @work_list: The work list to dequeue from.
+ * @op_ctx: Output context filled with the dequeued range's pending state.
+ *
+ * Move the first range on @work_list to PROCESSING and atomically snapshot
+ * its pending op/interval into @op_ctx while resetting them on the range, so
+ * invalidations that arrive during processing accumulate afresh. Caller must
+ * hold svm->work_lock.
+ *
+ * Return: true if a range was dequeued, false if @work_list was empty.
+ */
+bool
+amdgpu_svm_range_dequeue_locked(struct amdgpu_svm *svm,
+ struct list_head *work_list,
+ struct amdgpu_svm_range_op_ctx *op_ctx)
+{
+ struct amdgpu_svm_range *range;
+
+ lockdep_assert_held(&svm->work_lock);
+
+ range = list_first_entry_or_null(work_list, struct amdgpu_svm_range,
+ work_node);
+ if (!range)
+ return false;
+
+ list_del_init(&range->work_node);
+ range->queue_state = AMDGPU_SVM_RANGE_PROCESSING;
+
+ op_ctx->range = range;
+ op_ctx->start_page = range->pending_start_page;
+ op_ctx->last_page = range->pending_last_page;
+ op_ctx->pending_ops = range->pending_ops;
+
+ range->pending_start_page = ULONG_MAX;
+ range->pending_last_page = 0;
+ range->pending_ops = AMDGPU_SVM_RANGE_OP_NONE;
+
+ return true;
+}
+
+/**
+ * amdgpu_svm_range_put_if_dequeued() - Finish processing a dequeued range
+ * @svm: The SVM context.
+ * @range: The range previously moved to PROCESSING.
+ *
+ * Called after a PROCESSING range has been handled. If new UNMAP work was
+ * enqueued while it was being processed, re-queue it on the GC list and kick
+ * the worker again; otherwise return it to NOT_QUEUED and drop the reference
+ * taken at enqueue time.
+ */
+void
+amdgpu_svm_range_put_if_dequeued(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range)
+{
+ bool release_kref = false;
+ bool queue_gc = false;
+
+ spin_lock(&svm->work_lock);
+
+ if (range->queue_state != AMDGPU_SVM_RANGE_PROCESSING) {
+ spin_unlock(&svm->work_lock);
+ return;
+ }
+
+ if (range->pending_ops & AMDGPU_SVM_RANGE_OP_UNMAP) {
+ list_add_tail(&range->work_node, &svm->gc.list);
+ range->queue_state = AMDGPU_SVM_RANGE_IN_GC;
+ queue_gc = true;
+ } else {
+ range->queue_state = AMDGPU_SVM_RANGE_NOT_QUEUED;
+ release_kref = true;
+ }
+
+ spin_unlock(&svm->work_lock);
+
+ if (queue_gc)
+ queue_work(svm->gc.wq, &svm->gc.work);
+ if (release_kref)
+ drm_gpusvm_range_put(&range->base);
+}
+
+void amdgpu_svm_range_unqueue(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range)
+{
+ bool put = false;
+
+ amdgpu_svm_assert_locked(svm);
+
+ spin_lock(&svm->work_lock);
+ if (range->queue_state == AMDGPU_SVM_RANGE_IN_GC) {
+ list_del_init(&range->work_node);
+ range->queue_state = AMDGPU_SVM_RANGE_NOT_QUEUED;
+ range->pending_ops = AMDGPU_SVM_RANGE_OP_NONE;
+ put = true;
+ }
+ spin_unlock(&svm->work_lock);
+
+ if (put)
+ drm_gpusvm_range_put(&range->base);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h
index e5a5f52166576..f423be277a85a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h
@@ -149,6 +149,16 @@ int amdgpu_svm_range_update_mapping(struct amdgpu_svm *svm,
bool read_only,
bool intr, bool wait,
bool flush_tlb);
+bool amdgpu_svm_range_dequeue_locked(struct amdgpu_svm *svm,
+ struct list_head *work_list,
+ struct amdgpu_svm_range_op_ctx *op_ctx);
+void amdgpu_svm_range_put_if_dequeued(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range);
+void amdgpu_svm_range_unqueue(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range);
+void amdgpu_svm_range_remove(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range,
+ struct drm_gpusvm_ctx *ctx);
int amdgpu_svm_range_map_attrs(struct amdgpu_svm *svm,
const struct amdgpu_svm_attrs *attrs,
unsigned long start, unsigned long end);
@@ -165,5 +175,11 @@ amdgpu_svm_range_find_or_insert(struct amdgpu_svm *svm, unsigned long addr,
int amdgpu_svm_range_get_pages(struct amdgpu_svm *svm,
struct drm_gpusvm_range *range,
struct drm_gpusvm_ctx *ctx);
+bool amdgpu_svm_range_notifier_event_begin(struct amdgpu_svm *svm,
+ struct drm_gpusvm_range *range,
+ const struct mmu_notifier_range *mmu_range);
+int amdgpu_svm_range_invalidate_interval(struct amdgpu_svm *svm,
+ unsigned long start_page,
+ unsigned long last_page);
#endif /* __AMDGPU_SVM_RANGE_H__ */
--
2.53.0
next prev parent reply other threads:[~2026-08-04 9:43 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 9:42 [PATCH v9 00/18] drm/amdgpu: AMDGPU SVM support based on DRM (Phase 1: single GPU, XNACK on) Huang Rui
2026-08-04 9:42 ` [PATCH v9 01/18] drm/amdgpu: add SVM ioctl UAPI definitions Huang Rui
2026-08-11 10:58 ` Christian König
2026-08-11 13:42 ` Huang, Honglei
2026-08-04 9:42 ` [PATCH v9 02/18] drm/amdgpu: add SVM core header and VM integration Huang Rui
2026-08-11 11:02 ` Christian König
2026-08-11 14:06 ` Huang, Honglei
2026-08-12 8:36 ` Christian König
2026-08-12 9:55 ` Huang, Honglei
2026-08-12 12:16 ` Christian König
2026-08-12 13:36 ` Huang Rui
2026-08-04 9:42 ` [PATCH v9 03/18] drm/amdgpu: implement SVM attribute tree and helper functions Huang Rui
2026-08-04 9:42 ` [PATCH v9 04/18] drm/amdgpu: implement SVM attribute set/get/clear operations Huang Rui
2026-08-04 9:42 ` [PATCH v9 05/18] drm/amdgpu: add SVM range types and work queue interface Huang Rui
2026-08-04 9:42 ` [PATCH v9 06/18] drm/amdgpu/gmc: add get_svm_pte_flags callback Huang Rui
2026-08-04 9:42 ` [PATCH v9 07/18] drm/amdgpu: implement SVM range GPU mapping core Huang Rui
2026-08-04 9:42 ` Huang Rui [this message]
2026-08-04 9:42 ` [PATCH v9 09/18] drm/amdgpu: add SVM notifier invalidate callback and checkpoint Huang Rui
2026-08-04 9:42 ` [PATCH v9 10/18] drm/amdgpu: implement SVM initialization and lifecycle Huang Rui
2026-08-04 9:42 ` [PATCH v9 11/18] drm/amdgpu: add SVM ioctl entry and fault handler module Huang Rui
2026-08-04 9:42 ` [PATCH v9 12/18] drm/amdgpu: integrate SVM into build system and VM fault path Huang Rui
2026-08-04 9:42 ` [PATCH v9 13/18] drm/amdgpu: add VRAM migration infrastructure for drm_pagemap Huang Rui
2026-08-04 9:42 ` [PATCH v9 14/18] drm/amdgpu: implement drm_pagemap SDMA migration callbacks Huang Rui
2026-08-04 9:42 ` [PATCH v9 15/18] drm/amdgpu: implement synchronous TTM eviction for SVM BOs Huang Rui
2026-08-04 9:42 ` [PATCH v9 16/18] drm/amdgpu: hook up ZONE_DEVICE registration in device init and reset Huang Rui
2026-08-04 9:42 ` [PATCH v9 17/18] drm/amdgpu: add SVM range migration helpers for drm_pagemap Huang Rui
2026-08-04 9:42 ` [PATCH v9 18/18] drm/amdgpu: integrate VRAM migration into SVM fault and prefetch paths 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=20260804094246.1719318-9-ray.huang@amd.com \
--to=ray.huang@amd.com \
--cc=Jenny-Jing.Liu@amd.com \
--cc=Junhua.Shen@amd.com \
--cc=Oak.Zeng@amd.com \
--cc=Philip.Yang@amd.com \
--cc=alexander.deucher@amd.com \
--cc=aliceryhl@google.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=christian.koenig@amd.com \
--cc=dakr@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=felix.kuehling@amd.com \
--cc=honghuan@amd.com \
--cc=honglei1.huang@amd.com \
--cc=lingshan.zhu@amd.com \
--cc=matthew.brost@intel.com \
--cc=rodrigo.vivi@intel.com \
--cc=simona@ffwll.ch \
--cc=thomas.hellstrom@linux.intel.com \
--cc=xiaogang.chen@amd.com \
--cc=yiru.ma@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.