* [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off)
@ 2026-06-05 7:53 Junhua Shen
2026-06-05 7:53 ` [PATCH v5 1/8] drm/amdgpu: add VRAM migration infrastructure for drm_pagemap Junhua Shen
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
This series adds VRAM migration support to amdgpu's SVM (Shared Virtual
Memory) implementation, using the drm_pagemap framework for ZONE_DEVICE
page management and SDMA for data migration.
This v5 is the unified version supporting both XNACK-on (GPU fault-driven)
and XNACK-off (ioctl-driven / restore-based) migration paths, built on top
of the drm_gpusvm-based amdgpu SVM core [1] and xnack-off restore
infrastructure [2]. Previous v1/v2/v3 were XNACK-off only; v4 was XNACK-on
only; this v5 merges both paths into a single unified series.
The implementation follows the Xe driver's approach for TTM eviction,
using synchronous bo_move to migrate device-private pages back to system
RAM when TTM needs to evict SVM BOs.
Key design points:
- GPU VRAM registered as ZONE_DEVICE via devm_memremap_pages(),
wrapped in struct amdgpu_pagemap with drm_pagemap state
- SDMA-based data transfer through GART aperture window for both
copy_to_devmem and copy_to_ram callbacks
- amdgpu_bo_svm: lightweight BO subtype with drm_pagemap_devmem for
ZONE_DEVICE page ownership tracking
- Synchronous TTM eviction via drm_pagemap_evict_to_ram() in bo_move,
following the Xe pattern (no eviction fences needed)
- Migration policy driven by SVM range attributes (preferred location,
prefetch hints) for both XNACK-on fault path and XNACK-off restore path
- XNACK-off integration: migration triggered in restore worker and
attr-change boundary realign paths
Limitations:
- Single GPU only; multi-GPU migration is not addressed
- No VRAM-to-VRAM (peer GPU) migration
Open issue:
- Unnecessary TTM system memory allocation during eviction: when TTM
evicts an SVM BO, it allocates a destination system memory resource
(TTM_PL_SYSTEM) before calling bo_move, then frees it afterwards.
This allocation is unnecessary because the actual data migration is
done via drm_pagemap_evict_to_ram() → migrate_device_* which
migrates device-private pages directly to regular system pages,
bypassing the TTM-allocated resource entirely. The current TTM
framework does not support num_placement=0 to skip this redundant
allocation; this needs further discussion.
Dependencies:
This series applies on top of:
[1] amdgpu drm_gpusvm SVM core (xnack-on):
https://lore.kernel.org/amd-gfx/20260529054928.596825-1-honglei1.huang@amd.com/
[2] amdgpu drm_gpusvm SVM xnack-off restore:
https://lore.kernel.org/amd-gfx/20260529054928.596825-1-honglei1.huang@amd.com/
Changes since v4:
- Unified XNACK-on and XNACK-off migration into a single series
(v4 was XNACK-on only)
- Added patch 6/8: refactor SVM attr devmem_possible and prefer_vram
API for cleaner integration with both paths
- Added patch 8/8: integrate VRAM migration into XNACK-off SVM restore
and attr-change boundary realign paths
- Split the integration patch into two: fault/prefetch path (7/8) and
restore/realign path (8/8) for better reviewability
- Rebased on latest SVM core with xnack-off restore support
Changes since v3:
- Rebased on drm_gpusvm-based amdgpu SVM core [1], switching from
XNACK-off ioctl-driven to XNACK-on GPU fault-driven migration
- Introduced amdgpu_bo_svm subtype with drm_pagemap_devmem embedding
and two-layer reference counting (GEM refcount + TTM kref)
- Added synchronous TTM eviction via drm_pagemap_evict_to_ram() in
amdgpu_bo_move(), following the Xe driver pattern
- Added amdgpu_bo_is_amdgpu_bo() check for SVM BOs in TTM path
- Cleaned up container_of macros to follow amdgpu conventions
(to_amdgpu_bo_svm as #define, devmem_to_amdgpu_bo_svm as inline)
Changes since v2:
- Moved amdgpu_pagemap entirely to amdgpu side, eliminating all KFD
modifications
- Split commits for better reviewability: separated infrastructure
from SDMA callbacks, decision layer from integration
- Merged ZONE_DEVICE registration hook into the integration patch
Changes since v1:
- Dropped the eviction fence patch (was 4/6) after Christian König
pointed out it violates the dma_fence contract
- Refactored migration integration: extracted migration logic into
new files amdgpu_svm_range_migrate.{c,h}
- Introduced enum amdgpu_svm_migrate_mode (PREFERRED, TO_VRAM,
TO_SYSMEM, NONE) to make migration intent explicit, replacing
the _ex functions used in v1
Previous versions:
v1 (XNACK-off): https://lore.kernel.org/amd-gfx/20260410113146.146212-1-Junhua.Shen@amd.com/
v2 (XNACK-off): https://lore.kernel.org/amd-gfx/20260413103031.181953-1-Junhua.Shen@amd.com/
v3 (XNACK-off): https://lore.kernel.org/amd-gfx/20260427100522.7014-1-Junhua.Shen@amd.com/
v4 (XNACK-on): https://lore.kernel.org/amd-gfx/20260513085725.163786-1-Junhua.Shen@amd.com/
Test results:
Tested on gfx943 (MI300X) and gfx906 (MI60) with both XNACK on and off:
- KFD test: 95%+ passed (both modes).
- ROCR test: 98%+ passed (both modes).
Patch overview:
1/8 Core VRAM migration infrastructure (ZONE_DEVICE registration,
amdgpu_pagemap, amdgpu_bo_svm subtype, drm_pagemap_ops)
2/8 SDMA migration callbacks (copy_to_devmem, copy_to_ram,
populate_devmem_pfn via GART aperture window)
3/8 Synchronous TTM eviction for SVM BOs (amdgpu_svm_bo_evict
in bo_move path, amdgpu_bo_is_amdgpu_bo check)
4/8 Hook up ZONE_DEVICE registration in device init and GPU reset
5/8 SVM range migration helpers (range-level migrate_to_vram /
migrate_to_sysmem decision layer)
6/8 Refactor SVM attr devmem_possible and prefer_vram API for
unified xnack-on/off usage
7/8 Wire up VRAM migration into SVM fault and prefetch paths
(XNACK-on)
8/8 Wire up VRAM migration into SVM restore and attr-change
realign paths (XNACK-off)
Junhua Shen (8):
drm/amdgpu: add VRAM migration infrastructure for drm_pagemap
drm/amdgpu: implement drm_pagemap SDMA migration callbacks
drm/amdgpu: implement synchronous TTM eviction for SVM BOs
drm/amdgpu: hook up ZONE_DEVICE registration in device init and reset
drm/amdgpu: add SVM range migration helpers for drm_pagemap
drm/amdgpu: refactor SVM attr devmem_possible and prefer_vram API
drm/amdgpu: integrate VRAM migration into SVM fault and prefetch paths
drm/amdgpu: integrate VRAM migration into SVM restore and realign paths
drivers/gpu/drm/amd/amdgpu/Makefile | 8 +-
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 8 +
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c | 831 +++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h | 102 +++
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 4 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c | 2 +
drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c | 12 +
drivers/gpu/drm/amd/amdgpu/amdgpu_svm.h | 4 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c | 18 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.h | 5 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c | 20 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c | 32 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h | 1 +
.../gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.c | 115 +++
.../gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.h | 35 +
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 20 +
drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c | 85 ++-
18 files changed, 1244 insertions(+), 60 deletions(-)
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.c
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.h
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v5 1/8] drm/amdgpu: add VRAM migration infrastructure for drm_pagemap
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 2/8] drm/amdgpu: implement drm_pagemap SDMA migration callbacks Junhua Shen
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
Add the drm_pagemap-based VRAM migration infrastructure:
- Define struct amdgpu_pagemap wrapping dev_pagemap + drm_pagemap
- Define AMDGPU_PGMAP_OWNER() and AMDGPU_INTERCONNECT_VRAM macros
- Implement amdgpu_svm_migration_init() to register ZONE_DEVICE via
devm_memremap_pages() and initialize the drm_pagemap
- Add amdgpu_pagemap pointer (apagemap) to struct amdgpu_device
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/Makefile | 8 +-
drivers/gpu/drm/amd/amdgpu/amdgpu.h | 8 +
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c | 179 ++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h | 87 ++++++++++
4 files changed, 279 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index b15a8e23e5f3..08207cad310d 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -328,12 +328,14 @@ amdgpu-$(CONFIG_HMM_MIRROR) += amdgpu_hmm.o
# svm support
amdgpu-$(CONFIG_DRM_AMDGPU_SVM) += amdgpu_svm.o amdgpu_svm_attr.o \
- amdgpu_svm_fault.o amdgpu_svm_range.o amdgpu_userptr.o
+ amdgpu_svm_fault.o amdgpu_svm_range.o amdgpu_userptr.o amdgpu_migrate.o
.PHONY: clean-svm
clean-svm:
- rm -f $(obj)/amdgpu_svm.o $(obj)/amdgpu_svm_attr.o $(obj)/amdgpu_svm_fault.o $(obj)/amdgpu_svm_range.o $(obj)/amdgpu_userptr.o \
- $(obj)/.amdgpu_svm.o.cmd $(obj)/.amdgpu_svm_attr.o.cmd $(obj)/.amdgpu_svm_fault.o.cmd $(obj)/.amdgpu_svm_range.o.cmd $(obj)/.amdgpu_userptr.o.cmd
+ rm -f $(obj)/amdgpu_svm.o $(obj)/amdgpu_svm_attr.o $(obj)/amdgpu_svm_fault.o $(obj)/amdgpu_svm_range.o \
+ $(obj)/amdgpu_userptr.o $(obj)/amdgpu_migrate.o \
+ $(obj)/.amdgpu_svm.o.cmd $(obj)/.amdgpu_svm_attr.o.cmd $(obj)/.amdgpu_svm_fault.o.cmd $(obj)/.amdgpu_svm_range.o.cmd \
+ $(obj)/.amdgpu_userptr.o.cmd $(obj)/.amdgpu_migrate.o.cmd
include $(FULL_AMD_PATH)/pm/Makefile
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 5d7bfa59424a..aece773037d3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -326,6 +326,7 @@ struct amdgpu_fpriv;
struct amdgpu_bo_va_mapping;
struct kfd_vm_fault_info;
struct amdgpu_hive_info;
+struct amdgpu_pagemap;
struct amdgpu_reset_context;
struct amdgpu_reset_control;
struct amdgpu_coredump_info;
@@ -1158,6 +1159,13 @@ struct amdgpu_device {
struct amdgpu_uma_carveout_info uma_info;
+#if IS_ENABLED(CONFIG_DRM_AMDGPU_SVM)
+ /* SVM VRAM migration via drm_pagemap (drm_gpusvm path).
+ * Allocated in amdgpu_svm_migration_init(), NULL if SVM disabled.
+ */
+ struct amdgpu_pagemap *apagemap;
+#endif
+
/* KFD
* Must be last --ends in a flexible-array member.
*/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
new file mode 100644
index 000000000000..6b40f7477793
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
@@ -0,0 +1,179 @@
+// 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.
+ *
+ */
+
+/**
+ * DOC: AMDGPU SVM Migration
+ *
+ * This file implements the drm_pagemap-based migration infrastructure for
+ * AMDGPU SVM. It provides the callbacks that the DRM GPUSVM / drm_pagemap
+ * framework needs to:
+ *
+ * 1. Map ZONE_DEVICE pages to GPU-visible VRAM MC addresses (device_map)
+ * 2. Allocate VRAM and migrate pages from system memory (populate_mm)
+ * 3. Copy data between RAM and VRAM using SDMA (copy_to_devmem / copy_to_ram)
+ * 4. Release VRAM backing when pages migrate back to system memory (devmem_release)
+ *
+ * Architecture overview::
+ *
+ * adev->apagemap->dpagemap (struct drm_pagemap)
+ * .ops = &amdgpu_svm_drm_pagemap_ops
+ * |
+ * +---+-------------------+
+ * | |
+ * .populate_mm .device_map
+ * (alloc BO + migrate) (page -> VRAM MC addr)
+ * |
+ * v
+ * drm_pagemap_devmem_ops (per-BO migration mechanics)
+ * .populate_devmem_pfn -> BO buddy blocks -> PFN array
+ * .copy_to_devmem -> SDMA copy RAM -> VRAM
+ * .copy_to_ram -> SDMA copy VRAM -> RAM
+ * .devmem_release -> release BO reference
+ *
+ * The three address spaces involved:
+ *
+ * VRAM offset [0, real_vram_size) - buddy allocator managed
+ * + hpa_base
+ * HPA / PFN [hpa_base, hpa_base+..) - ZONE_DEVICE struct page management
+ * + vm_manager.vram_base_offset
+ * PTE address [vram_base_offset, ..] - GPU page table entries (from MMHUB FB_OFFSET)
+ */
+
+#include <drm/drm_pagemap.h>
+#include <linux/memremap.h>
+#include <linux/migrate.h>
+
+#include "amdgpu_amdkfd.h"
+#include "amdgpu_migrate.h"
+#include "amdgpu.h"
+
+static inline struct amdgpu_pagemap *
+dpagemap_to_apagemap(struct drm_pagemap *dpagemap)
+{
+ return container_of(dpagemap, struct amdgpu_pagemap, dpagemap);
+}
+
+static inline struct amdgpu_device *
+dpagemap_to_adev(struct drm_pagemap *dpagemap)
+{
+ return drm_to_adev(dpagemap->drm);
+}
+
+/**
+ * amdgpu_svm_page_to_apagemap - Get amdgpu_pagemap from a ZONE_DEVICE page
+ * @page: A ZONE_DEVICE page backed by VRAM
+ *
+ * Follows: page -> pgmap -> container_of(apagemap)
+ */
+static inline struct amdgpu_pagemap *
+amdgpu_svm_page_to_apagemap(struct page *page)
+{
+ struct dev_pagemap *pgmap = page_pgmap(page);
+
+ return container_of(pgmap, struct amdgpu_pagemap, pgmap);
+}
+
+
+const struct drm_pagemap_ops amdgpu_svm_drm_pagemap_ops = { };
+
+/**
+ * amdgpu_svm_migration_init - Register ZONE_DEVICE and initialize drm_pagemap
+ * @adev: AMDGPU device to set up VRAM migration for
+ *
+ * Allocates a ZONE_DEVICE region covering the GPU's VRAM, registers it
+ * via devm_memremap_pages() with drm_pagemap's generic dev_pagemap_ops,
+ * and then initializes the drm_pagemap (dpagemap) that provides the
+ * device_map / populate_mm callbacks for the DRM GPUSVM migration path.
+ *
+ * For XGMI-connected CPUs, uses the device's aperture directly
+ * (MEMORY_DEVICE_COHERENT). For discrete GPUs, requests a free
+ * iomem region for MEMORY_DEVICE_PRIVATE pages.
+ *
+ * Return: 0 on success, -EINVAL if GPU IP too old, negative error on failure
+ */
+int amdgpu_svm_migration_init(struct amdgpu_device *adev)
+{
+ struct amdgpu_pagemap *svm_dm;
+ struct dev_pagemap *pgmap;
+ struct resource *res = NULL;
+ unsigned long size;
+ void *r;
+
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) < IP_VERSION(9, 0, 1))
+ return -EINVAL;
+
+ if (adev->apu_prefer_gtt)
+ return 0;
+
+ if (adev->apagemap && adev->apagemap->initialized)
+ return 0;
+
+ svm_dm = devm_kzalloc(adev->dev, sizeof(*svm_dm), GFP_KERNEL);
+ if (!svm_dm)
+ return -ENOMEM;
+
+ pgmap = &svm_dm->pgmap;
+
+ size = ALIGN(adev->gmc.real_vram_size, 2ULL << 20);
+ if (adev->gmc.xgmi.connected_to_cpu) {
+ pgmap->range.start = adev->gmc.aper_base;
+ pgmap->range.end = adev->gmc.aper_base + adev->gmc.aper_size - 1;
+ pgmap->type = MEMORY_DEVICE_COHERENT;
+ } else {
+ res = devm_request_free_mem_region(adev->dev, &iomem_resource, size);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+ pgmap->range.start = res->start;
+ pgmap->range.end = res->end;
+ pgmap->type = MEMORY_DEVICE_PRIVATE;
+ }
+
+ pgmap->nr_range = 1;
+ pgmap->flags = 0;
+ pgmap->ops = drm_pagemap_pagemap_ops_get();
+ pgmap->owner = AMDGPU_PGMAP_OWNER(adev);
+
+ r = devm_memremap_pages(adev->dev, pgmap);
+ if (IS_ERR(r)) {
+ dev_err(adev->dev, "SVM: failed to register HMM device memory\n");
+ if (pgmap->type == MEMORY_DEVICE_PRIVATE && res)
+ devm_release_mem_region(adev->dev, res->start, resource_size(res));
+ pgmap->type = 0;
+ return PTR_ERR(r);
+ }
+
+ if (drm_pagemap_init(&svm_dm->dpagemap, pgmap, adev_to_drm(adev),
+ &amdgpu_svm_drm_pagemap_ops)) {
+ dev_err(adev->dev, "SVM: failed to init drm_pagemap\n");
+ return -EINVAL;
+ }
+ svm_dm->adev = adev;
+ svm_dm->hpa_base = pgmap->range.start;
+ svm_dm->initialized = true;
+ adev->apagemap = svm_dm;
+
+ dev_info(adev->dev, "SVM: registered %ldMB device memory, hpa_base=0x%llx\n",
+ size >> 20, svm_dm->hpa_base);
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
new file mode 100644
index 000000000000..af42bd997d49
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
@@ -0,0 +1,87 @@
+/* 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_MIGRATE_H__
+#define __AMDGPU_MIGRATE_H__
+
+#include <drm/drm_pagemap.h>
+#include <linux/memremap.h>
+
+struct amdgpu_device;
+
+/*
+ * AMDGPU_PGMAP_OWNER - Unique owner token for dev_pagemap registration.
+ *
+ * migrate_vma_setup() uses pgmap->owner to distinguish "own" device pages
+ * from "foreign" device pages (e.g., another GPU in an XGMI hive).
+ * Pages whose page->pgmap->owner matches the migration source are skipped
+ * (they're already in the right place).
+ *
+ * For XGMI hive: all GPUs in the hive share the same owner (the hive pointer)
+ * so intra-hive pages are treated as local.
+ * For standalone GPU: use the adev pointer itself as a unique per-device token.
+ */
+#define AMDGPU_PGMAP_OWNER(adev) \
+ ((adev)->hive ? (void *)(adev)->hive : (void *)(adev))
+
+/**
+ * struct amdgpu_pagemap - VRAM migration infrastructure for drm_pagemap
+ * @dpagemap: DRM pagemap wrapper providing device_map/populate_mm callbacks
+ * @adev: back-pointer to the owning amdgpu_device
+ * @hpa_base: host physical address base of the ZONE_DEVICE region
+ * @initialized: set to true after successful registration
+ * @pgmap: the dev_pagemap registered with devm_memremap_pages();
+ * must be last — contains a flexible-array member (ranges[])
+ *
+ * Allocated with devm_kzalloc() in amdgpu_svm_migration_init() and stored
+ * as adev->apagemap. Lifetime is tied to the device via devres.
+ */
+struct amdgpu_pagemap {
+ struct drm_pagemap dpagemap;
+ struct amdgpu_device *adev;
+ resource_size_t hpa_base;
+ bool initialized;
+ struct dev_pagemap pgmap; /* must be last — flex-array */
+};
+
+#if IS_ENABLED(CONFIG_DRM_AMDGPU_SVM)
+int amdgpu_svm_migration_init(struct amdgpu_device *adev);
+#else
+static inline
+int amdgpu_svm_migration_init(struct amdgpu_device *adev)
+{
+ return 0;
+}
+#endif
+
+/**
+ * amdgpu_svm_drm_pagemap_ops - drm_pagemap_ops for AMDGPU VRAM migration
+ *
+ * Provides:
+ * .device_map - Convert ZONE_DEVICE page to VRAM address
+ * .populate_mm - Allocate VRAM BO and migrate pages from system memory
+ */
+extern const struct drm_pagemap_ops amdgpu_svm_drm_pagemap_ops;
+
+#endif /* __AMDGPU_MIGRATE_H__ */
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 2/8] drm/amdgpu: implement drm_pagemap SDMA migration callbacks
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
2026-06-05 7:53 ` [PATCH v5 1/8] drm/amdgpu: add VRAM migration infrastructure for drm_pagemap Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 3/8] drm/amdgpu: implement synchronous TTM eviction for SVM BOs Junhua Shen
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
Implement the drm_pagemap_devmem_ops and drm_pagemap_ops callbacks
that the DRM GPUSVM migration framework requires.
Introduce struct amdgpu_bo_svm as a BO subtype (following the
amdgpu_bo_user/amdgpu_bo_vm pattern) that embeds struct amdgpu_bo
and carries a drm_pagemap_devmem allocation.
drm_pagemap_ops (top-level entry points):
- device_map: convert ZONE_DEVICE page to GPU PTE address
- populate_mm: allocate amdgpu_bo_svm and trigger migration
drm_pagemap_devmem_ops (per-BO migration mechanics):
- populate_devmem_pfn: walk BO buddy blocks to build PFN array
- copy_to_devmem: SDMA copy system RAM -> VRAM via GART window
- copy_to_ram: SDMA copy VRAM -> system RAM via GART window
- devmem_release: drop BO ref (triggers destroy to free amdgpu_bo_svm)
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c | 628 +++++++++++++++++++-
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h | 3 +
2 files changed, 629 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
index 6b40f7477793..4d6ec19c4887 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
@@ -64,9 +64,19 @@
#include <linux/memremap.h>
#include <linux/migrate.h>
-#include "amdgpu_amdkfd.h"
#include "amdgpu_migrate.h"
#include "amdgpu.h"
+#include "amdgpu_ttm.h"
+#include "amdgpu_res_cursor.h"
+
+#define AMDGPU_MIGRATE_TRACE(fmt, ...) \
+ pr_debug("%s: " fmt, __func__, ##__VA_ARGS__)
+
+/* SDMA copy direction */
+#define FROM_RAM_TO_VRAM 0
+#define FROM_VRAM_TO_RAM 1
+
+static const struct drm_pagemap_devmem_ops amdgpu_pagemap_ops;
static inline struct amdgpu_pagemap *
dpagemap_to_apagemap(struct drm_pagemap *dpagemap)
@@ -94,8 +104,622 @@ amdgpu_svm_page_to_apagemap(struct page *page)
return container_of(pgmap, struct amdgpu_pagemap, pgmap);
}
+/* drm_pagemap_devmem_ops — per-BO migration mechanics */
+
+/**
+ * struct amdgpu_bo_svm - SVM BO subtype with drm_pagemap devmem allocation
+ *
+ * @bo: Embedded base amdgpu_bo
+ * @devmem: drm_pagemap device memory allocation (passed to framework)
+ *
+ * Lifecycle is managed by the drm_pagemap framework's internal zdd refcount:
+ * - zdd->devmem_allocation points to &svm_bo->devmem
+ * - When zdd refcount drops to zero, framework calls devmem_release()
+ * - devmem_release() drops the BO reference (triggering destroy callback)
+ */
+struct amdgpu_bo_svm {
+ struct amdgpu_bo bo;
+ struct drm_pagemap_devmem devmem;
+};
+
+#define to_amdgpu_bo_svm(abo) container_of((abo), struct amdgpu_bo_svm, bo)
+
+static inline struct amdgpu_bo_svm *
+devmem_to_amdgpu_bo_svm(struct drm_pagemap_devmem *devmem_allocation)
+{
+ return container_of(devmem_allocation, struct amdgpu_bo_svm, devmem);
+}
+
+/**
+ * amdgpu_bo_svm_destroy - TTM destroy callback for SVM BO
+ *
+ * Called when the last reference to the BO is dropped.
+ * Follows amdgpu_bo_user_destroy pattern: delegates to amdgpu_bo_base_destroy.
+ */
+static void amdgpu_bo_svm_destroy(struct ttm_buffer_object *tbo)
+{
+ struct amdgpu_bo *bo = ttm_to_amdgpu_bo(tbo);
+ struct amdgpu_bo_svm *svm_bo = to_amdgpu_bo_svm(bo);
+
+ amdgpu_bo_kunmap(bo);
+ drm_gem_object_release(&bo->tbo.base);
+ amdgpu_bo_unref(&bo->parent);
+ kvfree(svm_bo);
+}
+
+/**
+ * amdgpu_bo_svm_alloc - Allocate an amdgpu_bo_svm with VRAM backing
+ * @adev: AMDGPU device
+ * @dpagemap: The drm_pagemap for this device
+ * @mm: mm_struct of the owning process
+ * @size: Allocation size in bytes
+ *
+ * Uses bp->bo_ptr_size = sizeof(struct amdgpu_bo_svm) so that
+ * amdgpu_bo_create() allocates the full amdgpu_bo_svm structure,
+ * with the embedded amdgpu_bo as the base.
+ *
+ * Return: Pointer to allocated amdgpu_bo_svm on success, ERR_PTR on failure
+ */
+static struct amdgpu_bo_svm *
+amdgpu_bo_svm_alloc(struct amdgpu_device *adev,
+ struct drm_pagemap *dpagemap,
+ struct mm_struct *mm, unsigned long size)
+{
+ struct amdgpu_bo_param bp = {};
+ struct amdgpu_bo_svm *svm_bo;
+ struct amdgpu_bo *bo;
+ int ret;
+
+ bp.size = size;
+ bp.bo_ptr_size = sizeof(struct amdgpu_bo_svm);
+ bp.destroy = &amdgpu_bo_svm_destroy;
+ bp.domain = AMDGPU_GEM_DOMAIN_VRAM;
+ bp.type = ttm_bo_type_device;
+ bp.flags = AMDGPU_GEM_CREATE_NO_CPU_ACCESS |
+ AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS |
+ AMDGPU_GEM_CREATE_VRAM_CLEARED;
+
+ ret = amdgpu_bo_create(adev, &bp, &bo);
+ if (ret) {
+ AMDGPU_MIGRATE_TRACE("Failed to create SVM BO\n");
+ return ERR_PTR(ret);
+ }
+
+ svm_bo = to_amdgpu_bo_svm(bo);
+
+ drm_pagemap_devmem_init(&svm_bo->devmem,
+ adev->dev, mm,
+ &amdgpu_pagemap_ops,
+ dpagemap, size, NULL);
+
+ return svm_bo;
+}
+
+/**
+ * amdgpu_svm_devmem_release - Release BO when all device pages migrate back
+ *
+ * Called by the drm_pagemap framework (via drm_pagemap_zdd_destroy) when the
+ * last device-private page backed by this allocation has been migrated back
+ * to system memory (or the owning process exits).
+ *
+ * Drops the BO reference, which triggers the destroy callback to free
+ * the amdgpu_bo_svm structure.
+ */
+static void
+amdgpu_svm_devmem_release(struct drm_pagemap_devmem *devmem_allocation)
+{
+ struct amdgpu_bo_svm *svm_bo = devmem_to_amdgpu_bo_svm(devmem_allocation);
+ struct amdgpu_bo *bo = &svm_bo->bo;
+
+ AMDGPU_MIGRATE_TRACE("Release svm_bo=%px bo=%px\n", svm_bo, bo);
+ amdgpu_bo_unref(&bo);
+}
+
+/**
+ * amdgpu_svm_populate_devmem_pfn - Convert BO VRAM allocation to PFN array
+ * @devmem_allocation: The devmem allocation in the amdgpu_bo_svm wrapper
+ * @npages: Number of PFN entries to fill
+ * @pfn: Output PFN array
+ *
+ * Iterates over the BO's TTM vram_mgr buddy blocks and converts each
+ * block's VRAM offset to ZONE_DEVICE PFNs:
+ *
+ * PFN = PHYS_PFN(block_offset + apagemap.hpa_base) + page_index
+ *
+ * This is called by drm_pagemap_migrate_to_devmem() to build the
+ * destination PFN array for migrate_vma_pages().
+ *
+ * Return: 0 on success
+ */
+static int
+amdgpu_svm_populate_devmem_pfn(struct drm_pagemap_devmem *devmem_allocation,
+ unsigned long npages, unsigned long *pfn)
+{
+ struct amdgpu_pagemap *svm_dm = dpagemap_to_apagemap(devmem_allocation->dpagemap);
+ struct amdgpu_bo_svm *svm_bo = devmem_to_amdgpu_bo_svm(devmem_allocation);
+ struct amdgpu_bo *bo = &svm_bo->bo;
+ struct amdgpu_res_cursor cursor;
+ unsigned long i = 0;
+
+ dma_resv_assert_held(bo->tbo.base.resv);
+
+ amdgpu_res_first(bo->tbo.resource, 0, npages << PAGE_SHIFT, &cursor);
+ while (cursor.remaining && i < npages) {
+ u64 pfn_base = PHYS_PFN(cursor.start + svm_dm->hpa_base);
+ u64 pages = cursor.size >> PAGE_SHIFT;
+ unsigned long j;
+
+ for (j = 0; j < pages && i < npages; j++, i++)
+ pfn[i] = pfn_base + j;
+
+ amdgpu_res_next(&cursor, cursor.size);
+ }
+
+ AMDGPU_MIGRATE_TRACE("populate_devmem_pfn: npages=%lu first_pfn=0x%lx\n",
+ npages, npages > 0 ? pfn[0] : 0);
+
+ return 0;
+}
+
+/* SDMA copy helpers — GART window based data transfer */
+
+/**
+ * amdgpu_svm_direct_mapping_addr - Convert VRAM offset to MC address
+ * @adev: AMDGPU device
+ * @vram_offset: Byte offset within VRAM
+ *
+ * Return: MC address suitable for SDMA src/dst
+ */
+static u64
+amdgpu_svm_direct_mapping_addr(struct amdgpu_device *adev, u64 vram_offset)
+{
+ return vram_offset + amdgpu_ttm_domain_start(adev, TTM_PL_VRAM);
+}
+
+/**
+ * amdgpu_svm_gart_map - Map system DMA addresses into GART window
+ * @ring: SDMA ring for the GART update job
+ * @npages: Number of pages to map
+ * @addr: Array of system memory DMA addresses
+ * @gart_addr: Output — GART base address to use in SDMA copy
+ * @flags: PTE flags (e.g. writeable for RAM-to-VRAM src)
+ *
+ * Builds GART PTEs pointing at the given DMA addresses, submits an
+ * SDMA job to update the GART entries, and returns the GART address
+ * that can be used as src or dst in a subsequent amdgpu_copy_buffer().
+ *
+ * Uses GART window 0, protected by gtt_window_lock.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+amdgpu_svm_gart_map(struct amdgpu_ring *ring,
+ struct amdgpu_ttm_buffer_entity *entity,
+ u64 npages,
+ dma_addr_t *addr, u64 *gart_addr, u64 flags)
+{
+ struct amdgpu_device *adev = ring->adev;
+ struct amdgpu_job *job;
+ unsigned int num_dw, num_bytes;
+ struct dma_fence *fence;
+ u64 src_addr, dst_addr;
+ u64 pte_flags;
+ void *cpu_addr;
+ int r;
+
+ /* Use entity's GART window 0 */
+ *gart_addr = amdgpu_compute_gart_address(&adev->gmc, entity, 0);
+
+ num_dw = ALIGN(adev->mman.buffer_funcs->copy_num_dw, 8);
+ num_bytes = npages * 8 * AMDGPU_GPU_PAGES_IN_CPU_PAGE;
+
+ r = amdgpu_job_alloc_with_ib(adev, &entity->base,
+ AMDGPU_FENCE_OWNER_UNDEFINED,
+ num_dw * 4 + num_bytes,
+ AMDGPU_IB_POOL_DELAYED,
+ &job,
+ AMDGPU_KERNEL_JOB_ID_KFD_GART_MAP);
+ if (r)
+ return r;
+
+ src_addr = num_dw * 4;
+ src_addr += job->ibs[0].gpu_addr;
+
+ dst_addr = amdgpu_bo_gpu_offset(adev->gart.bo);
+ dst_addr += (entity->gart_window_offs[0] >> AMDGPU_GPU_PAGE_SHIFT) * 8;
+ amdgpu_emit_copy_buffer(adev, &job->ibs[0], src_addr,
+ dst_addr, num_bytes, 0);
+
+ amdgpu_ring_pad_ib(ring, &job->ibs[0]);
+ WARN_ON(job->ibs[0].length_dw > num_dw);
+
+ pte_flags = AMDGPU_PTE_VALID | AMDGPU_PTE_READABLE;
+ pte_flags |= AMDGPU_PTE_SYSTEM | AMDGPU_PTE_SNOOPED;
+ if (flags & AMDGPU_PTE_WRITEABLE)
+ pte_flags |= AMDGPU_PTE_WRITEABLE;
+ pte_flags |= adev->gart.gart_pte_flags;
+
+ cpu_addr = &job->ibs[0].ptr[num_dw];
+
+ amdgpu_gart_map(adev, 0, npages, addr, pte_flags, cpu_addr);
+ fence = amdgpu_job_submit(job);
+ dma_fence_put(fence);
+
+ return 0;
+}
+
+/**
+ * amdgpu_svm_copy_memory_gart - SDMA copy between system RAM and VRAM
+ * @adev: AMDGPU device
+ * @sys: Array of DMA addresses for system memory pages
+ * @vram: Array of VRAM byte offsets (relative to start of VRAM)
+ * @npages: Number of pages to copy
+ * @direction: FROM_RAM_TO_VRAM or FROM_VRAM_TO_RAM
+ * @mfence: In/out — carries the last SDMA fence for serialization
+ *
+ * Maps system memory pages into the GART window and uses SDMA to copy
+ * data to/from VRAM. Handles splitting into AMDGPU_GTT_MAX_TRANSFER_SIZE
+ * chunks. Acquires entity->lock internally to protect the GART window,
+ * matching the KFD svm_migrate_copy_memory_gart() pattern.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+amdgpu_svm_copy_memory_gart(struct amdgpu_device *adev, dma_addr_t *sys,
+ u64 *vram, u64 npages, int direction,
+ struct dma_fence **mfence)
+{
+ const u64 max_pages = AMDGPU_GTT_MAX_TRANSFER_SIZE;
+ struct amdgpu_ring *ring = to_amdgpu_ring(adev->mman.buffer_funcs_scheds[0]);
+ struct amdgpu_ttm_buffer_entity *entity = &adev->mman.move_entities[0];
+ u64 gart_s, gart_d;
+ struct dma_fence *next;
+ u64 size;
+ int r;
+
+ mutex_lock(&entity->lock);
+
+ while (npages) {
+ size = min(max_pages, npages);
+
+ if (direction == FROM_VRAM_TO_RAM) {
+ gart_s = amdgpu_svm_direct_mapping_addr(adev, *vram);
+ r = amdgpu_svm_gart_map(ring, entity, size, sys,
+ &gart_d, AMDGPU_PTE_WRITEABLE);
+ } else {
+ r = amdgpu_svm_gart_map(ring, entity, size, sys,
+ &gart_s, 0);
+ gart_d = amdgpu_svm_direct_mapping_addr(adev, *vram);
+ }
+ if (r) {
+ dev_err(adev->dev, "failed %d to map GART for SDMA\n", r);
+ goto out_unlock;
+ }
+
+ AMDGPU_MIGRATE_TRACE("SDMA_COPY: %s npages=%llu vram_off=0x%llx\n",
+ direction == FROM_RAM_TO_VRAM ? "RAM->VRAM" : "VRAM->RAM",
+ size, (u64)*vram);
+
+ r = amdgpu_copy_buffer(adev, entity, gart_s, gart_d,
+ size * PAGE_SIZE,
+ NULL, &next, true, 0);
+ if (r) {
+ dev_err(adev->dev, "failed %d to copy buffer\n", r);
+ goto out_unlock;
+ }
+
+ dma_fence_put(*mfence);
+ *mfence = next;
+ npages -= size;
+ if (npages) {
+ sys += size;
+ vram += size;
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&entity->lock);
+
+ return r;
+}
+
+/**
+ * amdgpu_svm_copy_to_devmem - SDMA copy system memory -> VRAM
+ * @pages: Array of destination ZONE_DEVICE pages (VRAM-backed)
+ * @pagemap_addr: Array of source DMA addresses (system memory, already mapped)
+ * @npages: Number of pages to copy
+ *
+ * Builds parallel sys[] and vram[] arrays from the framework-provided
+ * pagemap_addr and device pages, then submits batched SDMA copies via
+ * the GART window.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+amdgpu_svm_copy_to_devmem(struct page **pages,
+ struct drm_pagemap_addr *pagemap_addr,
+ unsigned long npages,
+ struct dma_fence *pre_migrate_fence)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_pagemap *svm_dm;
+ struct dma_fence *mfence = NULL;
+ dma_addr_t *sys;
+ u64 *vram;
+ unsigned long i, j;
+ int ret = 0;
+
+ if (!npages)
+ return 0;
+
+ /*
+ * Find the first non-NULL page to derive the device.
+ * The pages array may contain NULL entries for positions where
+ * no valid device page exists.
+ */
+ for (i = 0; i < npages; i++) {
+ if (pages[i])
+ break;
+ }
+ if (i == npages)
+ return 0;
+
+ svm_dm = amdgpu_svm_page_to_apagemap(pages[i]);
+ adev = svm_dm->adev;
+
+ sys = kvcalloc(npages, sizeof(*sys), GFP_KERNEL);
+ vram = kvcalloc(npages, sizeof(*vram), GFP_KERNEL);
+ if (!sys || !vram) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ for (i = 0, j = 0; i < npages; i++) {
+ if (!pagemap_addr[i].addr)
+ goto flush;
+
+ sys[j] = pagemap_addr[i].addr;
+ vram[j] = ((u64)page_to_pfn(pages[i]) << PAGE_SHIFT) -
+ svm_dm->hpa_base;
+
+ /* Check if next vram page is contiguous with current */
+ if (j > 0 && vram[j] != vram[j - 1] + PAGE_SIZE)
+ goto flush;
+
+ j++;
+ continue;
+flush:
+ if (j) {
+ ret = amdgpu_svm_copy_memory_gart(adev, sys, vram, j,
+ FROM_RAM_TO_VRAM,
+ &mfence);
+ if (ret)
+ goto out_fence;
+ j = 0;
+ }
+ /* Re-process current page if it was valid but broke contiguity */
+ if (pagemap_addr[i].addr) {
+ sys[0] = pagemap_addr[i].addr;
+ vram[0] = ((u64)page_to_pfn(pages[i]) << PAGE_SHIFT) -
+ svm_dm->hpa_base;
+ j = 1;
+ }
+ }
+
+ /* Flush remaining batch */
+ if (j)
+ ret = amdgpu_svm_copy_memory_gart(adev, sys, vram, j,
+ FROM_RAM_TO_VRAM, &mfence);
+
+out_fence:
+ if (mfence) {
+ dma_fence_wait(mfence, false);
+ dma_fence_put(mfence);
+ }
+
+ AMDGPU_MIGRATE_TRACE("copy_to_devmem done: npages=%ld ret=%d\n",
+ npages, ret);
+
+out_free:
+ kvfree(vram);
+ kvfree(sys);
+ return ret;
+}
+
+/**
+ * amdgpu_svm_copy_to_ram - SDMA copy VRAM -> system memory
+ * @pages: Array of source ZONE_DEVICE pages (VRAM-backed)
+ * @pagemap_addr: Array of destination DMA addresses (system memory, already mapped)
+ * @npages: Number of pages to copy
+ *
+ * Mirror of copy_to_devmem with src/dst swapped.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+amdgpu_svm_copy_to_ram(struct page **pages,
+ struct drm_pagemap_addr *pagemap_addr,
+ unsigned long npages,
+ struct dma_fence *pre_migrate_fence)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_pagemap *svm_dm;
+ struct dma_fence *mfence = NULL;
+ dma_addr_t *sys;
+ u64 *vram;
+ unsigned long i, j;
+ int ret = 0;
+
+ if (!npages)
+ return 0;
+
+ for (i = 0; i < npages; i++) {
+ if (pages[i])
+ break;
+ }
+ if (i == npages)
+ return 0;
+
+ svm_dm = amdgpu_svm_page_to_apagemap(pages[i]);
+ adev = svm_dm->adev;
+
+ sys = kvcalloc(npages, sizeof(*sys), GFP_KERNEL);
+ vram = kvcalloc(npages, sizeof(*vram), GFP_KERNEL);
+ if (!sys || !vram) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ for (i = 0, j = 0; i < npages; i++) {
+ if (!pagemap_addr[i].addr || !pages[i])
+ goto flush;
+
+ vram[j] = ((u64)page_to_pfn(pages[i]) << PAGE_SHIFT) -
+ svm_dm->hpa_base;
+ sys[j] = pagemap_addr[i].addr;
+
+ /* Check if next vram page is contiguous with current */
+ if (j > 0 && vram[j] != vram[j - 1] + PAGE_SIZE)
+ goto flush;
+
+ j++;
+ continue;
+flush:
+ if (j) {
+ ret = amdgpu_svm_copy_memory_gart(adev, sys, vram, j,
+ FROM_VRAM_TO_RAM,
+ &mfence);
+ if (ret)
+ goto out_fence;
+ j = 0;
+ }
+ /* Re-process current page if it was valid but broke contiguity */
+ if (pagemap_addr[i].addr && pages[i]) {
+ vram[0] = ((u64)page_to_pfn(pages[i]) << PAGE_SHIFT) -
+ svm_dm->hpa_base;
+ sys[0] = pagemap_addr[i].addr;
+ j = 1;
+ }
+ }
+
+ /* Flush remaining batch */
+ if (j)
+ ret = amdgpu_svm_copy_memory_gart(adev, sys, vram, j,
+ FROM_VRAM_TO_RAM, &mfence);
+
+out_fence:
+ if (mfence) {
+ dma_fence_wait(mfence, false);
+ dma_fence_put(mfence);
+ }
+
+ AMDGPU_MIGRATE_TRACE("copy_to_ram done: npages=%ld ret=%d\n", npages, ret);
+
+out_free:
+ kvfree(vram);
+ kvfree(sys);
+ return ret;
+}
+
+static const struct drm_pagemap_devmem_ops amdgpu_pagemap_ops = {
+ .devmem_release = amdgpu_svm_devmem_release,
+ .populate_devmem_pfn = amdgpu_svm_populate_devmem_pfn,
+ .copy_to_devmem = amdgpu_svm_copy_to_devmem,
+ .copy_to_ram = amdgpu_svm_copy_to_ram,
+};
+
+/* drm_pagemap_ops — top-level migration entry points */
+
+/**
+ * amdgpu_svm_device_map - Convert ZONE_DEVICE page to GPU PTE address
+ * @dpagemap: The drm_pagemap for this device
+ * @dev: Requesting device (for P2P check)
+ * @page: ZONE_DEVICE page backed by VRAM
+ * @order: Page order (0 = 4K, 9 = 2M, etc.)
+ * @dir: DMA direction (unused for local VRAM)
+ *
+ * Address conversion chain:
+ * page -> PFN -> HPA -> VRAM offset -> PTE address
+ *
+ * HPA = page_to_pfn(page) << PAGE_SHIFT
+ * VRAM offset = HPA - apagemap.hpa_base
+ * PTE address = VRAM offset + adev->vm_manager.vram_base_offset
+ *
+ * Return: drm_pagemap_addr with PTE address and AMDGPU_INTERCONNECT_VRAM protocol
+ */
+static struct drm_pagemap_addr
+amdgpu_svm_device_map(struct drm_pagemap *dpagemap,
+ struct device *dev,
+ struct page *page,
+ unsigned int order,
+ enum dma_data_direction dir)
+{
+ struct amdgpu_pagemap *svm_dm = dpagemap_to_apagemap(dpagemap);
+ struct amdgpu_device *adev = dpagemap_to_adev(dpagemap);
+ dma_addr_t addr;
+
+ if (dpagemap->drm->dev == dev) {
+ /* Same device: return VRAM PTE address */
+ u64 hpa = (u64)page_to_pfn(page) << PAGE_SHIFT;
+ u64 vram_offset = hpa - svm_dm->hpa_base;
+
+ addr = vram_offset + adev->vm_manager.vram_base_offset;
+ } else {
+ /* Cross-device P2P: not yet supported */
+ addr = DMA_MAPPING_ERROR;
+ }
+
+ return drm_pagemap_addr_encode(addr,
+ AMDGPU_INTERCONNECT_VRAM, order, dir);
+}
+
+/**
+ * amdgpu_svm_populate_mm - Allocate VRAM BO and migrate pages
+ * @dpagemap: The drm_pagemap for this device
+ * @start: Start virtual address of the range to migrate
+ * @end: End virtual address (exclusive)
+ * @mm: mm_struct of the owning process
+ * @timeslice_ms: Maximum time to spend migrating (for fairness)
+ *
+ * Core migration entry point called by drm_pagemap_populate_mm().
+ * Allocates an amdgpu_bo_svm via amdgpu_bo_svm_alloc(), then calls
+ * drm_pagemap_migrate_to_devmem() to execute the actual migration.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+amdgpu_svm_populate_mm(struct drm_pagemap *dpagemap,
+ unsigned long start, unsigned long end,
+ struct mm_struct *mm,
+ unsigned long timeslice_ms)
+{
+ struct amdgpu_device *adev = dpagemap_to_adev(dpagemap);
+ struct drm_pagemap_migrate_details mdetails = {
+ .timeslice_ms = timeslice_ms,
+ };
+ struct amdgpu_bo_svm *svm_bo;
+ int ret;
+
+ svm_bo = amdgpu_bo_svm_alloc(adev, dpagemap, mm, end - start);
+ if (IS_ERR(svm_bo))
+ return PTR_ERR(svm_bo);
+
+ AMDGPU_MIGRATE_TRACE("populate_mm: [0x%lx-0x%lx] size=%lu\n",
+ start, end, end - start);
+
+ ret = drm_pagemap_migrate_to_devmem(&svm_bo->devmem,
+ mm, start, end,
+ &mdetails);
+
+ amdgpu_bo_unreserve(&svm_bo->bo);
+
+ return ret;
+}
-const struct drm_pagemap_ops amdgpu_svm_drm_pagemap_ops = { };
+const struct drm_pagemap_ops amdgpu_svm_drm_pagemap_ops = {
+ .device_map = amdgpu_svm_device_map,
+ .populate_mm = amdgpu_svm_populate_mm,
+};
/**
* amdgpu_svm_migration_init - Register ZONE_DEVICE and initialize drm_pagemap
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
index af42bd997d49..a196a11ff802 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
@@ -30,6 +30,9 @@
struct amdgpu_device;
+#define AMDGPU_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER
+#define AMDGPU_INTERCONNECT_P2P (AMDGPU_INTERCONNECT_VRAM + 1)
+
/*
* AMDGPU_PGMAP_OWNER - Unique owner token for dev_pagemap registration.
*
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 3/8] drm/amdgpu: implement synchronous TTM eviction for SVM BOs
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
2026-06-05 7:53 ` [PATCH v5 1/8] drm/amdgpu: add VRAM migration infrastructure for drm_pagemap Junhua Shen
2026-06-05 7:53 ` [PATCH v5 2/8] drm/amdgpu: implement drm_pagemap SDMA migration callbacks Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 4/8] drm/amdgpu: hook up ZONE_DEVICE registration in device init and reset Junhua Shen
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
Implement the TTM eviction path for SVM (Shared Virtual Memory) BOs,
enabling VRAM overcommit scenarios where device-private pages must be
migrated back to system memory when VRAM is full.
- amdgpu_ttm.c: Add SVM BO detection in amdgpu_evict_flags() to evict
directly to SYSTEM domain. Add SVM BO handling in amdgpu_bo_move()
to call amdgpu_svm_bo_evict() for VRAM->SYSTEM transitions, which
synchronously migrates device-private pages back to RAM via
SDMA.
- amdgpu_migrate.c: Add amdgpu_svm_bo_evict() and
amdgpu_svm_bo_is_svm() helpers.
- amdgpu_object.c: Register SVM BO destroy callback in
amdgpu_bo_is_amdgpu_bo() so TTM eviction can identify and handle
SVM BOs correctly.
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c | 28 +++++++++++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h | 12 +++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 4 ++-
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 20 +++++++++++++++
4 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
index 4d6ec19c4887..0979e0319cef 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.c
@@ -628,6 +628,34 @@ static const struct drm_pagemap_devmem_ops amdgpu_pagemap_ops = {
.copy_to_ram = amdgpu_svm_copy_to_ram,
};
+/**
+ * amdgpu_svm_bo_evict - Evict SVM BO by migrating device pages back to RAM
+ * @bo: The amdgpu_bo to evict (must be an amdgpu_bo_svm)
+ *
+ * Called from amdgpu_bo_move() when TTM needs to evict a SVM BO from VRAM.
+ * Uses drm_pagemap_evict_to_ram() to synchronously migrate all device-private
+ * pages back to system memory via SDMA.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int amdgpu_svm_bo_evict(struct amdgpu_bo *bo)
+{
+ struct amdgpu_bo_svm *sbo = to_amdgpu_bo_svm(bo);
+
+ return drm_pagemap_evict_to_ram(&sbo->devmem);
+}
+
+/**
+ * amdgpu_svm_bo_is_svm - Check if a TTM BO is an SVM BO
+ * @tbo: TTM buffer object
+ *
+ * Return: true if this is an amdgpu_bo_svm (identified by destroy callback)
+ */
+bool amdgpu_svm_bo_is_svm(struct ttm_buffer_object *tbo)
+{
+ return tbo->destroy == &amdgpu_bo_svm_destroy;
+}
+
/* drm_pagemap_ops — top-level migration entry points */
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
index a196a11ff802..25e0c1f13180 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_migrate.h
@@ -29,6 +29,8 @@
#include <linux/memremap.h>
struct amdgpu_device;
+struct amdgpu_bo;
+struct ttm_buffer_object;
#define AMDGPU_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER
#define AMDGPU_INTERCONNECT_P2P (AMDGPU_INTERCONNECT_VRAM + 1)
@@ -70,12 +72,22 @@ struct amdgpu_pagemap {
#if IS_ENABLED(CONFIG_DRM_AMDGPU_SVM)
int amdgpu_svm_migration_init(struct amdgpu_device *adev);
+int amdgpu_svm_bo_evict(struct amdgpu_bo *bo);
+bool amdgpu_svm_bo_is_svm(struct ttm_buffer_object *tbo);
#else
static inline
int amdgpu_svm_migration_init(struct amdgpu_device *adev)
{
return 0;
}
+static inline int amdgpu_svm_bo_evict(struct amdgpu_bo *bo)
+{
+ return -ENODEV;
+}
+static inline bool amdgpu_svm_bo_is_svm(struct ttm_buffer_object *tbo)
+{
+ return false;
+}
#endif
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index 4dd7c712b8c3..d6ea8737dedc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -43,6 +43,7 @@
#include "amdgpu_vram_mgr.h"
#include "amdgpu_vm.h"
#include "amdgpu_dma_buf.h"
+#include "amdgpu_migrate.h"
/**
* DOC: amdgpu_object
@@ -93,7 +94,8 @@ static void amdgpu_bo_user_destroy(struct ttm_buffer_object *tbo)
bool amdgpu_bo_is_amdgpu_bo(struct ttm_buffer_object *bo)
{
if (bo->destroy == &amdgpu_bo_destroy ||
- bo->destroy == &amdgpu_bo_user_destroy)
+ bo->destroy == &amdgpu_bo_user_destroy ||
+ amdgpu_svm_bo_is_svm(bo))
return true;
return false;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 6c6ab4dd6ea9..9d3deec7cede 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -59,6 +59,7 @@
#include "amdgpu_hmm.h"
#include "amdgpu_atomfirmware.h"
#include "amdgpu_res_cursor.h"
+#include "amdgpu_migrate.h"
#include "bif/bif_4_1_d.h"
MODULE_IMPORT_NS("DMA_BUF");
@@ -121,6 +122,15 @@ static void amdgpu_evict_flags(struct ttm_buffer_object *bo,
return;
}
+ /* SVM BOs must evict directly to system memory for drm_pagemap
+ * migration back to RAM in amdgpu_bo_move().
+ */
+ if (amdgpu_svm_bo_is_svm(bo)) {
+ amdgpu_bo_placement_from_domain(abo, AMDGPU_GEM_DOMAIN_CPU);
+ *placement = abo->placement;
+ return;
+ }
+
switch (bo->resource->mem_type) {
case AMDGPU_PL_GDS:
case AMDGPU_PL_GWS:
@@ -575,6 +585,16 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
abo->flags &= ~AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
}
+ /* SVM BO eviction: migrate device-private pages back to RAM */
+ if (amdgpu_svm_bo_is_svm(bo) &&
+ old_mem->mem_type == TTM_PL_VRAM &&
+ new_mem->mem_type == TTM_PL_SYSTEM) {
+ r = amdgpu_svm_bo_evict(abo);
+ if (!r)
+ ttm_bo_move_null(bo, new_mem);
+ return r;
+ }
+
if (adev->mman.buffer_funcs_enabled &&
((old_mem->mem_type == TTM_PL_SYSTEM &&
new_mem->mem_type == TTM_PL_VRAM) ||
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 4/8] drm/amdgpu: hook up ZONE_DEVICE registration in device init and reset
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
` (2 preceding siblings ...)
2026-06-05 7:53 ` [PATCH v5 3/8] drm/amdgpu: implement synchronous TTM eviction for SVM BOs Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 5/8] drm/amdgpu: add SVM range migration helpers for drm_pagemap Junhua Shen
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
Call amdgpu_svm_migration_init() in the device initialization and
XGMI reset-restore paths to register the GPU's VRAM as a ZONE_DEVICE
region before KFD initialization.
This activates the drm_pagemap migration infrastructure.
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 ++
drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 5ff224163bab..5297bee6fb72 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -78,6 +78,7 @@
#include "amdgpu_reset.h"
#include "amdgpu_virt.h"
#include "amdgpu_dev_coredump.h"
+#include "amdgpu_migrate.h"
#include <linux/suspend.h>
#include <drm/task_barrier.h>
@@ -4093,6 +4094,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
/* Don't init kfd if whole hive need to be reset during init */
if (adev->init_lvl->level != AMDGPU_INIT_LEVEL_MINIMAL_XGMI) {
+ amdgpu_svm_migration_init(adev);
kgd2kfd_init_zone_device(adev);
kfd_update_svm_support_properties(adev);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c
index da77e0ef129b..c2ea540e392b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_reset.c
@@ -25,6 +25,7 @@
#include "aldebaran.h"
#include "sienna_cichlid.h"
#include "smu_v13_0_10.h"
+#include "amdgpu_migrate.h"
static int amdgpu_reset_xgmi_reset_on_init_suspend(struct amdgpu_device *adev)
{
@@ -87,6 +88,7 @@ static int amdgpu_reset_xgmi_reset_on_init_restore_hwctxt(
return r;
list_for_each_entry(tmp_adev, reset_device_list, reset_list) {
if (!tmp_adev->kfd.init_complete) {
+ amdgpu_svm_migration_init(tmp_adev);
kgd2kfd_init_zone_device(tmp_adev);
amdgpu_amdkfd_device_init(tmp_adev);
amdgpu_amdkfd_drm_client_create(tmp_adev);
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 5/8] drm/amdgpu: add SVM range migration helpers for drm_pagemap
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
` (3 preceding siblings ...)
2026-06-05 7:53 ` [PATCH v5 4/8] drm/amdgpu: hook up ZONE_DEVICE registration in device init and reset Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 6/8] drm/amdgpu: refactor SVM attr devmem_possible and prefer_vram API Junhua Shen
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
Add amdgpu_svm_range_migrate.c/.h implementing the per-range migration
helpers for SVM VRAM migration via drm_pagemap:
- Implement range_needs_migrate_to_vram() checking migrate_devmem
capability and current backing location.
- Add amdgpu_svm_range_migrate_to_vram() wrapping
drm_pagemap_populate_mm() for RAM-to-VRAM migration.
- Add amdgpu_pagemap_capable() to check device memory support.
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/Makefile | 6 +-
.../drm/amd/amdgpu/amdgpu_svm_range_migrate.c | 115 ++++++++++++++++++
.../drm/amd/amdgpu/amdgpu_svm_range_migrate.h | 35 ++++++
3 files changed, 153 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.c
create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.h
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index 08207cad310d..d644079f3e55 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -328,14 +328,14 @@ amdgpu-$(CONFIG_HMM_MIRROR) += amdgpu_hmm.o
# svm support
amdgpu-$(CONFIG_DRM_AMDGPU_SVM) += amdgpu_svm.o amdgpu_svm_attr.o \
- amdgpu_svm_fault.o amdgpu_svm_range.o amdgpu_userptr.o amdgpu_migrate.o
+ amdgpu_svm_fault.o amdgpu_svm_range.o amdgpu_svm_range_migrate.o amdgpu_userptr.o amdgpu_migrate.o
.PHONY: clean-svm
clean-svm:
rm -f $(obj)/amdgpu_svm.o $(obj)/amdgpu_svm_attr.o $(obj)/amdgpu_svm_fault.o $(obj)/amdgpu_svm_range.o \
- $(obj)/amdgpu_userptr.o $(obj)/amdgpu_migrate.o \
+ $(obj)/amdgpu_svm_range_migrate.o $(obj)/amdgpu_userptr.o $(obj)/amdgpu_migrate.o \
$(obj)/.amdgpu_svm.o.cmd $(obj)/.amdgpu_svm_attr.o.cmd $(obj)/.amdgpu_svm_fault.o.cmd $(obj)/.amdgpu_svm_range.o.cmd \
- $(obj)/.amdgpu_userptr.o.cmd $(obj)/.amdgpu_migrate.o.cmd
+ $(obj)/.amdgpu_svm_range_migrate.o.cmd $(obj)/.amdgpu_userptr.o.cmd $(obj)/.amdgpu_migrate.o.cmd
include $(FULL_AMD_PATH)/pm/Makefile
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.c
new file mode 100644
index 000000000000..e51657c2a65c
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.c
@@ -0,0 +1,115 @@
+// 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.h"
+#include "amdgpu_svm.h"
+#include "amdgpu_svm_range.h"
+#include "amdgpu_migrate.h"
+#include "amdgpu_svm_range_migrate.h"
+
+static bool
+range_in_vram(struct drm_gpusvm_range *range)
+{
+ struct drm_gpusvm_pages_flags flags = {
+ /* Pairs with WRITE_ONCE in drm_gpusvm_get_pages() */
+ .__flags = READ_ONCE(range->pages.flags.__flags),
+ };
+
+ return flags.has_devmem_pages;
+}
+
+static bool
+range_needs_migrate_to_vram(struct drm_gpusvm_range *range)
+{
+ if (!range->pages.flags.migrate_devmem)
+ return false;
+ if (range_in_vram(range))
+ return false;
+
+ return true;
+}
+
+static struct drm_pagemap *
+amdgpu_svm_get_dpagemap(struct amdgpu_svm *svm)
+{
+ struct amdgpu_pagemap *apagemap = svm->adev->apagemap;
+
+ if (!apagemap || !apagemap->initialized)
+ return NULL;
+
+ return &apagemap->dpagemap;
+}
+
+bool
+amdgpu_pagemap_capable(struct amdgpu_svm *svm)
+{
+ if (svm->adev->gmc.is_app_apu)
+ return false;
+
+ if (!amdgpu_svm_get_dpagemap(svm))
+ return false;
+
+ return true;
+}
+
+/**
+ * amdgpu_svm_range_migrate_to_vram - Migrate range pages to VRAM
+ * @svm: Pointer to the AMDGPU SVM structure
+ * @range: The GPU SVM range to migrate
+ *
+ * Wraps drm_pagemap_populate_mm() to migrate system memory pages
+ * backing the given range into device VRAM. Skips migration if the
+ * range doesn't support devmem or is already in VRAM.
+ *
+ * Return: 0 on success or no-op, negative error code on failure
+ */
+int
+amdgpu_svm_range_migrate_to_vram(struct amdgpu_svm *svm,
+ struct drm_gpusvm_range *range)
+{
+ struct drm_pagemap *dpagemap;
+ unsigned long start, end;
+ int ret;
+
+ if (!range_needs_migrate_to_vram(range))
+ return 0;
+
+ dpagemap = amdgpu_svm_get_dpagemap(svm);
+ if (!dpagemap)
+ return -ENODEV;
+
+ start = drm_gpusvm_range_start(range);
+ end = drm_gpusvm_range_end(range);
+
+ ret = drm_pagemap_populate_mm(dpagemap, start, end,
+ svm->gpusvm.mm, 0);
+
+ if (ret) {
+ AMDGPU_SVM_TRACE("migrate_to_vram failed: ret=%d [0x%lx-0x%lx]\n",
+ ret, start, end);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.h
new file mode 100644
index 000000000000..6e4b6e45512e
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range_migrate.h
@@ -0,0 +1,35 @@
+/* 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_RANGE_MIGRATE_H__
+#define __AMDGPU_SVM_RANGE_MIGRATE_H__
+
+struct amdgpu_svm;
+struct drm_gpusvm_range;
+
+bool amdgpu_pagemap_capable(struct amdgpu_svm *svm);
+int amdgpu_svm_range_migrate_to_vram(struct amdgpu_svm *svm,
+ struct drm_gpusvm_range *range);
+
+#endif /* __AMDGPU_SVM_RANGE_MIGRATE_H__ */
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 6/8] drm/amdgpu: refactor SVM attr devmem_possible and prefer_vram API
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
` (4 preceding siblings ...)
2026-06-05 7:53 ` [PATCH v5 5/8] drm/amdgpu: add SVM range migration helpers for drm_pagemap Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 7/8] drm/amdgpu: integrate VRAM migration into SVM fault and prefetch paths Junhua Shen
2026-06-05 7:53 ` [PATCH v5 8/8] drm/amdgpu: integrate VRAM migration into SVM restore and realign paths Junhua Shen
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
- Remove duplicate AMDGPU_INTERCONNECT_VRAM/P2P defines from amdgpu_svm.h
- Move devmem_possible() from attr module to amdgpu_svm.c as
amdgpu_svm_devmem_possible() since it is a device capability check,
not an attribute-level decision
- Add amdgpu_pagemap_capable() guard in devmem_possible()
- Decouple prefer_vram() from devmem_possible(), caller combines both
- Update all callers to new API signatures
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c | 12 ++++++++++++
drivers/gpu/drm/amd/amdgpu/amdgpu_svm.h | 4 +---
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c | 18 +-----------------
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.h | 5 +----
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c | 9 +++++----
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c | 8 +++++---
drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c | 13 +++++++------
7 files changed, 32 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c
index 626c5790e4d0..47e10e1401a6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.c
@@ -33,6 +33,7 @@
#include "amdgpu_svm_attr.h"
#include "amdgpu_svm_fault.h"
#include "amdgpu_svm_range.h"
+#include "amdgpu_svm_range_migrate.h"
#include "amdgpu_vm.h"
#if IS_ENABLED(CONFIG_DRM_AMDGPU_SVM)
@@ -318,6 +319,17 @@ int amdgpu_svm_apply_attr_change(struct amdgpu_svm *svm,
(last_page + 1) << PAGE_SHIFT);
}
+bool amdgpu_svm_devmem_possible(struct amdgpu_svm *svm)
+{
+ if (!amdgpu_pagemap_capable(svm))
+ return false;
+
+ if (svm->adev->apu_prefer_gtt)
+ return false;
+
+ return true;
+}
+
static bool amdgpu_svm_default_xnack_enabled(struct amdgpu_device *adev)
{
uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.h
index 1ab9c84addf7..0dc04d1383f4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm.h
@@ -47,9 +47,6 @@ struct amdgpu_svm_attrs;
struct drm_device;
struct drm_file;
-#define AMDGPU_INTERCONNECT_VRAM DRM_INTERCONNECT_DRIVER
-#define AMDGPU_INTERCONNECT_P2P (AMDGPU_INTERCONNECT_VRAM + 1)
-
enum amdgpu_svm_xnack_mode {
AMDGPU_SVM_XNACK_OFF,
AMDGPU_SVM_XNACK_ON,
@@ -154,6 +151,7 @@ int amdgpu_svm_apply_attr_change(struct amdgpu_svm *svm,
const struct amdgpu_svm_attrs *new_attrs,
unsigned long start_page,
unsigned long last_page);
+bool amdgpu_svm_devmem_possible(struct amdgpu_svm *svm);
#else
static inline int amdgpu_svm_init(struct amdgpu_device *adev,
struct amdgpu_vm *vm)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
index 177eb3cd501b..26e33defb8b9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.c
@@ -52,24 +52,8 @@ struct attr_get_ctx {
bool has_range;
};
-bool amdgpu_svm_attr_devmem_possible(struct amdgpu_svm *svm,
- const struct amdgpu_svm_attrs *attrs)
+bool amdgpu_svm_attr_prefer_vram(const struct amdgpu_svm_attrs *attrs)
{
- if (svm->adev->apu_prefer_gtt)
- return false;
-
- if (attrs->preferred_loc == AMDGPU_SVM_LOCATION_SYSMEM)
- return false;
-
- return true;
-}
-
-bool amdgpu_svm_attr_prefer_vram(struct amdgpu_svm *svm,
- const struct amdgpu_svm_attrs *attrs)
-{
- if (!amdgpu_svm_attr_devmem_possible(svm, attrs))
- return false;
-
if (attrs->preferred_loc != AMDGPU_SVM_LOCATION_UNDEFINED &&
attrs->preferred_loc != AMDGPU_SVM_LOCATION_SYSMEM)
return true;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.h
index ce7be1bc4ae8..36080405b504 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_attr.h
@@ -162,10 +162,7 @@ amdgpu_svm_attr_range_alloc(unsigned long start_page,
const struct amdgpu_svm_attrs *attrs);
void amdgpu_svm_attr_range_insert_locked(struct amdgpu_svm_attr_tree *attr_tree,
struct amdgpu_svm_attr_range *range);
-bool amdgpu_svm_attr_devmem_possible(struct amdgpu_svm *svm,
- const struct amdgpu_svm_attrs *attrs);
-bool amdgpu_svm_attr_prefer_vram(struct amdgpu_svm *svm,
- const struct amdgpu_svm_attrs *attrs);
+bool amdgpu_svm_attr_prefer_vram(const struct amdgpu_svm_attrs *attrs);
struct vm_area_struct *amdgpu_svm_check_vma(struct mm_struct *mm,
unsigned long addr);
int amdgpu_svm_attr_check_vm_bo(struct amdgpu_svm_attr_tree *attr_tree,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
index eb0e14439762..3f0c010e4e35 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
@@ -151,14 +151,15 @@ static int fault_map_range(struct amdgpu_svm *svm,
bool write_fault)
{
const struct amdgpu_svm_attrs *attrs = &attr_range->attrs;
- bool devmem_possible = false; /* TODO: add migration */
- bool need_vram_migration = amdgpu_svm_attr_prefer_vram(svm, attrs);
+ bool devmem_possible = amdgpu_svm_devmem_possible(svm);
+ bool need_vram_migration = devmem_possible &&
+ amdgpu_svm_attr_prefer_vram(attrs);
struct drm_gpusvm_ctx map_ctx = {
.read_only = !!(attrs->flags & AMDGPU_SVM_ATTR_BIT_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,
+ .devmem_only = need_vram_migration,
+ .timeslice_ms = need_vram_migration ? 5 : 0,
};
struct amdgpu_svm_range *range;
ktime_t timestamp = ktime_get_boottime();
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
index ef913a2363e3..21a2b0c49c53 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
@@ -26,6 +26,7 @@
#include "amdgpu_svm_attr.h"
#include "amdgpu_svm_range.h"
#include "amdgpu_svm_fault.h"
+#include "amdgpu_migrate.h"
#include "amdgpu.h"
#include "amdgpu_vm.h"
@@ -423,11 +424,12 @@ amdgpu_svm_range_map_attrs(struct amdgpu_svm *svm,
{
unsigned long addr = start;
int ret;
- bool devmem_possible = false; /* TODO: add migration */
- bool need_vram_migration = amdgpu_svm_attr_prefer_vram(svm, attrs);
+ bool devmem_possible = amdgpu_svm_devmem_possible(svm);
+ bool need_vram_migration = devmem_possible &&
+ amdgpu_svm_attr_prefer_vram(attrs);
struct drm_gpusvm_ctx map_ctx = {
.devmem_possible = devmem_possible,
- .devmem_only = need_vram_migration && devmem_possible,
+ .devmem_only = need_vram_migration,
.check_pages_threshold = devmem_possible ? SZ_64K : 0,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c
index 4582fb9453b2..8bf00aed7f15 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c
@@ -247,9 +247,10 @@ svm_restore_range(struct amdgpu_svm *svm, struct amdgpu_svm_range *range)
if (!attr_range || !amdgpu_svm_attr_has_access(attrs.access))
return 0;
- devmem_possible = amdgpu_svm_attr_devmem_possible(svm, &attrs);
- need_vram_migration = amdgpu_svm_attr_prefer_vram(svm, &attrs);
- devmem_possible = false; /* TODO: add migration */
+ devmem_possible = amdgpu_svm_devmem_possible(svm);
+ need_vram_migration = devmem_possible &&
+ amdgpu_svm_attr_prefer_vram(&attrs);
+
map_ctx = (struct drm_gpusvm_ctx){
.read_only = !!(attrs.flags & AMDGPU_SVM_ATTR_BIT_GPU_RO),
.devmem_possible = devmem_possible,
@@ -502,12 +503,12 @@ svm_restore_find_or_insert_by_attrs(struct amdgpu_svm *svm,
unsigned long start, unsigned long end)
{
unsigned long addr = start;
- bool devmem_possible = amdgpu_svm_attr_devmem_possible(svm, attrs);
- bool need_vram_migration = amdgpu_svm_attr_prefer_vram(svm, attrs);
+ bool devmem_possible = amdgpu_svm_devmem_possible(svm);
+ bool need_vram_migration = devmem_possible && amdgpu_svm_attr_prefer_vram(attrs);
struct drm_gpusvm_ctx map_ctx = {
.read_only = !!(attrs->flags & AMDGPU_SVM_ATTR_BIT_GPU_RO),
.devmem_possible = devmem_possible,
- .devmem_only = need_vram_migration && devmem_possible,
+ .devmem_only = need_vram_migration,
.check_pages_threshold = devmem_possible ? SZ_64K : 0,
};
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 7/8] drm/amdgpu: integrate VRAM migration into SVM fault and prefetch paths
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
` (5 preceding siblings ...)
2026-06-05 7:53 ` [PATCH v5 6/8] drm/amdgpu: refactor SVM attr devmem_possible and prefer_vram API Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
2026-06-05 7:53 ` [PATCH v5 8/8] drm/amdgpu: integrate VRAM migration into SVM restore and realign paths Junhua Shen
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
- Call amdgpu_svm_range_migrate_to_vram() before GPU mapping in
fault and prefetch paths when VRAM is preferred
- Add device_private_page_owner to drm_gpusvm_ctx for devmem awareness
- Allow AMDGPU_INTERCONNECT_VRAM entries in update_gpu_range proto check
- Skip valid-mapping fast path in prefetch when VRAM migration is
requested, so prefetch can actively move pages to VRAM
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c | 11 ++++++++--
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c | 20 ++++++++++++++++---
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
index 3f0c010e4e35..2acfcccd062d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_fault.c
@@ -26,6 +26,8 @@
#include "amdgpu_svm_attr.h"
#include "amdgpu_svm_fault.h"
#include "amdgpu_svm_range.h"
+#include "amdgpu_svm_range_migrate.h"
+#include "amdgpu_migrate.h"
#include "amdgpu.h"
#include "amdgpu_vm.h"
#include "amdgpu_gmc.h"
@@ -160,6 +162,8 @@ static int fault_map_range(struct amdgpu_svm *svm,
.check_pages_threshold = devmem_possible ? SZ_64K : 0,
.devmem_only = need_vram_migration,
.timeslice_ms = need_vram_migration ? 5 : 0,
+ .device_private_page_owner = devmem_possible ?
+ AMDGPU_PGMAP_OWNER(svm->adev) : NULL,
};
struct amdgpu_svm_range *range;
ktime_t timestamp = ktime_get_boottime();
@@ -229,9 +233,12 @@ static int fault_map_range(struct amdgpu_svm *svm,
}
AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT");
- /* TODO: add migration*/
+ if (need_vram_migration) {
+ AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT - MIGRATE PAGES");
+ amdgpu_svm_range_migrate_to_vram(svm, &range->base);
+ }
- AMDGPU_SVM_RANGE_DEBUG(range, "GET PAGES");
+ AMDGPU_SVM_RANGE_DEBUG(range, "PAGE FAULT - GET PAGES");
ret = amdgpu_svm_range_get_pages(svm, &range->base, &map_ctx);
if (ret == -EOPNOTSUPP || ret == -EFAULT) {
/*
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
index 21a2b0c49c53..88923e6a0194 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
@@ -26,6 +26,7 @@
#include "amdgpu_svm_attr.h"
#include "amdgpu_svm_range.h"
#include "amdgpu_svm_fault.h"
+#include "amdgpu_svm_range_migrate.h"
#include "amdgpu_migrate.h"
#include "amdgpu.h"
#include "amdgpu_vm.h"
@@ -279,7 +280,8 @@ amdgpu_svm_range_update_gpu_range(struct amdgpu_svm *svm,
unsigned long start_page, last_page;
bool is_last_seg;
- if (entry->proto != DRM_INTERCONNECT_SYSTEM)
+ if (entry->proto != DRM_INTERCONNECT_SYSTEM &&
+ entry->proto != AMDGPU_INTERCONNECT_VRAM)
return -EOPNOTSUPP;
pte_flags = amdgpu_svm_range_attr_pte_flags(svm, attrs,
@@ -431,6 +433,8 @@ amdgpu_svm_range_map_attrs(struct amdgpu_svm *svm,
.devmem_possible = devmem_possible,
.devmem_only = need_vram_migration,
.check_pages_threshold = devmem_possible ? SZ_64K : 0,
+ .device_private_page_owner = devmem_possible ?
+ AMDGPU_PGMAP_OWNER(svm->adev) : NULL,
};
while (addr < end) {
@@ -453,12 +457,22 @@ amdgpu_svm_range_map_attrs(struct amdgpu_svm *svm,
if (next_addr <= addr)
return -EINVAL;
- if (amdgpu_svm_range_is_valid(svm, range, attrs)) {
+ /*
+ * Prefetch to device memory should still run migration logic even if
+ * the current GPU mapping is already valid. This allows prefetch
+ * requests to actively move backing pages to VRAM instead of being
+ * skipped by the valid-mapping fast path.
+ */
+ if (amdgpu_svm_range_is_valid(svm, range, attrs) &&
+ !need_vram_migration) {
addr = next_addr;
continue;
}
- /* TODO: add migration */
+ if (need_vram_migration) {
+ AMDGPU_SVM_RANGE_DEBUG(range, "PREFETCH - MIGRATE PAGES");
+ amdgpu_svm_range_migrate_to_vram(svm, &range->base);
+ }
AMDGPU_SVM_RANGE_DEBUG(range, "GET PAGES");
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 8/8] drm/amdgpu: integrate VRAM migration into SVM restore and realign paths
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
` (6 preceding siblings ...)
2026-06-05 7:53 ` [PATCH v5 7/8] drm/amdgpu: integrate VRAM migration into SVM fault and prefetch paths Junhua Shen
@ 2026-06-05 7:53 ` Junhua Shen
7 siblings, 0 replies; 9+ messages in thread
From: Junhua Shen @ 2026-06-05 7:53 UTC (permalink / raw)
To: Alexander.Deucher, Felix.Kuehling, Christian.Koenig, Oak.Zeng,
Jenny-Jing.Liu, Philip.Yang, Xiaogang.Chen, Ray.Huang,
honglei1.huang, Lingshan.Zhu, simona
Cc: amd-gfx, dri-devel, Junhua.Shen
Integrate the VRAM migration infrastructure into the SVM restore and
boundary realignment paths to ensure correct interaction between
migration and the existing SVM lifecycle framework.
Suppress restore for realigned ranges:
- Add suppress_restore flag to amdgpu_svm_range
- Set flag before svm_restore_realign_boundary() evicts a crossing range
- Prevent migration notifier from enqueueing restore work while
realignment or VRAM migration is in progress
- Remove existing restore-list entry and drop its queue kref
- On realign failure, clear flag and re-enqueue restore for rollback
- Enqueue, worker retry, and put_if_dequeued paths respect the flag
Extend svm_restore_range() with migration support:
- Move amdgpu_svm_range_is_valid() check before map_ctx setup
- Set devmem_only = false since restore should not force VRAM placement
- Add device_private_page_owner for proper devmem identification
Signed-off-by: Junhua Shen <Junhua.Shen@amd.com>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c | 4 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h | 1 +
drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c | 76 +++++++++++++++----
3 files changed, 65 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
index 88923e6a0194..31229c4770b8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.c
@@ -728,11 +728,13 @@ amdgpu_svm_range_put_if_dequeued(struct amdgpu_svm *svm,
list_add_tail(&range->work_node, &svm->gc.list);
range->queue_state = AMDGPU_SVM_RANGE_IN_GC;
queue_gc = true;
- } else if (RESTORE_WORK(range->pending_ops)) {
+ } else if (RESTORE_WORK(range->pending_ops) &&
+ !READ_ONCE(range->suppress_restore)) {
list_add_tail(&range->work_node, &svm->restore.list);
range->queue_state = AMDGPU_SVM_RANGE_IN_RESTORE;
queue_restore = true;
} else {
+ range->pending_ops &= ~AMDGPU_SVM_RANGE_OP_RESTORE;
range->queue_state = AMDGPU_SVM_RANGE_NOT_QUEUED;
release_kref = true;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h
index 2d677f0965b4..4bf05c304564 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_svm_range.h
@@ -55,6 +55,7 @@ struct amdgpu_svm_range {
struct drm_gpusvm_range base;
struct list_head work_node;
bool gpu_mapped;
+ bool suppress_restore;
u8 queue_state;
u8 pending_ops;
unsigned long pending_start_page;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c
index 8bf00aed7f15..45a611479d1c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userptr.c
@@ -28,6 +28,7 @@
#include "amdgpu_svm_attr.h"
#include "amdgpu.h"
#include "amdgpu_amdkfd.h"
+#include "amdgpu_migrate.h"
#include <drm/drm_exec.h>
#include <drm/drm_gpusvm.h>
@@ -151,6 +152,11 @@ svm_restore_enqueue_work(struct amdgpu_svm *svm,
spin_lock(&svm->work_lock);
+ if (!UNMAP_WORK(pending_ops) && READ_ONCE(range->suppress_restore)) {
+ spin_unlock(&svm->work_lock);
+ return;
+ }
+
/* Deny any work if range is unmapped */
if (UNMAP_WORK(range->pending_ops)) {
spin_unlock(&svm->work_lock);
@@ -232,7 +238,7 @@ svm_restore_range(struct amdgpu_svm *svm, struct amdgpu_svm_range *range)
unsigned long range_start_page;
int ret;
struct drm_gpusvm_ctx map_ctx;
- bool devmem_possible, need_vram_migration;
+ bool devmem_possible;
amdgpu_svm_assert_locked(svm);
@@ -247,20 +253,20 @@ svm_restore_range(struct amdgpu_svm *svm, struct amdgpu_svm_range *range)
if (!attr_range || !amdgpu_svm_attr_has_access(attrs.access))
return 0;
+ if (amdgpu_svm_range_is_valid(svm, range, &attrs))
+ return 0;
+
devmem_possible = amdgpu_svm_devmem_possible(svm);
- need_vram_migration = devmem_possible &&
- amdgpu_svm_attr_prefer_vram(&attrs);
map_ctx = (struct drm_gpusvm_ctx){
.read_only = !!(attrs.flags & AMDGPU_SVM_ATTR_BIT_GPU_RO),
.devmem_possible = devmem_possible,
- .devmem_only = need_vram_migration,
+ .devmem_only = false,
.check_pages_threshold = devmem_possible ? SZ_64K : 0,
+ .device_private_page_owner = devmem_possible ?
+ AMDGPU_PGMAP_OWNER(svm->adev) : NULL,
};
- if (amdgpu_svm_range_is_valid(svm, range, &attrs))
- return 0;
-
AMDGPU_SVM_RANGE_DEBUG(range, "RESTORE - GET PAGES");
ret = amdgpu_svm_range_get_pages(svm, &range->base, &map_ctx);
@@ -289,6 +295,42 @@ static bool amdgpu_svm_nonretryable(int ret)
}
}
+static void
+svm_restore_suppress_range_restore(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range)
+{
+ bool release_kref = false;
+
+ spin_lock(&svm->work_lock);
+
+ WRITE_ONCE(range->suppress_restore, true);
+ range->pending_ops &= ~AMDGPU_SVM_RANGE_OP_RESTORE;
+
+ if (range->queue_state == AMDGPU_SVM_RANGE_IN_RESTORE) {
+ list_del_init(&range->work_node);
+ range->queue_state = AMDGPU_SVM_RANGE_NOT_QUEUED;
+ range->pending_start_page = ULONG_MAX;
+ range->pending_last_page = 0;
+ release_kref = true;
+ }
+
+ spin_unlock(&svm->work_lock);
+
+ if (release_kref)
+ drm_gpusvm_range_put(&range->base);
+}
+
+static void
+svm_restore_unsuppress_range_restore(struct amdgpu_svm *svm,
+ struct amdgpu_svm_range *range,
+ unsigned long start_page,
+ unsigned long last_page)
+{
+ WRITE_ONCE(range->suppress_restore, false);
+ svm_restore_enqueue_work(svm, range, start_page, last_page,
+ AMDGPU_SVM_RANGE_OP_RESTORE);
+}
+
static void amdgpu_svm_restore_worker(struct work_struct *w)
{
struct delayed_work *dwork = to_delayed_work(w);
@@ -334,17 +376,19 @@ static void amdgpu_svm_restore_worker(struct work_struct *w)
op_ctx.start_page,
op_ctx.last_page, ret);
spin_lock(&svm->work_lock);
- if (!UNMAP_WORK(op_ctx.range->pending_ops))
+ if (!UNMAP_WORK(op_ctx.range->pending_ops) &&
+ !READ_ONCE(op_ctx.range->suppress_restore)) {
op_ctx.range->pending_ops |=
AMDGPU_SVM_RANGE_OP_RESTORE;
- op_ctx.range->pending_start_page =
- min(op_ctx.range->pending_start_page,
- op_ctx.start_page);
- op_ctx.range->pending_last_page =
- max(op_ctx.range->pending_last_page,
- op_ctx.last_page);
+ op_ctx.range->pending_start_page =
+ min(op_ctx.range->pending_start_page,
+ op_ctx.start_page);
+ op_ctx.range->pending_last_page =
+ max(op_ctx.range->pending_last_page,
+ op_ctx.last_page);
+ need_resched = true;
+ }
spin_unlock(&svm->work_lock);
- need_resched = true;
} else {
AMDGPU_SVM_TRACE(
"restore work drop non retryable [0x%lx-0x%lx] ret=%d\n",
@@ -746,6 +790,7 @@ svm_restore_realign_boundary(struct amdgpu_svm *svm,
rs = drm_gpusvm_range_start(r) >> PAGE_SHIFT;
rl = (drm_gpusvm_range_end(r) >> PAGE_SHIFT) - 1;
+ svm_restore_suppress_range_restore(svm, svm_range);
amdgpu_svm_range_evict(svm, r);
if (svm_range->gpu_mapped) {
@@ -755,6 +800,7 @@ svm_restore_realign_boundary(struct amdgpu_svm *svm,
if (ret < 0) {
AMDGPU_SVM_TRACE("zap failed ret=%d [0x%lx-0x%lx]\n",
ret, rs, rl);
+ svm_restore_unsuppress_range_restore(svm, svm_range, rs, rl);
drm_exec_fini(&exec);
return ret;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-06-05 7:55 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 7:53 [PATCH v5 0/8] drm/amdgpu: SVM VRAM migration via drm_pagemap (unified XNACK on/off) Junhua Shen
2026-06-05 7:53 ` [PATCH v5 1/8] drm/amdgpu: add VRAM migration infrastructure for drm_pagemap Junhua Shen
2026-06-05 7:53 ` [PATCH v5 2/8] drm/amdgpu: implement drm_pagemap SDMA migration callbacks Junhua Shen
2026-06-05 7:53 ` [PATCH v5 3/8] drm/amdgpu: implement synchronous TTM eviction for SVM BOs Junhua Shen
2026-06-05 7:53 ` [PATCH v5 4/8] drm/amdgpu: hook up ZONE_DEVICE registration in device init and reset Junhua Shen
2026-06-05 7:53 ` [PATCH v5 5/8] drm/amdgpu: add SVM range migration helpers for drm_pagemap Junhua Shen
2026-06-05 7:53 ` [PATCH v5 6/8] drm/amdgpu: refactor SVM attr devmem_possible and prefer_vram API Junhua Shen
2026-06-05 7:53 ` [PATCH v5 7/8] drm/amdgpu: integrate VRAM migration into SVM fault and prefetch paths Junhua Shen
2026-06-05 7:53 ` [PATCH v5 8/8] drm/amdgpu: integrate VRAM migration into SVM restore and realign paths Junhua Shen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox