Hi, Himal,
On Wed, 2025-04-30 at 17:48 +0530, Himal Prasad Ghimiray wrote:
From: Matthew Brost matthew.brost@intel.com
Mixing GPU and CPU atomics does not work unless a strict migration policy of GPU atomics must be device memory. Enforce a policy of must be in VRAM with a retry loop of 3 attempts, if retry loop fails abort fault.
Removing always_migrate_to_vram modparam as we now have real migration policy.
v2: - Only retry migration on atomics - Drop alway migrate modparam v3: - Only set vram_only on DGFX (Himal) - Bail on get_pages failure if vram_only and retry count exceeded (Himal) - s/vram_only/devmem_only - Update xe_svm_range_is_valid to accept devmem_only argument v4: - Fix logic bug get_pages failure v5: - Fix commit message (Himal) - Mention removing always_migrate_to_vram in commit message (Lucas) - Fix xe_svm_range_is_valid to check for devmem pages - Bail on devmem_only && !migrate_devmem (Thomas) v6: - Add READ_ONCE barriers for opportunistic checks (Thomas)
Fixes: 2f118c949160 ("drm/xe: Add SVM VRAM migration") Cc: stable@vger.kernel.org Signed-off-by: Himal Prasad Ghimiray himal.prasad.ghimiray@intel.com Signed-off-by: Matthew Brost matthew.brost@intel.com Acked-by: Himal Prasad Ghimiray himal.prasad.ghimiray@intel.com
drivers/gpu/drm/xe/xe_module.c | 3 - drivers/gpu/drm/xe/xe_module.h | 1 - drivers/gpu/drm/xe/xe_svm.c | 103 ++++++++++++++++++++++++------- -- drivers/gpu/drm/xe/xe_svm.h | 5 -- include/drm/drm_gpusvm.h | 40 ++++++++----- 5 files changed, 103 insertions(+), 49 deletions(-)
diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c index 64bf46646544..e4742e27e2cd 100644 --- a/drivers/gpu/drm/xe/xe_module.c +++ b/drivers/gpu/drm/xe/xe_module.c @@ -30,9 +30,6 @@ struct xe_modparam xe_modparam = { module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600); MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size(in MiB), must be power of 2"); -module_param_named(always_migrate_to_vram, xe_modparam.always_migrate_to_vram, bool, 0444); -MODULE_PARM_DESC(always_migrate_to_vram, "Always migrate to VRAM on GPU fault");
module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h index 84339e509c80..5a3bfea8b7b4 100644 --- a/drivers/gpu/drm/xe/xe_module.h +++ b/drivers/gpu/drm/xe/xe_module.h @@ -12,7 +12,6 @@ struct xe_modparam { bool force_execlist; bool probe_display;
- bool always_migrate_to_vram;
u32 force_vram_bar_size; int guc_log_level; char *guc_firmware_path; diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index 890f6b2f40e9..dcc84e65ca96 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -16,8 +16,12 @@ static bool xe_svm_range_in_vram(struct xe_svm_range *range) {
- /* Not reliable without notifier lock */
- return range->base.flags.has_devmem_pages;
- /* Not reliable without notifier lock, opportunistic only */
- struct drm_gpusvm_range_flags flags = {
.__flags = READ_ONCE(range->base.flags.__flags),
- };
- return flags.has_devmem_pages;
} static bool xe_svm_range_has_vram_binding(struct xe_svm_range *range) @@ -650,9 +654,13 @@ void xe_svm_fini(struct xe_vm *vm) } static bool xe_svm_range_is_valid(struct xe_svm_range *range,
struct xe_tile *tile)
struct xe_tile *tile,
bool devmem_only)
{
- return (range->tile_present & ~range->tile_invalidated) &
BIT(tile->id);
- /* Not reliable without notifier lock, opportunistic only */
- return ((READ_ONCE(range->tile_present) &
~READ_ONCE(range->tile_invalidated)) & BIT(tile-
id)) &&
(!devmem_only || xe_svm_range_in_vram(range));
}
Hmm, TBH I had something more elaborate in mind:
https://lore.kernel.org/intel-xe/b5569de8cc036e23b976f21a51c4eb5ca104d4bb.ca...
(Separate function for lockless access and a lockdep assert for locked access + similar documentation as the functions I mentioned there + a "Pairs with" comment.
Thanks, Thomas
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) @@ -726,6 +734,36 @@ static int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile, } #endif +static bool supports_4K_migration(struct xe_device *xe) +{
- if (xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K)
return false;
- return true;
+}
+static bool xe_svm_range_needs_migrate_to_vram(struct xe_svm_range *range,
struct xe_vma *vma)
+{
- struct xe_vm *vm = range_to_vm(&range->base);
- u64 range_size = xe_svm_range_size(range);
- if (!range->base.flags.migrate_devmem)
return false;
- /* Not reliable without notifier lock, opportunistic only */
- if (xe_svm_range_in_vram(range)) {
drm_dbg(&vm->xe->drm, "Range is already in VRAM\n");
return false;
- }
- if (range_size <= SZ_64K && !supports_4K_migration(vm->xe))
{
drm_dbg(&vm->xe->drm, "Platform doesn't support
SZ_4K range migration\n");
return false;
- }
- return true;
+} /** * xe_svm_handle_pagefault() - SVM handle page fault @@ -750,12 +788,15 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR), .check_pages_threshold = IS_DGFX(vm->xe) && IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ? SZ_64K : 0,
.devmem_only = atomic && IS_DGFX(vm->xe) &&
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
}; struct xe_svm_range *range; struct drm_gpusvm_range *r; struct drm_exec exec; struct dma_fence *fence; struct xe_tile *tile = gt_to_tile(gt);
- int migrate_try_count = ctx.devmem_only ? 3 : 1;
ktime_t end = 0; int err; @@ -776,24 +817,30 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, if (IS_ERR(r)) return PTR_ERR(r);
- if (ctx.devmem_only && !r->flags.migrate_devmem)
return -EACCES;
range = to_xe_range(r);
- if (xe_svm_range_is_valid(range, tile))
- if (xe_svm_range_is_valid(range, tile, ctx.devmem_only))
return 0; range_debug(range, "PAGE FAULT");
- /* XXX: Add migration policy, for now migrate range once */
- if (!range->skip_migrate && range->base.flags.migrate_devmem
&&
- xe_svm_range_size(range) >= SZ_64K) {
range->skip_migrate = true;
- if (--migrate_try_count >= 0 &&
- xe_svm_range_needs_migrate_to_vram(range, vma)) {
err = xe_svm_alloc_vram(vm, tile, range, &ctx); if (err) {
drm_dbg(&vm->xe->drm,
"VRAM allocation failed, falling
back to "
"retrying fault, asid=%u,
errno=%pe\n",
vm->usm.asid, ERR_PTR(err));
goto retry;
if (migrate_try_count || !ctx.devmem_only) {
drm_dbg(&vm->xe->drm,
"VRAM allocation failed,
falling back to retrying fault, asid=%u, errno=%pe\n",
vm->usm.asid, ERR_PTR(err));
goto retry;
} else {
drm_err(&vm->xe->drm,
"VRAM allocation failed,
retry count exceeded, asid=%u, errno=%pe\n",
vm->usm.asid, ERR_PTR(err));
return err;
}
} } @@ -801,15 +848,22 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, err = drm_gpusvm_range_get_pages(&vm->svm.gpusvm, r, &ctx); /* Corner where CPU mappings have changed */ if (err == -EOPNOTSUPP || err == -EFAULT || err == -EPERM) {
if (err == -EOPNOTSUPP) {
range_debug(range, "PAGE FAULT - EVICT
PAGES");
drm_gpusvm_range_evict(&vm->svm.gpusvm,
&range->base);
if (migrate_try_count > 0 || !ctx.devmem_only) {
if (err == -EOPNOTSUPP) {
range_debug(range, "PAGE FAULT -
EVICT PAGES");
drm_gpusvm_range_evict(&vm-
svm.gpusvm,
&range-
base);
}
drm_dbg(&vm->xe->drm,
"Get pages failed, falling back to
retrying, asid=%u, gpusvm=%p, errno=%pe\n",
vm->usm.asid, &vm->svm.gpusvm,
ERR_PTR(err));
range_debug(range, "PAGE FAULT - RETRY
PAGES");
goto retry;
} else {
drm_err(&vm->xe->drm,
"Get pages failed, retry count
exceeded, asid=%u, gpusvm=%p, errno=%pe\n",
vm->usm.asid, &vm->svm.gpusvm,
ERR_PTR(err)); }
drm_dbg(&vm->xe->drm,
"Get pages failed, falling back to retrying,
asid=%u, gpusvm=%p, errno=%pe\n",
vm->usm.asid, &vm->svm.gpusvm,
ERR_PTR(err));
range_debug(range, "PAGE FAULT - RETRY PAGES");
goto retry;
} if (err) { range_debug(range, "PAGE FAULT - FAIL PAGE COLLECT"); @@ -843,9 +897,6 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, } drm_exec_fini(&exec);
- if (xe_modparam.always_migrate_to_vram)
range->skip_migrate = false;
dma_fence_wait(fence, false); dma_fence_put(fence); diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index 3d441eb1f7ea..0e1f376a7471 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -39,11 +39,6 @@ struct xe_svm_range { * range. Protected by GPU SVM notifier lock. */ u8 tile_invalidated;
- /**
* @skip_migrate: Skip migration to VRAM, protected by GPU
fault handler
* locking.
*/
- u8 skip_migrate :1;
}; /** diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h index 9fd25fc880a4..653d48dbe1c1 100644 --- a/include/drm/drm_gpusvm.h +++ b/include/drm/drm_gpusvm.h @@ -185,6 +185,31 @@ struct drm_gpusvm_notifier { } flags; }; +/**
- struct drm_gpusvm_range_flags - Structure representing a GPU SVM
range flags
- @migrate_devmem: Flag indicating whether the range can be
migrated to device memory
- @unmapped: Flag indicating if the range has been unmapped
- @partial_unmap: Flag indicating if the range has been partially
unmapped
- @has_devmem_pages: Flag indicating if the range has devmem pages
- @has_dma_mapping: Flag indicating if the range has a DMA mapping
- @__flags: Flags for range in u16 form (used for READ_ONCE)
- */
+struct drm_gpusvm_range_flags {
- union {
struct {
/* All flags below must be set upon creation
*/
u16 migrate_devmem : 1;
/* All flags below must be set / cleared
under notifier lock */
u16 unmapped : 1;
u16 partial_unmap : 1;
u16 has_devmem_pages : 1;
u16 has_dma_mapping : 1;
};
u16 __flags;
- };
+};
/** * struct drm_gpusvm_range - Structure representing a GPU SVM range * @@ -198,11 +223,6 @@ struct drm_gpusvm_notifier { * @dpagemap: The struct drm_pagemap of the device pages we're dma- mapping. * Note this is assuming only one drm_pagemap per range is allowed. * @flags: Flags for range
- @flags.migrate_devmem: Flag indicating whether the range can be
migrated to device memory
- @flags.unmapped: Flag indicating if the range has been unmapped
- @flags.partial_unmap: Flag indicating if the range has been
partially unmapped
- @flags.has_devmem_pages: Flag indicating if the range has devmem
pages
- @flags.has_dma_mapping: Flag indicating if the range has a DMA
mapping * * This structure represents a GPU SVM range used for tracking memory ranges * mapped in a DRM device. @@ -216,15 +236,7 @@ struct drm_gpusvm_range { unsigned long notifier_seq; struct drm_pagemap_device_addr *dma_addr; struct drm_pagemap *dpagemap;
- struct {
/* All flags below must be set upon creation */
u16 migrate_devmem : 1;
/* All flags below must be set / cleared under
notifier lock */
u16 unmapped : 1;
u16 partial_unmap : 1;
u16 has_devmem_pages : 1;
u16 has_dma_mapping : 1;
- } flags;
- struct drm_gpusvm_range_flags flags;
}; /**