From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id CB675C61DC4 for ; Thu, 27 Aug 2026 16:45:09 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 79D9710EFE6; Thu, 27 Aug 2026 16:45:09 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="Ajt0OHMv"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) by gabe.freedesktop.org (Postfix) with ESMTPS id 0599110EA8A for ; Thu, 27 Aug 2026 16:45:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1787849108; x=1819385108; h=from:to:subject:date:message-id:mime-version: content-transfer-encoding; bh=eHovEDgjIeTnJCu/i3eKwP/YmkNS114Pa0gy8f9VZqg=; b=Ajt0OHMvz2H+KUExGmRda4JrxFvHBXQEeVUK8W5YYBwKOwbJNoI4SX/V Z5w1CFINnqeMlnHin0pNOgI5l4a2J31muTzhPJafURzLambXwSkeVQbGr 0ivLtc7bEYcCDRQkGdgecV6WLWA+TAI1cWgCi/5JZPUdnA1wzMQnFtkqL n6xeSRMDKIPMJA1YKP9ZkPDRjiE90yHSRhcpmkC3Yub9pPN7xYxGtzyrw ZiA7NLPDZKrmkJy0rrMVQrbCwaz7m3p8BuetUJW6rPtswdw2v7tTm/YNF AwFsZXM+p38m51AR36C5c5GpP85qxtJGMtkTZKl7dkxsiDCEPcqaQn5ol A==; X-CSE-ConnectionGUID: YRJUNqnARfa7/OcJl0T2bg== X-CSE-MsgGUID: I+hgxAHiS7CXET74PdcXWg== X-IronPort-AV: E=McAfee;i="6800,10657,11888"; a="98690922" X-IronPort-AV: E=Sophos;i="6.25,246,1779174000"; d="scan'208";a="98690922" Received: from fmviesa002.fm.intel.com ([10.60.135.142]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 27 Aug 2026 09:45:08 -0700 X-CSE-ConnectionGUID: aKIOwzRoTx2MnlXKH3xURQ== X-CSE-MsgGUID: o40mHEHxQ/+l+yfHJaeglA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,246,1779174000"; d="scan'208";a="291415033" Received: from mkosciow-mobl1.ger.corp.intel.com (HELO mwauld-desk.intel.com) ([10.245.244.196]) by fmviesa002-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 27 Aug 2026 09:45:06 -0700 From: Matthew Auld To: intel-xe@lists.freedesktop.org Subject: [CI v4] drm/xe/vram: Add early VRAM health check Date: Thu, 27 Aug 2026 17:44:57 +0100 Message-ID: <20260827164456.504540-2-matthew.auld@intel.com> X-Mailer: git-send-email 2.55.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: intel-xe@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel Xe graphics driver List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-xe-bounces@lists.freedesktop.org Sender: "Intel-xe" DO NOT REVIEW. Use the last page as a canary for BAR sizing, CCS sizing, identity map setup. If something is wrong the last page is where we will likely find it. Hit it with everything we have. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Matthew Auld --- drivers/gpu/drm/xe/display/xe_dsb_buffer.c | 2 +- drivers/gpu/drm/xe/display/xe_fb_pin.c | 2 +- drivers/gpu/drm/xe/xe_device.c | 12 +- drivers/gpu/drm/xe/xe_device.h | 2 +- drivers/gpu/drm/xe/xe_migrate.c | 64 +++++++ drivers/gpu/drm/xe/xe_migrate.h | 6 + drivers/gpu/drm/xe/xe_tile_types.h | 4 + drivers/gpu/drm/xe/xe_vram.c | 208 +++++++++++++++++++++ drivers/gpu/drm/xe/xe_vram.h | 10 + 9 files changed, 304 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c index a7158c73a14c..82974e933e8d 100644 --- a/drivers/gpu/drm/xe/display/xe_dsb_buffer.c +++ b/drivers/gpu/drm/xe/display/xe_dsb_buffer.c @@ -88,7 +88,7 @@ static void xe_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf) * both for weak ordering archs and discrete cards. */ xe_device_wmb(xe); - xe_device_l2_flush(xe); + xe_device_l2_flush(xe, false); } const struct intel_display_dsb_interface xe_display_dsb_interface = { diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 73469ea5f333..b46a2c32ac07 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -203,7 +203,7 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj, vma->node = dpt->ggtt_node[tile0->id]; /* Ensure DPT writes are flushed */ - xe_device_l2_flush(xe); + xe_device_l2_flush(xe, false); return 0; } diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 74d566693dfd..c1c4c8f61f37 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1051,6 +1051,8 @@ int xe_device_probe(struct xe_device *xe) if (err) return err; + xe_vram_reserve_memtest_bo(xe); + for_each_tile(tile, xe, id) { err = xe_tile_init(tile); if (err) @@ -1067,6 +1069,10 @@ int xe_device_probe(struct xe_device *xe) return err; } + err = xe_vram_memtest(xe); + if (err) + return err; + err = xe_pagefault_init(xe); if (err) return err; @@ -1270,7 +1276,7 @@ bool xe_device_is_l2_flush_optimized(struct xe_device *xe) return false; } -void xe_device_l2_flush(struct xe_device *xe) +void xe_device_l2_flush(struct xe_device *xe, bool force) { struct xe_gt *gt; @@ -1278,7 +1284,7 @@ void xe_device_l2_flush(struct xe_device *xe) if (!gt) return; - if (!XE_GT_WA(gt, 16023588340)) + if (!force && !XE_GT_WA(gt, 16023588340)) return; CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT); @@ -1333,7 +1339,7 @@ void xe_device_td_flush(struct xe_device *xe) if (XE_GT_WA(root_gt, 16023588340)) { /* A transient flush is not sufficient: flush the L2 */ - xe_device_l2_flush(xe); + xe_device_l2_flush(xe, false); } else { xe_guc_pc_apply_flush_freq_limit(&root_gt->uc.guc.pc); tdf_request_sync(xe); diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h index 6c4cfaebc44a..6d3d6d5eba29 100644 --- a/drivers/gpu/drm/xe/xe_device.h +++ b/drivers/gpu/drm/xe/xe_device.h @@ -205,7 +205,7 @@ u64 xe_device_uncanonicalize_addr(struct xe_device *xe, u64 address); bool xe_device_is_l2_flush_optimized(struct xe_device *xe); void xe_device_td_flush(struct xe_device *xe); -void xe_device_l2_flush(struct xe_device *xe); +void xe_device_l2_flush(struct xe_device *xe, bool force); static inline bool xe_device_wedged(struct xe_device *xe) { diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c index 75b83687f1b5..0f24308caaf6 100644 --- a/drivers/gpu/drm/xe/xe_migrate.c +++ b/drivers/gpu/drm/xe/xe_migrate.c @@ -2633,3 +2633,67 @@ void xe_migrate_job_lock_assert(struct xe_exec_queue *q) #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) #include "tests/xe_migrate.c" #endif + +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM) +int xe_migrate_debug_ccs_overlap(struct xe_migrate *m, + struct xe_bo *scratch_bo, + bool write_to_ccs) +{ + struct xe_device *xe = tile_to_xe(m->tile); + struct xe_gt *gt = m->tile->primary_gt; + struct dma_fence *fence; + struct xe_bb *bb; + struct xe_sched_job *job; + u64 first_page_dpa, clear_L0_ofs, scratch_dpa, scratch_L0_ofs; + + if (!xe_device_has_flat_ccs(xe)) + return 0; + + first_page_dpa = xe_vram_region_dpa_base(m->tile->mem.vram); + clear_L0_ofs = xe_migrate_vram_ofs(xe, first_page_dpa, true); + + scratch_dpa = xe_bo_addr(scratch_bo, 0, XE_PAGE_SIZE); + scratch_L0_ofs = xe_migrate_vram_ofs(xe, scratch_dpa, false); + + bb = xe_bb_new(gt, 32, xe->info.has_usm); + if (IS_ERR(bb)) { + drm_warn(&xe->drm, "Failed to create bb for VRAM overlap check\n"); + return PTR_ERR(bb); + } + + /* 4MB payload = 16KB CCS metadata. Covers up to 4MB of VRAM holes */ + if (write_to_ccs) { + emit_copy_ccs(gt, bb, clear_L0_ofs, true, + scratch_L0_ofs, false, SZ_4M); + } else { + emit_copy_ccs(gt, bb, scratch_L0_ofs, false, + clear_L0_ofs, true, SZ_4M); + } + + bb->cs[bb->len++] = MI_BATCH_BUFFER_END; + + job = xe_bb_create_migration_job(m->q, bb, + xe_migrate_batch_base(m, xe->info.has_usm), + 0); + if (!IS_ERR(job)) { + xe_sched_job_add_migrate_flush(job, MI_FLUSH_DW_CCS); + + mutex_lock(&m->job_mutex); + xe_sched_job_arm(job); + + fence = dma_fence_get(&job->drm.s_fence->finished); + xe_sched_job_push(job); + mutex_unlock(&m->job_mutex); + + dma_fence_wait(fence, false); + dma_fence_put(fence); + } else { + drm_warn(&xe->drm, "Failed to create job for VRAM overlap check\n"); + xe_bb_free(bb, NULL); + return PTR_ERR(job); + } + + xe_bb_free(bb, NULL); + return 0; +} +#endif diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h index c3a268b01768..a9acc62f78f0 100644 --- a/drivers/gpu/drm/xe/xe_migrate.h +++ b/drivers/gpu/drm/xe/xe_migrate.h @@ -182,4 +182,10 @@ static inline void xe_migrate_job_lock_assert(struct xe_exec_queue *q) void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q); void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q); +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM) +int xe_migrate_debug_ccs_overlap(struct xe_migrate *m, + struct xe_bo *scratch_bo, + bool write_to_ccs); +#endif + #endif diff --git a/drivers/gpu/drm/xe/xe_tile_types.h b/drivers/gpu/drm/xe/xe_tile_types.h index 0048100ccb72..e1368c04846a 100644 --- a/drivers/gpu/drm/xe/xe_tile_types.h +++ b/drivers/gpu/drm/xe/xe_tile_types.h @@ -97,6 +97,10 @@ struct xe_tile { * Only main GT has page reclaim list allocations. */ struct xe_sa_manager *reclaim_pool; +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM) + /** @mem.memtest_bo: VRAM overlap check BO */ + struct xe_bo *memtest_bo; +#endif } mem; /** @sriov: tile level virtualization data */ diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c index 23eb7edbdd57..8db772ba937c 100644 --- a/drivers/gpu/drm/xe/xe_vram.c +++ b/drivers/gpu/drm/xe/xe_vram.c @@ -17,8 +17,11 @@ #include "xe_device.h" #include "xe_force_wake.h" #include "xe_gt_mcr.h" +#include "xe_map.h" +#include "xe_migrate.h" #include "xe_mmio.h" #include "xe_sriov.h" +#include "xe_tile.h" #include "xe_tile_sriov_vf.h" #include "xe_ttm_vram_mgr.h" #include "xe_vram.h" @@ -387,3 +390,208 @@ resource_size_t xe_vram_region_actual_physical_size(const struct xe_vram_region return vram ? vram->actual_physical_size : 0; } EXPORT_SYMBOL_IF_KUNIT(xe_vram_region_actual_physical_size); + +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM) +static void memtest_bo_cleanup(void *arg) +{ + struct xe_device *xe = arg; + + xe_vram_free_memtest_bos(xe); +} + +void xe_vram_reserve_memtest_bo(struct xe_device *xe) +{ + struct xe_tile *tile; + u8 id; + + if (IS_SRIOV_VF(xe)) + return; + + for_each_tile(tile, xe, id) { + u64 vram_size; + + if (!tile->mem.vram) + continue; + + if (tile->mem.vram->io_size < tile->mem.vram->usable_size) { + drm_info(&xe->drm, + "Tile %d: Small-BAR system detected, skipping VRAM memtest\n", + id); + continue; + } + + vram_size = tile->mem.vram->usable_size; + + tile->mem.memtest_bo = xe_bo_create_pin_map_at_novm(xe, tile, SZ_64K, + vram_size - SZ_64K, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile), + 0, false); + if (IS_ERR(tile->mem.memtest_bo)) { + drm_warn(&xe->drm, "Tile %d: Failed to reserve memtest BO\n", id); + tile->mem.memtest_bo = NULL; + continue; + } + + drm_info(&xe->drm, "Tile %d: Reserved memtest BO at offset 0x%llx\n", + id, vram_size - SZ_64K); + } + + devm_add_action_or_reset(xe->drm.dev, memtest_bo_cleanup, xe); +} + +void xe_vram_free_memtest_bos(struct xe_device *xe) +{ + struct xe_tile *tile; + u8 id; + + for_each_tile(tile, xe, id) { + if (tile->mem.memtest_bo) { + xe_bo_unpin_map_no_vm(tile->mem.memtest_bo); + tile->mem.memtest_bo = NULL; + } + } +} + +int xe_vram_memtest(struct xe_device *xe) +{ + struct xe_tile *tile; + u8 id; + int err = 0; + + if (IS_SRIOV_VF(xe)) + return 0; + + for_each_tile(tile, xe, id) { + struct xe_bo *last_page_bo = tile->mem.memtest_bo; + struct dma_fence *fence; + bool overlap = false; + int i; + u8 val; + + if (!last_page_bo || !tile->migrate) + continue; + + drm_info(&xe->drm, "Tile %d: Running VRAM memtest...\n", id); + + /* 1. CPU write and readback first and last byte of the last page */ + xe_map_wr(xe, &last_page_bo->vmap, 0, u8, 0xA5); + xe_map_wr(xe, &last_page_bo->vmap, SZ_64K - 1, u8, 0x5A); + + val = xe_map_rd(xe, &last_page_bo->vmap, 0, u8); + if (drm_WARN(&xe->drm, val != 0xA5, + "Tile %d: CPU memtest failed at offset 0 (expected 0xA5, got 0x%02x)\n", + id, val)) { + err = -EIO; + goto unpin; + } + + val = xe_map_rd(xe, &last_page_bo->vmap, SZ_64K - 1, u8); + if (drm_WARN(&xe->drm, val != 0x5A, + "Tile %d: CPU memtest failed at offset 65535 (expected 0x5A, got 0x%02x)\n", + id, val)) { + err = -EIO; + goto unpin; + } + + /* 2. Non-CCS access via GPU on the last page */ + xe_bo_lock(last_page_bo, false); + fence = xe_migrate_clear(tile->migrate, last_page_bo, + last_page_bo->ttm.resource, + XE_MIGRATE_CLEAR_FLAG_BO_DATA); + xe_bo_unlock(last_page_bo); + + if (!IS_ERR(fence)) { + dma_fence_wait(fence, false); + dma_fence_put(fence); + } else { + err = PTR_ERR(fence); + goto unpin; + } + + val = xe_map_rd(xe, &last_page_bo->vmap, 0, u8); + if (drm_WARN(&xe->drm, val != 0x00, + "Tile %d: GPU memtest clear failed at offset 0 (expected 0x00, got 0x%02x)\n", + id, val)) { + err = -EIO; + goto unpin; + } + + if (xe_device_has_flat_ccs(xe) && GRAPHICS_VERx100(xe) >= 2000) { + struct xe_bo *scratch_bo_before; + struct xe_bo *scratch_bo_after; + + scratch_bo_before = xe_bo_create_pin_map_novm(xe, tile, SZ_1M, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile), + false); + if (IS_ERR(scratch_bo_before)) { + err = PTR_ERR(scratch_bo_before); + goto unpin; + } + + scratch_bo_after = xe_bo_create_pin_map_novm(xe, tile, SZ_1M, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile), + false); + if (IS_ERR(scratch_bo_after)) { + xe_bo_unpin_map_no_vm(scratch_bo_before); + err = PTR_ERR(scratch_bo_after); + goto unpin; + } + + /* Save original CCS metadata for PA 0 */ + err = xe_migrate_debug_ccs_overlap(tile->migrate, scratch_bo_before, false); + if (err) + goto unpin; + + /* Fill last page with CPU */ + xe_map_memset(xe, &last_page_bo->vmap, 0, 0x5A, SZ_64K); + xe_device_wmb(xe); + xe_device_l2_flush(xe, true); + + /* Use GPU to clear CCS state for PA 0 */ + xe_map_memset(xe, &scratch_bo_after->vmap, 0, 0x00, SZ_1M); + err = xe_migrate_debug_ccs_overlap(tile->migrate, scratch_bo_after, true); + if (err) + goto unpin; + xe_device_l2_flush(xe, true); + + /* Check if last_page_bo was corrupted by the GPU CCS clear */ + for (i = 0; i < SZ_64K; i += 8) { + u64 payload = xe_map_rd(xe, &last_page_bo->vmap, i, u64); + + if (payload != 0x5A5A5A5A5A5A5A5AULL) { + overlap = true; + break; + } + } + + /* Restore original CCS metadata for PA 0 */ + err = xe_migrate_debug_ccs_overlap(tile->migrate, scratch_bo_before, true); + if (err) + drm_warn(&xe->drm, "Failed to restore CCS metadata\n"); + + xe_bo_unpin_map_no_vm(scratch_bo_before); + xe_bo_unpin_map_no_vm(scratch_bo_after); + } + + if (drm_WARN(&xe->drm, overlap, + "Tile %d: VRAM bounds overlap CCS region! VRAM sizing is incorrect.\n", + id)) { + err = -EINVAL; + goto unpin; + } + + drm_info(&xe->drm, "Tile %d: VRAM memtest completed.\n", id); + +unpin: + if (err) + break; + } + + xe_vram_free_memtest_bos(xe); + + return err; +} +#endif diff --git a/drivers/gpu/drm/xe/xe_vram.h b/drivers/gpu/drm/xe/xe_vram.h index dd1c8bf17922..0649b21c859a 100644 --- a/drivers/gpu/drm/xe/xe_vram.h +++ b/drivers/gpu/drm/xe/xe_vram.h @@ -23,4 +23,14 @@ resource_size_t xe_vram_region_dpa_base(const struct xe_vram_region *vram); resource_size_t xe_vram_region_usable_size(const struct xe_vram_region *vram); resource_size_t xe_vram_region_actual_physical_size(const struct xe_vram_region *vram); +#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEM) +void xe_vram_reserve_memtest_bo(struct xe_device *xe); +void xe_vram_free_memtest_bos(struct xe_device *xe); +int xe_vram_memtest(struct xe_device *xe); +#else +static inline void xe_vram_reserve_memtest_bo(struct xe_device *xe) {} +static inline void xe_vram_free_memtest_bos(struct xe_device *xe) {} +static inline int xe_vram_memtest(struct xe_device *xe) { return 0; } +#endif + #endif -- 2.55.0