diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c index cb24e001b142..80f62634fae5 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -45,12 +45,18 @@ DECLARE_FAULT_ATTR(gt_reset_failure); DECLARE_FAULT_ATTR(inject_csc_hw_error); DECLARE_FAULT_ATTR(wedge_cold_reset); +DECLARE_FAULT_ATTR(inject_mempage_offline); static bool csc_hw_error_available(struct xe_device *xe) { return !IS_SRIOV_VF(xe) && xe->info.platform == XE_BATTLEMAGE; } +static bool is_crescent_island_pf(struct xe_device *xe) +{ + return !IS_SRIOV_VF(xe) && xe->info.platform == XE_CRESCENTISLAND; +} + /* * Fault injection table. Each entry registers a debugfs attribute; add a * matching FAULT_ACTION() below for every entry added here. @@ -67,6 +73,9 @@ static struct { .is_visible = csc_hw_error_available }, { .name = "wedge_cold_reset", .attr = &wedge_cold_reset }, + { .name = "inject_mempage_offline", + .attr = &inject_mempage_offline, + .is_visible = is_crescent_island_pf }, }; /* @@ -82,6 +91,40 @@ bool xe_fault_##name(void) \ FAULT_ACTION(gt_reset, gt_reset_failure) FAULT_ACTION(csc_hw_error, inject_csc_hw_error) FAULT_ACTION(wedge_cold_reset, wedge_cold_reset) +FAULT_ACTION(mempage_offline, inject_mempage_offline) + +static ssize_t inject_mempage_offline_trigger(struct file *f, + const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct xe_device *xe = file_inode(f)->i_private; + struct xe_tile *tile = xe_device_get_root_tile(xe); + struct xe_vram_region *vr = tile->mem.vram; + u64 pfn; + int ret; + + if (!vr) + return -ENODEV; + + ret = kstrtou64_from_user(ubuf, size, 0, &pfn); + if (ret) + return ret; + + if (!xe_fault_mempage_offline()) + return size; + + xe_warn(xe, "Page offlining test interface accessed. Notice: Offlined or reserved memory pages cannot be reclaimed dynamically. A driver rebind (unbind and bind loop) is required post-test to clean up.\n"); + if (pfn == 0) + return xe_ttm_vram_inject_fault(xe) ?: size; + + /* User provided PFN - convert to DPA and inject */ + return xe_ttm_vram_handle_addr_fault(xe, pfn << PAGE_SHIFT) ?: size; +} + +static const struct file_operations inject_mempage_offline_fops = { + .owner = THIS_MODULE, + .write = inject_mempage_offline_trigger, +}; static void xe_fault_inject_debugfs_register(struct xe_device *xe, struct dentry *root) @@ -96,6 +139,11 @@ static void xe_fault_inject_debugfs_register(struct xe_device *xe, fault_create_debugfs_attr(xe_fault_inject_entry[i].name, root, xe_fault_inject_entry[i].attr); } + + if (is_crescent_island_pf(xe)) { + debugfs_create_file("inject_mempage_offline_trigger", 0200, + root, xe, &inject_mempage_offline_fops); + } } static void read_residency_counter(struct xe_device *xe, struct xe_mmio *mmio, diff --git a/drivers/gpu/drm/xe/xe_debugfs.h b/drivers/gpu/drm/xe/xe_debugfs.h index 0dcd28fd7dc0..88d91c78036b 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.h +++ b/drivers/gpu/drm/xe/xe_debugfs.h @@ -14,11 +14,13 @@ struct xe_device; bool xe_fault_gt_reset(void); bool xe_fault_csc_hw_error(void); bool xe_fault_wedge_cold_reset(void); +bool xe_fault_mempage_offline(void); void xe_debugfs_register(struct xe_device *xe); #else static inline bool xe_fault_gt_reset(void) { return false; } static inline bool xe_fault_csc_hw_error(void) { return false; } static inline bool xe_fault_wedge_cold_reset(void) { return false; } +static inline bool xe_fault_mempage_offline(void) { return false; } static inline void xe_debugfs_register(struct xe_device *xe) { } #endif diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c index 6aa24eb064eb..9a514d983e90 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c @@ -944,6 +944,64 @@ int xe_ttm_vram_handle_addr_fault(struct xe_device *xe, u64 addr) } EXPORT_SYMBOL(xe_ttm_vram_handle_addr_fault); +/** + * xe_ttm_vram_inject_fault - Inject a VRAM page fault for testing + * @xe: xe device instance + * + * Picks the last unallocated VRAM page and reports it as faulted + * via xe_ttm_vram_handle_addr_fault(). Used by the fault-inject + * debugfs interface for testing page offlining. + * + * Note: Executing this test will permanently retire the allocated + * memory tracking pages. The driver must be rebinded (unbind and bind) + * post-test execution to reclaim the reserved space, as these pages + * cannot be freed or reclaimed dynamically while the current instance + * remains active. + * + * Return: 0 on success, negative error code on failure. + */ +int xe_ttm_vram_inject_fault(struct xe_device *xe) +{ + struct xe_tile *tile = xe_device_get_root_tile(xe); + struct xe_vram_region *vr = tile->mem.vram; + struct xe_ttm_vram_mgr *vram_mgr = &vr->ttm; + struct gpu_buddy *mm = &vram_mgr->mm; + u64 addr; + + if (vr->actual_physical_size < PAGE_SIZE) + return -ENOSPC; + + addr = vr->actual_physical_size - PAGE_SIZE; + while (addr < vr->actual_physical_size) { + struct gpu_buddy_block *block; + bool found = false; + + scoped_guard(mutex, &vram_mgr->lock) { + block = gpu_buddy_allocated_addr_to_block(mm, addr); + if (!block) + found = true; + } + + /* + * Intentional race window: xe_ttm_vram_handle_addr_fault() + * re-acquires vram_mgr->lock internally, so we cannot hold + * it here. A concurrent allocation claiming this page between + * the two calls is an acceptable false negative for this + * test-only path. + */ + if (found) + return xe_ttm_vram_handle_addr_fault(xe, addr + vr->dpa_base); + + cond_resched(); + if (addr == 0) + break; + addr -= PAGE_SIZE; + } + + return -ENOSPC; +} +EXPORT_SYMBOL(xe_ttm_vram_inject_fault); + static int vram_bad_pages_show(struct seq_file *m, void *unused) { struct xe_device *xe = m->private; diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h index 48f39fd20e44..d77f067d197b 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h @@ -32,6 +32,7 @@ u64 xe_ttm_vram_get_cpu_visible_size(struct ttm_resource_manager *man); void xe_ttm_vram_get_used(struct ttm_resource_manager *man, u64 *used, u64 *used_visible); int xe_ttm_vram_handle_addr_fault(struct xe_device *xe, u64 addr); +int xe_ttm_vram_inject_fault(struct xe_device *xe); static inline struct xe_ttm_vram_mgr_resource * to_xe_ttm_vram_mgr_resource(struct ttm_resource *res)