From 9f49e185ee31d5d5fc8ea1a23aaaba5a4374a949 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Thu, 12 Mar 2026 09:29:23 -0700 Subject: drm/xe/wa: Drop redundant entries for Wa_16021867713 & Wa_14019449301 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Xe2_HPM-specific RTP table entries for Wa_16021867713 and Wa_14019449301 were removed by commit 941f538b0af8 ("drm/xe: Consolidate workaround entries for Wa_16021867713") and commit aa0f0a678370 ("drm/xe: Consolidate workaround entries for Wa_14019449301") in favor of alternate entries earlier in the table that cover a wider range of IP versions. However these Xe2_HPM-specific entries were accidentally resurrected during a backmerge, which causes the Xe driver to complain on probe about two entries trying to program the same registers+bits: <3> [48.491155] xe 0000:03:00.0: [drm] *ERROR* Tile0: GT1: discarding save-restore reg 1c3f1c (clear: 00000008, set: 00000008, masked: no, mcr: no): ret=-22 <3> [48.491211] xe 0000:03:00.0: [drm] *ERROR* Tile0: GT1: discarding save-restore reg 1d3f1c (clear: 00000008, set: 00000008, masked: no, mcr: no): ret=-22 <3> [48.491225] xe 0000:03:00.0: [drm] *ERROR* Tile0: GT1: discarding save-restore reg 1c3f08 (clear: 00000020, set: 00000020, masked: no, mcr: no): ret=-22 <3> [48.491238] xe 0000:03:00.0: [drm] *ERROR* Tile0: GT1: discarding save-restore reg 1d3f08 (clear: 00000020, set: 00000020, masked: no, mcr: no): ret=-22 Re-drop the redundant Xe2_HPM-specific entries to eliminate the dmesg errors. Fixes: 58351f46de26 ("Merge v7.0-rc3 into drm-next") Cc: Simona Vetter Cc: Rodrigo Vivi Cc: Matthew Brost Cc: Thomas Hellström Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/7608 Reviewed-by: Simona Vetter Link: https://patch.msgid.link/20260312-wa_merge_fix-v1-1-2ec6607f1e0c@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/xe/xe_wa.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c index 0eb96abc27df..d2fc1f50c508 100644 --- a/drivers/gpu/drm/xe/xe_wa.c +++ b/drivers/gpu/drm/xe/xe_wa.c @@ -263,20 +263,6 @@ static const struct xe_rtp_entry_sr gt_was[] = { LSN_DIM_Z_WGT(1))) }, - /* Xe2_HPM */ - - { XE_RTP_NAME("16021867713"), - XE_RTP_RULES(MEDIA_VERSION(1301), - ENGINE_CLASS(VIDEO_DECODE)), - XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)), - XE_RTP_ENTRY_FLAG(FOREACH_ENGINE), - }, - { XE_RTP_NAME("14019449301"), - XE_RTP_RULES(MEDIA_VERSION(1301), ENGINE_CLASS(VIDEO_DECODE)), - XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F08(0), CG3DDISHRS_CLKGATE_DIS)), - XE_RTP_ENTRY_FLAG(FOREACH_ENGINE), - }, - /* Xe3_LPG */ { XE_RTP_NAME("14021871409"), -- cgit v1.2.3 From 341a2c99c87ce6f62c6f4423fa641a39f0966bff Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Thu, 12 Mar 2026 21:32:45 +0530 Subject: drm/xe/uapi: Fix kernel-doc for DRM_XE_VM_BIND_FLAG_DECOMPRESS There is kernel-doc warning for DRM_XE_VM_BIND_FLAG_DECOMPRESS: ./include/uapi/drm/xe_drm.h:1060: WARNING: Block quote ends without a blank line; unexpected unindent. Fix the warning by adding the missing '%' prefix to DRM_XE_VM_BIND_FLAG_DECOMPRESS in the kernel-doc list entry for struct drm_xe_vm_bind_op. Fixes: 2270bd7124f4 ("drm/xe: add VM_BIND DECOMPRESS uapi flag") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603121515.gEMrFlTL-lkp@intel.com/ Cc: Matthew Auld Signed-off-by: Nitin Gote Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260312160244.809849-2-nitin.r.gote@intel.com Signed-off-by: Tejas Upadhyay --- include/uapi/drm/xe_drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 0497b85fa12a..f8b2afb20540 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -1057,7 +1057,7 @@ struct drm_xe_vm_destroy { * not invoke autoreset. Neither will stack variables going out of scope. * Therefore it's recommended to always explicitly reset the madvises when * freeing the memory backing a region used in a &DRM_IOCTL_XE_MADVISE call. - * - DRM_XE_VM_BIND_FLAG_DECOMPRESS - Request on-device decompression for a MAP. + * - %DRM_XE_VM_BIND_FLAG_DECOMPRESS - Request on-device decompression for a MAP. * When set on a MAP bind operation, request the driver schedule an on-device * in-place decompression (via the migrate/resolve path) for the GPU mapping * created by this bind. Only valid for DRM_XE_VM_BIND_OP_MAP; usage on -- cgit v1.2.3 From c85ec5c5753a46b5c2aea1292536487be9470ffe Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 2 Mar 2026 16:17:33 -0800 Subject: drm/xe/guc: Fail immediately on GuC load error By using the same variable for both the return of poll_timeout_us and the return of the polled function guc_wait_ucode, the return value of the latter is overwritten and lost after exiting the polling loop. Since guc_wait_ucode returns -1 on GuC load failure, we lose that information and always continue as if the GuC had been loaded correctly. This is fixed by simply using 2 separate variables. Fixes: a4916b4da448 ("drm/xe/guc: Refactor GuC load to use poll_timeout_us()") Signed-off-by: Daniele Ceraolo Spurio Reviewed-by: Matthew Brost Signed-off-by: Vinay Belgaumkar Link: https://patch.msgid.link/20260303001732.2540493-2-daniele.ceraolospurio@intel.com --- drivers/gpu/drm/xe/xe_guc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index e75653a5e797..ad18be4688c5 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -1176,14 +1176,14 @@ static int guc_wait_ucode(struct xe_guc *guc) struct xe_guc_pc *guc_pc = >->uc.guc.pc; u32 before_freq, act_freq, cur_freq; u32 status = 0, tries = 0; + int load_result, ret; ktime_t before; u64 delta_ms; - int ret; before_freq = xe_guc_pc_get_act_freq(guc_pc); before = ktime_get(); - ret = poll_timeout_us(ret = guc_load_done(gt, &status, &tries), ret, + ret = poll_timeout_us(load_result = guc_load_done(gt, &status, &tries), load_result, 10 * USEC_PER_MSEC, GUC_LOAD_TIMEOUT_SEC * USEC_PER_SEC, false); @@ -1191,7 +1191,7 @@ static int guc_wait_ucode(struct xe_guc *guc) act_freq = xe_guc_pc_get_act_freq(guc_pc); cur_freq = xe_guc_pc_get_cur_freq_fw(guc_pc); - if (ret) { + if (ret || load_result <= 0) { xe_gt_err(gt, "load failed: status = 0x%08X, time = %lldms, freq = %dMHz (req %dMHz)\n", status, delta_ms, xe_guc_pc_get_act_freq(guc_pc), xe_guc_pc_get_cur_freq_fw(guc_pc)); -- cgit v1.2.3 From 78f3bf00be4f15daead02ba32d4737129419c902 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 10 Mar 2026 18:50:33 -0400 Subject: drm/xe: Always kill exec queues in xe_guc_submit_pause_abort xe_guc_submit_pause_abort is intended to be called after something disastrous occurs (e.g., VF migration fails, device wedging, or driver unload) and should immediately trigger the teardown of remaining submission state. With that, kill any remaining queues in this function. Fixes: 7c4b7e34c83b ("drm/xe/vf: Abort VF post migration recovery on failure") Cc: stable@vger.kernel.org Signed-off-by: Zhanjun Dong Reviewed-by: Stuart Summers Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-2-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_guc_submit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index ca7aa4f358d0..b31e0e0af5cb 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -2763,8 +2763,7 @@ void xe_guc_submit_pause_abort(struct xe_guc *guc) continue; xe_sched_submission_start(sched); - if (exec_queue_killed_or_banned_or_wedged(q)) - xe_guc_exec_queue_trigger_cleanup(q); + guc_exec_queue_kill(q); } mutex_unlock(&guc->submission_state.lock); } -- cgit v1.2.3 From a6ab444a111a59924bd9d0c1e0613a75a0a40b89 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 10 Mar 2026 18:50:34 -0400 Subject: drm/xe: Forcefully tear down exec queues in GuC submit fini In GuC submit fini, forcefully tear down any exec queues by disabling CTs, stopping the scheduler (which cleans up lost G2H), killing all remaining queues, and resuming scheduling to allow any remaining cleanup actions to complete and signal any remaining fences. Split guc_submit_fini into device related and software only part. Using device-managed and drm-managed action guarantees the correct ordering of cleanup. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Cc: stable@vger.kernel.org Reviewed-by: Zhanjun Dong Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-3-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_guc.c | 26 +++++++++++++++++++-- drivers/gpu/drm/xe/xe_guc.h | 1 + drivers/gpu/drm/xe/xe_guc_submit.c | 48 ++++++++++++++++++++++++++++++-------- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index ad18be4688c5..576f3d500390 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -1399,15 +1399,37 @@ int xe_guc_enable_communication(struct xe_guc *guc) return 0; } -int xe_guc_suspend(struct xe_guc *guc) +/** + * xe_guc_softreset() - Soft reset GuC + * @guc: The GuC object + * + * Send soft reset command to GuC through mmio send. + * + * Return: 0 if success, otherwise error code + */ +int xe_guc_softreset(struct xe_guc *guc) { - struct xe_gt *gt = guc_to_gt(guc); u32 action[] = { XE_GUC_ACTION_CLIENT_SOFT_RESET, }; int ret; + if (!xe_uc_fw_is_running(&guc->fw)) + return 0; + ret = xe_guc_mmio_send(guc, action, ARRAY_SIZE(action)); + if (ret) + return ret; + + return 0; +} + +int xe_guc_suspend(struct xe_guc *guc) +{ + struct xe_gt *gt = guc_to_gt(guc); + int ret; + + ret = xe_guc_softreset(guc); if (ret) { xe_gt_err(gt, "GuC suspend failed: %pe\n", ERR_PTR(ret)); return ret; diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h index 66e7edc70ed9..02514914f404 100644 --- a/drivers/gpu/drm/xe/xe_guc.h +++ b/drivers/gpu/drm/xe/xe_guc.h @@ -44,6 +44,7 @@ int xe_guc_opt_in_features_enable(struct xe_guc *guc); void xe_guc_runtime_suspend(struct xe_guc *guc); void xe_guc_runtime_resume(struct xe_guc *guc); int xe_guc_suspend(struct xe_guc *guc); +int xe_guc_softreset(struct xe_guc *guc); void xe_guc_notify(struct xe_guc *guc); int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr); int xe_guc_mmio_send(struct xe_guc *guc, const u32 *request, u32 len); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index b31e0e0af5cb..8afd424b27fb 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -47,6 +47,8 @@ #define XE_GUC_EXEC_QUEUE_CGP_CONTEXT_ERROR_LEN 6 +static int guc_submit_reset_prepare(struct xe_guc *guc); + static struct xe_guc * exec_queue_to_guc(struct xe_exec_queue *q) { @@ -238,7 +240,7 @@ static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q) EXEC_QUEUE_STATE_BANNED)); } -static void guc_submit_fini(struct drm_device *drm, void *arg) +static void guc_submit_sw_fini(struct drm_device *drm, void *arg) { struct xe_guc *guc = arg; struct xe_device *xe = guc_to_xe(guc); @@ -256,6 +258,19 @@ static void guc_submit_fini(struct drm_device *drm, void *arg) xa_destroy(&guc->submission_state.exec_queue_lookup); } +static void guc_submit_fini(void *arg) +{ + struct xe_guc *guc = arg; + + /* Forcefully kill any remaining exec queues */ + xe_guc_ct_stop(&guc->ct); + guc_submit_reset_prepare(guc); + xe_guc_softreset(guc); + xe_guc_submit_stop(guc); + xe_uc_fw_sanitize(&guc->fw); + xe_guc_submit_pause_abort(guc); +} + static void guc_submit_wedged_fini(void *arg) { struct xe_guc *guc = arg; @@ -325,7 +340,11 @@ int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids) guc->submission_state.initialized = true; - return drmm_add_action_or_reset(&xe->drm, guc_submit_fini, guc); + err = drmm_add_action_or_reset(&xe->drm, guc_submit_sw_fini, guc); + if (err) + return err; + + return devm_add_action_or_reset(xe->drm.dev, guc_submit_fini, guc); } /* @@ -2298,6 +2317,7 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = { static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) { struct xe_gpu_scheduler *sched = &q->guc->sched; + bool do_destroy = false; /* Stop scheduling + flush any DRM scheduler operations */ xe_sched_submission_stop(sched); @@ -2305,7 +2325,7 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) /* Clean up lost G2H + reset engine state */ if (exec_queue_registered(q)) { if (exec_queue_destroyed(q)) - __guc_exec_queue_destroy(guc, q); + do_destroy = true; } if (q->guc->suspend_pending) { set_exec_queue_suspended(q); @@ -2341,18 +2361,15 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) xe_guc_exec_queue_trigger_cleanup(q); } } + + if (do_destroy) + __guc_exec_queue_destroy(guc, q); } -int xe_guc_submit_reset_prepare(struct xe_guc *guc) +static int guc_submit_reset_prepare(struct xe_guc *guc) { int ret; - if (xe_gt_WARN_ON(guc_to_gt(guc), vf_recovery(guc))) - return 0; - - if (!guc->submission_state.initialized) - return 0; - /* * Using an atomic here rather than submission_state.lock as this * function can be called while holding the CT lock (engine reset @@ -2367,6 +2384,17 @@ int xe_guc_submit_reset_prepare(struct xe_guc *guc) return ret; } +int xe_guc_submit_reset_prepare(struct xe_guc *guc) +{ + if (xe_gt_WARN_ON(guc_to_gt(guc), vf_recovery(guc))) + return 0; + + if (!guc->submission_state.initialized) + return 0; + + return guc_submit_reset_prepare(guc); +} + void xe_guc_submit_reset_wait(struct xe_guc *guc) { wait_event(guc->ct.wq, xe_device_wedged(guc_to_xe(guc)) || -- cgit v1.2.3 From e25ba41c8227c5393c16e4aab398076014bd345f Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 10 Mar 2026 18:50:35 -0400 Subject: drm/xe: Trigger queue cleanup if not in wedged mode 2 The intent of wedging a device is to allow queues to continue running only in wedged mode 2. In other modes, queues should initiate cleanup and signal all remaining fences. Fix xe_guc_submit_wedge to correctly clean up queues when wedge mode != 2. Fixes: 7dbe8af13c18 ("drm/xe: Wedge the entire device") Cc: stable@vger.kernel.org Reviewed-by: Zhanjun Dong Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-4-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_guc_submit.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 8afd424b27fb..cb32053d57ec 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1319,6 +1319,7 @@ static void disable_scheduling_deregister(struct xe_guc *guc, */ void xe_guc_submit_wedge(struct xe_guc *guc) { + struct xe_device *xe = guc_to_xe(guc); struct xe_gt *gt = guc_to_gt(guc); struct xe_exec_queue *q; unsigned long index; @@ -1333,20 +1334,28 @@ void xe_guc_submit_wedge(struct xe_guc *guc) if (!guc->submission_state.initialized) return; - err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev, - guc_submit_wedged_fini, guc); - if (err) { - xe_gt_err(gt, "Failed to register clean-up in wedged.mode=%s; " - "Although device is wedged.\n", - xe_wedged_mode_to_string(XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)); - return; - } + if (xe->wedged.mode == 2) { + err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev, + guc_submit_wedged_fini, guc); + if (err) { + xe_gt_err(gt, "Failed to register clean-up on wedged.mode=2; " + "Although device is wedged.\n"); + return; + } - mutex_lock(&guc->submission_state.lock); - xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) - if (xe_exec_queue_get_unless_zero(q)) - set_exec_queue_wedged(q); - mutex_unlock(&guc->submission_state.lock); + mutex_lock(&guc->submission_state.lock); + xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) + if (xe_exec_queue_get_unless_zero(q)) + set_exec_queue_wedged(q); + mutex_unlock(&guc->submission_state.lock); + } else { + /* Forcefully kill any remaining exec queues, signal fences */ + guc_submit_reset_prepare(guc); + xe_guc_submit_stop(guc); + xe_guc_softreset(guc); + xe_uc_fw_sanitize(&guc->fw); + xe_guc_submit_pause_abort(guc); + } } static bool guc_submit_hint_wedged(struct xe_guc *guc) -- cgit v1.2.3 From a7f607610da721f77db358b09be8091e60bd8e89 Mon Sep 17 00:00:00 2001 From: Zhanjun Dong Date: Tue, 10 Mar 2026 18:50:36 -0400 Subject: drm/xe: Use XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET enum instead of magic number Replace the magic number 2 with the proper enum value XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET for better code readability and maintainability. Signed-off-by: Zhanjun Dong Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-5-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_guc_submit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index cb32053d57ec..a145234f662b 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1334,12 +1334,13 @@ void xe_guc_submit_wedge(struct xe_guc *guc) if (!guc->submission_state.initialized) return; - if (xe->wedged.mode == 2) { + if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) { err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev, guc_submit_wedged_fini, guc); if (err) { - xe_gt_err(gt, "Failed to register clean-up on wedged.mode=2; " - "Although device is wedged.\n"); + xe_gt_err(gt, "Failed to register clean-up on wedged.mode=%s; " + "Although device is wedged.\n", + xe_wedged_mode_to_string(XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)); return; } -- cgit v1.2.3 From dace8cb0032f57ea67c87b3b92ad73c89dd2db44 Mon Sep 17 00:00:00 2001 From: Zhanjun Dong Date: Tue, 10 Mar 2026 18:50:37 -0400 Subject: drm/xe/guc: Ensure CT state transitions via STOP before DISABLED The GuC CT state transition requires moving to the STOP state before entering the DISABLED state. Update the driver teardown sequence to make the proper state machine transitions. Fixes: ee4b32220a6b ("drm/xe/guc: Add devm release action to safely tear down CT") Cc: stable@vger.kernel.org Signed-off-by: Zhanjun Dong Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-6-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_guc_ct.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c index 496c6c77bee6..3b1c03743f83 100644 --- a/drivers/gpu/drm/xe/xe_guc_ct.c +++ b/drivers/gpu/drm/xe/xe_guc_ct.c @@ -352,6 +352,7 @@ static void guc_action_disable_ct(void *arg) { struct xe_guc_ct *ct = arg; + xe_guc_ct_stop(ct); guc_ct_change_state(ct, XE_GUC_CT_STATE_DISABLED); } -- cgit v1.2.3 From 9a9d960dce05b45b2a7dacd2c98d602480812a13 Mon Sep 17 00:00:00 2001 From: Zhanjun Dong Date: Tue, 10 Mar 2026 18:50:38 -0400 Subject: drm/xe/uc: Drop xe_guc_sanitize in favor of managed cleanup If the firmware fails to load in GT resets the device is wedged also initiating a GuC state cleanup. Signed-off-by: Zhanjun Dong Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-7-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_uc.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_uc.c b/drivers/gpu/drm/xe/xe_uc.c index d9aa845a308d..75091bde0d50 100644 --- a/drivers/gpu/drm/xe/xe_uc.c +++ b/drivers/gpu/drm/xe/xe_uc.c @@ -157,23 +157,19 @@ static int vf_uc_load_hw(struct xe_uc *uc) err = xe_gt_sriov_vf_connect(uc_to_gt(uc)); if (err) - goto err_out; + return err; uc->guc.submission_state.enabled = true; err = xe_guc_opt_in_features_enable(&uc->guc); if (err) - goto err_out; + return err; err = xe_gt_record_default_lrcs(uc_to_gt(uc)); if (err) - goto err_out; + return err; return 0; - -err_out: - xe_guc_sanitize(&uc->guc); - return err; } /* @@ -205,19 +201,19 @@ int xe_uc_load_hw(struct xe_uc *uc) ret = xe_gt_record_default_lrcs(uc_to_gt(uc)); if (ret) - goto err_out; + return ret; ret = xe_guc_post_load_init(&uc->guc); if (ret) - goto err_out; + return ret; ret = xe_guc_pc_start(&uc->guc.pc); if (ret) - goto err_out; + return ret; ret = xe_guc_rc_enable(&uc->guc); if (ret) - goto err_out; + return ret; xe_guc_engine_activity_enable_stats(&uc->guc); @@ -232,10 +228,6 @@ int xe_uc_load_hw(struct xe_uc *uc) xe_gsc_load_start(&uc->gsc); return 0; - -err_out: - xe_guc_sanitize(&uc->guc); - return ret; } int xe_uc_reset_prepare(struct xe_uc *uc) -- cgit v1.2.3 From 4f3a998a173b4325c2efd90bdadc6ccd3ad9a431 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Tue, 10 Mar 2026 18:50:39 -0400 Subject: drm/xe: Open-code GGTT MMIO access protection GGTT MMIO access is currently protected by hotplug (drm_dev_enter), which works correctly when the driver loads successfully and is later unbound or unloaded. However, if driver load fails, this protection is insufficient because drm_dev_unplug() is never called. Additionally, devm release functions cannot guarantee that all BOs with GGTT mappings are destroyed before the GGTT MMIO region is removed, as some BOs may be freed asynchronously by worker threads. To address this, introduce an open-coded flag, protected by the GGTT lock, that guards GGTT MMIO access. The flag is cleared during the dev_fini_ggtt devm release function to ensure MMIO access is disabled once teardown begins. Cc: stable@vger.kernel.org Fixes: 919bb54e989c ("drm/xe: Fix missing runtime outer protection for ggtt_remove_node") Reviewed-by: Zhanjun Dong Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260310225039.1320161-8-zhanjun.dong@intel.com --- drivers/gpu/drm/xe/xe_ggtt.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c index 0f2e3af49912..21071b64b09d 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.c +++ b/drivers/gpu/drm/xe/xe_ggtt.c @@ -66,6 +66,9 @@ * give us the correct placement for free. */ +#define XE_GGTT_FLAGS_64K BIT(0) +#define XE_GGTT_FLAGS_ONLINE BIT(1) + /** * struct xe_ggtt_node - A node in GGTT. * @@ -117,6 +120,8 @@ struct xe_ggtt { * @flags: Flags for this GGTT * Acceptable flags: * - %XE_GGTT_FLAGS_64K - if PTE size is 64K. Otherwise, regular is 4K. + * - %XE_GGTT_FLAGS_ONLINE - is GGTT online, protected by ggtt->lock + * after init */ unsigned int flags; /** @scratch: Internal object allocation used as a scratch page */ @@ -367,6 +372,8 @@ static void dev_fini_ggtt(void *arg) { struct xe_ggtt *ggtt = arg; + scoped_guard(mutex, &ggtt->lock) + ggtt->flags &= ~XE_GGTT_FLAGS_ONLINE; drain_workqueue(ggtt->wq); } @@ -437,6 +444,7 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt) if (err) return err; + ggtt->flags |= XE_GGTT_FLAGS_ONLINE; return devm_add_action_or_reset(xe->drm.dev, dev_fini_ggtt, ggtt); } ALLOW_ERROR_INJECTION(xe_ggtt_init_early, ERRNO); /* See xe_pci_probe() */ @@ -465,13 +473,10 @@ static void ggtt_node_fini(struct xe_ggtt_node *node) static void ggtt_node_remove(struct xe_ggtt_node *node) { struct xe_ggtt *ggtt = node->ggtt; - struct xe_device *xe = tile_to_xe(ggtt->tile); bool bound; - int idx; - - bound = drm_dev_enter(&xe->drm, &idx); mutex_lock(&ggtt->lock); + bound = ggtt->flags & XE_GGTT_FLAGS_ONLINE; if (bound) xe_ggtt_clear(ggtt, xe_ggtt_node_addr(node), xe_ggtt_node_size(node)); drm_mm_remove_node(&node->base); @@ -484,8 +489,6 @@ static void ggtt_node_remove(struct xe_ggtt_node *node) if (node->invalidate_on_remove) xe_ggtt_invalidate(ggtt); - drm_dev_exit(idx); - free_node: ggtt_node_fini(node); } -- cgit v1.2.3 From 440ec190c2fc06c368096df4862213f3f795db37 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Thu, 12 Mar 2026 20:20:11 +0100 Subject: drm/pagemap: Unlock and put folios when possible If the page is part of a folio, unlock and put the whole folio at once instead of individual pages one after the other. This will reduce the amount of operations once device THP are in use. Cc: Andrew Morton Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Zi Yan Cc: Alistair Popple Cc: Balbir Singh Cc: linux-mm@kvack.org Suggested-by: Matthew Brost Reviewed-by: Matthew Brost Signed-off-by: Francois Dugast Reviewed-by: Balbir Singh Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260312192126.2024853-2-francois.dugast@intel.com --- drivers/gpu/drm/drm_pagemap.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 862675ac5bb2..f453a12b6a8e 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -154,15 +154,15 @@ static void drm_pagemap_zdd_put(struct drm_pagemap_zdd *zdd) } /** - * drm_pagemap_migration_unlock_put_page() - Put a migration page - * @page: Pointer to the page to put + * drm_pagemap_migration_unlock_put_folio() - Put a migration folio + * @folio: Pointer to the folio to put * - * This function unlocks and puts a page. + * This function unlocks and puts a folio. */ -static void drm_pagemap_migration_unlock_put_page(struct page *page) +static void drm_pagemap_migration_unlock_put_folio(struct folio *folio) { - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } /** @@ -177,15 +177,23 @@ static void drm_pagemap_migration_unlock_put_pages(unsigned long npages, { unsigned long i; - for (i = 0; i < npages; ++i) { + for (i = 0; i < npages;) { struct page *page; + struct folio *folio; + unsigned int order = 0; if (!migrate_pfn[i]) - continue; + goto next; page = migrate_pfn_to_page(migrate_pfn[i]); - drm_pagemap_migration_unlock_put_page(page); + folio = page_folio(page); + order = folio_order(folio); + + drm_pagemap_migration_unlock_put_folio(folio); migrate_pfn[i] = 0; + +next: + i += NR_PAGES(order); } } -- cgit v1.2.3 From 2e03c0c5c59a086df534e15ddde03cb33bc475c4 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Thu, 12 Mar 2026 20:20:12 +0100 Subject: drm/pagemap: Add helper to access zone_device_data This new helper helps ensure all accesses to zone_device_data use the correct API whether the page is part of a folio or not. v2: - Move to drm_pagemap.h, stick to folio_zone_device_data (Matthew Brost) - Return struct drm_pagemap_zdd * (Matthew Brost) v3: - Add stub for !CONFIG_ZONE_DEVICE (CI) Cc: Andrew Morton Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Zi Yan Cc: Alistair Popple Cc: Balbir Singh Cc: linux-mm@kvack.org Suggested-by: Matthew Brost Reviewed-by: Matthew Brost Signed-off-by: Francois Dugast Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260312192126.2024853-3-francois.dugast@intel.com --- drivers/gpu/drm/drm_gpusvm.c | 7 +++++-- drivers/gpu/drm/drm_pagemap.c | 21 ++++++++++++--------- include/drm/drm_pagemap.h | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index 35dd07297dd0..4b928fda5b12 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -1488,12 +1488,15 @@ map_pages: order = drm_gpusvm_hmm_pfn_to_order(pfns[i], i, npages); if (is_device_private_page(page) || is_device_coherent_page(page)) { + struct drm_pagemap_zdd *__zdd = + drm_pagemap_page_zone_device_data(page); + if (!ctx->allow_mixed && - zdd != page->zone_device_data && i > 0) { + zdd != __zdd && i > 0) { err = -EOPNOTSUPP; goto err_unmap; } - zdd = page->zone_device_data; + zdd = __zdd; if (pagemap != page_pgmap(page)) { if (pagemap) { err = -EOPNOTSUPP; diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index f453a12b6a8e..733a3857947c 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -252,7 +252,7 @@ static int drm_pagemap_migrate_map_pages(struct device *dev, order = folio_order(folio); if (is_device_private_page(page)) { - struct drm_pagemap_zdd *zdd = page->zone_device_data; + struct drm_pagemap_zdd *zdd = drm_pagemap_page_zone_device_data(page); struct drm_pagemap *dpagemap = zdd->dpagemap; struct drm_pagemap_addr addr; @@ -323,7 +323,7 @@ static void drm_pagemap_migrate_unmap_pages(struct device *dev, goto next; if (is_zone_device_page(page)) { - struct drm_pagemap_zdd *zdd = page->zone_device_data; + struct drm_pagemap_zdd *zdd = drm_pagemap_page_zone_device_data(page); struct drm_pagemap *dpagemap = zdd->dpagemap; dpagemap->ops->device_unmap(dpagemap, dev, &pagemap_addr[i]); @@ -601,7 +601,8 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, pages[i] = NULL; if (src_page && is_device_private_page(src_page)) { - struct drm_pagemap_zdd *src_zdd = src_page->zone_device_data; + struct drm_pagemap_zdd *src_zdd = + drm_pagemap_page_zone_device_data(src_page); if (page_pgmap(src_page) == pagemap && !mdetails->can_migrate_same_pagemap) { @@ -723,8 +724,8 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, goto next; if (fault_page) { - if (src_page->zone_device_data != - fault_page->zone_device_data) + if (drm_pagemap_page_zone_device_data(src_page) != + drm_pagemap_page_zone_device_data(fault_page)) goto next; } @@ -1065,7 +1066,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, void *buf; int i, err = 0; - zdd = page->zone_device_data; + zdd = drm_pagemap_page_zone_device_data(page); if (time_before64(get_jiffies_64(), zdd->devmem_allocation->timeslice_expiration)) return 0; @@ -1148,7 +1149,9 @@ err_out: */ static void drm_pagemap_folio_free(struct folio *folio) { - drm_pagemap_zdd_put(folio->page.zone_device_data); + struct page *page = folio_page(folio, 0); + + drm_pagemap_zdd_put(drm_pagemap_page_zone_device_data(page)); } /** @@ -1164,7 +1167,7 @@ static void drm_pagemap_folio_free(struct folio *folio) */ static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf) { - struct drm_pagemap_zdd *zdd = vmf->page->zone_device_data; + struct drm_pagemap_zdd *zdd = drm_pagemap_page_zone_device_data(vmf->page); int err; err = __drm_pagemap_migrate_to_ram(vmf->vma, @@ -1230,7 +1233,7 @@ EXPORT_SYMBOL_GPL(drm_pagemap_devmem_init); */ struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page) { - struct drm_pagemap_zdd *zdd = page->zone_device_data; + struct drm_pagemap_zdd *zdd = drm_pagemap_page_zone_device_data(page); return zdd->devmem_allocation->dpagemap; } diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h index c848f578e3da..75e6ca58922d 100644 --- a/include/drm/drm_pagemap.h +++ b/include/drm/drm_pagemap.h @@ -4,6 +4,7 @@ #include #include +#include #include #define NR_PAGES(order) (1U << (order)) @@ -367,6 +368,26 @@ void drm_pagemap_destroy(struct drm_pagemap *dpagemap, bool is_atomic_or_reclaim int drm_pagemap_reinit(struct drm_pagemap *dpagemap); +/** + * drm_pagemap_page_zone_device_data() - Page to zone_device_data + * @page: Pointer to the page + * + * Return: Page's zone_device_data + */ +static inline struct drm_pagemap_zdd *drm_pagemap_page_zone_device_data(struct page *page) +{ + struct folio *folio = page_folio(page); + + return folio_zone_device_data(folio); +} + +#else + +static inline struct drm_pagemap_zdd *drm_pagemap_page_zone_device_data(struct page *page) +{ + return NULL; +} + #endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */ #endif -- cgit v1.2.3 From 139ab31aea8a9436460568d556b432bb7e9311f7 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Thu, 12 Mar 2026 20:20:13 +0100 Subject: drm/pagemap: Correct cpages calculation for migrate_vma_setup cpages returned from migrate_vma_setup represents the total number of individual pages found, not the number of 4K pages. The math in drm_pagemap_migrate_to_devmem for npages is based on the number of 4K pages, so cpages != npages can fail even if the entire memory range is found in migrate_vma_setup (e.g., when a single 2M page is found). Add drm_pagemap_cpages, which converts cpages to the number of 4K pages found. Cc: Andrew Morton Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Zi Yan Cc: Alistair Popple Cc: Balbir Singh Cc: linux-mm@kvack.org Reviewed-by: Francois Dugast Signed-off-by: Francois Dugast Reviewed-by: Balbir Singh Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260312192126.2024853-4-francois.dugast@intel.com --- drivers/gpu/drm/drm_pagemap.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 733a3857947c..837b881883f9 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -452,6 +452,41 @@ out: return ret; } +/** + * drm_pagemap_cpages() - Count collected pages + * @migrate_pfn: Array of migrate_pfn entries to account + * @npages: Number of entries in @migrate_pfn + * + * Compute the total number of minimum-sized pages represented by the + * collected entries in @migrate_pfn. The total is derived from the + * order encoded in each entry. + * + * Return: Total number of minimum-sized pages. + */ +static int drm_pagemap_cpages(unsigned long *migrate_pfn, unsigned long npages) +{ + unsigned long i, cpages = 0; + + for (i = 0; i < npages;) { + struct page *page = migrate_pfn_to_page(migrate_pfn[i]); + struct folio *folio; + unsigned int order = 0; + + if (page) { + folio = page_folio(page); + order = folio_order(folio); + cpages += NR_PAGES(order); + } else if (migrate_pfn[i] & MIGRATE_PFN_COMPOUND) { + order = HPAGE_PMD_ORDER; + cpages += NR_PAGES(order); + } + + i += NR_PAGES(order); + } + + return cpages; +} + /** * drm_pagemap_migrate_to_devmem() - Migrate a struct mm_struct range to device memory * @devmem_allocation: The device memory allocation to migrate to. @@ -554,7 +589,8 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, goto err_free; } - if (migrate.cpages != npages) { + if (migrate.cpages != npages && + drm_pagemap_cpages(migrate.src, npages) != npages) { /* * Some pages to migrate. But we want to migrate all or * nothing. Raced or unknown device pages. -- cgit v1.2.3 From 192cb1f5cb1694c45b7cac14519d7bad65ba22f6 Mon Sep 17 00:00:00 2001 From: Francois Dugast Date: Thu, 12 Mar 2026 20:20:14 +0100 Subject: drm/pagemap: Enable THP support for GPU memory migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables support for Transparent Huge Pages (THP) for device pages by using MIGRATE_VMA_SELECT_COMPOUND during migration. It removes the need to split folios and loop multiple times over all pages to perform required operations at page level. Instead, we rely on newly introduced support for higher orders in drm_pagemap and folio-level API. In Xe, this drastically improves performance when using SVM. The GT stats below collected after a 2MB page fault show overall servicing is more than 7 times faster, and thanks to reduced CPU overhead the time spent on the actual copy goes from 23% without THP to 80% with THP: Without THP: svm_2M_pagefault_us: 966 svm_2M_migrate_us: 942 svm_2M_device_copy_us: 223 svm_2M_get_pages_us: 9 svm_2M_bind_us: 10 With THP: svm_2M_pagefault_us: 132 svm_2M_migrate_us: 128 svm_2M_device_copy_us: 106 svm_2M_get_pages_us: 1 svm_2M_bind_us: 2 v2: - Fix one occurrence of drm_pagemap_get_devmem_page() (Matthew Brost) v3: - Remove migrate_device_split_page() and folio_split_lock, instead rely on free_zone_device_folio() to split folios before freeing (Matthew Brost) - Assert folio order is HPAGE_PMD_ORDER (Matthew Brost) - Always use folio_set_zone_device_data() in split (Matthew Brost) v4: - Warn on compound device page, s/continue/goto next/ (Matthew Brost) v5: - Revert warn on compound device page - s/zone_device_page_init()/zone_device_folio_init() (Matthew Brost) Cc: Matthew Brost Cc: Thomas Hellström Cc: Michal Mrozek Cc: Andrew Morton Cc: David Hildenbrand Cc: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Zi Yan Cc: Alistair Popple Cc: Balbir Singh Cc: linux-mm@kvack.org Reviewed-by: Matthew Brost Signed-off-by: Francois Dugast Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260312192126.2024853-5-francois.dugast@intel.com --- drivers/gpu/drm/drm_pagemap.c | 72 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c index 837b881883f9..5002049e0198 100644 --- a/drivers/gpu/drm/drm_pagemap.c +++ b/drivers/gpu/drm/drm_pagemap.c @@ -200,16 +200,19 @@ next: /** * drm_pagemap_get_devmem_page() - Get a reference to a device memory page * @page: Pointer to the page + * @order: Order * @zdd: Pointer to the GPU SVM zone device data * * This function associates the given page with the specified GPU SVM zone * device data and initializes it for zone device usage. */ static void drm_pagemap_get_devmem_page(struct page *page, + unsigned int order, struct drm_pagemap_zdd *zdd) { - page->zone_device_data = drm_pagemap_zdd_get(zdd); - zone_device_page_init(page, page_pgmap(page), 0); + zone_device_folio_init((struct folio *)page, zdd->dpagemap->pagemap, + order); + folio_set_zone_device_data(page_folio(page), drm_pagemap_zdd_get(zdd)); } /** @@ -524,7 +527,7 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, .end = end, .pgmap_owner = pagemap->owner, .flags = MIGRATE_VMA_SELECT_SYSTEM | MIGRATE_VMA_SELECT_DEVICE_COHERENT | - MIGRATE_VMA_SELECT_DEVICE_PRIVATE, + MIGRATE_VMA_SELECT_DEVICE_PRIVATE | MIGRATE_VMA_SELECT_COMPOUND, }; unsigned long i, npages = npages_in_range(start, end); unsigned long own_pages = 0, migrated_pages = 0; @@ -630,11 +633,13 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, own_pages = 0; - for (i = 0; i < npages; ++i) { + for (i = 0; i < npages;) { + unsigned long j; struct page *page = pfn_to_page(migrate.dst[i]); struct page *src_page = migrate_pfn_to_page(migrate.src[i]); - cur.start = i; + unsigned int order = 0; + cur.start = i; pages[i] = NULL; if (src_page && is_device_private_page(src_page)) { struct drm_pagemap_zdd *src_zdd = @@ -644,7 +649,7 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, !mdetails->can_migrate_same_pagemap) { migrate.dst[i] = 0; own_pages++; - continue; + goto next; } if (mdetails->source_peer_migrates) { cur.dpagemap = src_zdd->dpagemap; @@ -660,7 +665,20 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, pages[i] = page; } migrate.dst[i] = migrate_pfn(migrate.dst[i]); - drm_pagemap_get_devmem_page(page, zdd); + + if (migrate.src[i] & MIGRATE_PFN_COMPOUND) { + drm_WARN_ONCE(dpagemap->drm, src_page && + folio_order(page_folio(src_page)) != HPAGE_PMD_ORDER, + "Unexpected folio order\n"); + + order = HPAGE_PMD_ORDER; + migrate.dst[i] |= MIGRATE_PFN_COMPOUND; + + for (j = 1; j < NR_PAGES(order) && i + j < npages; j++) + migrate.dst[i + j] = 0; + } + + drm_pagemap_get_devmem_page(page, order, zdd); /* If we switched the migrating drm_pagemap, migrate previous pages now */ err = drm_pagemap_migrate_range(devmem_allocation, migrate.src, migrate.dst, @@ -670,7 +688,11 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation, npages = i + 1; goto err_finalize; } + +next: + i += NR_PAGES(order); } + cur.start = npages; cur.ops = NULL; /* Force migration */ err = drm_pagemap_migrate_range(devmem_allocation, migrate.src, migrate.dst, @@ -779,6 +801,8 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas, page = folio_page(folio, 0); mpfn[i] = migrate_pfn(page_to_pfn(page)); + if (order) + mpfn[i] |= MIGRATE_PFN_COMPOUND; next: if (page) addr += page_size(page); @@ -1034,8 +1058,15 @@ retry: if (err) goto err_finalize; - for (i = 0; i < npages; ++i) + for (i = 0; i < npages;) { + unsigned int order = 0; + pages[i] = migrate_pfn_to_page(src[i]); + if (pages[i]) + order = folio_order(page_folio(pages[i])); + + i += NR_PAGES(order); + } err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL); if (err) @@ -1088,7 +1119,8 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, .vma = vas, .pgmap_owner = page_pgmap(page)->owner, .flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE | - MIGRATE_VMA_SELECT_DEVICE_COHERENT, + MIGRATE_VMA_SELECT_DEVICE_COHERENT | + MIGRATE_VMA_SELECT_COMPOUND, .fault_page = page, }; struct drm_pagemap_migrate_details mdetails = {}; @@ -1154,8 +1186,15 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas, if (err) goto err_finalize; - for (i = 0; i < npages; ++i) + for (i = 0; i < npages;) { + unsigned int order = 0; + pages[i] = migrate_pfn_to_page(migrate.src[i]); + if (pages[i]) + order = folio_order(page_folio(pages[i])); + + i += NR_PAGES(order); + } err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL); if (err) @@ -1213,9 +1252,22 @@ static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf) return err ? VM_FAULT_SIGBUS : 0; } +static void drm_pagemap_folio_split(struct folio *orig_folio, struct folio *new_folio) +{ + struct drm_pagemap_zdd *zdd; + + if (!new_folio) + return; + + new_folio->pgmap = orig_folio->pgmap; + zdd = folio_zone_device_data(orig_folio); + folio_set_zone_device_data(new_folio, drm_pagemap_zdd_get(zdd)); +} + static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = { .folio_free = drm_pagemap_folio_free, .migrate_to_ram = drm_pagemap_migrate_to_ram, + .folio_split = drm_pagemap_folio_split, }; /** -- cgit v1.2.3 From 27a5e78a23dd36e487678aee92364fc9ef6d6871 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Fri, 13 Mar 2026 13:34:38 +0530 Subject: drm/xe/i2c: Assert/Deassert I2C IRQ I2C IRQ is triggered using virtual wire. Assert/Deassert it in IRQ handler to allow subsequent interrupt generation. Signed-off-by: Raag Jadav Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260313080438.4166251-1-raag.jadav@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/xe/xe_i2c.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 1deb812fe01d..706783863d07 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -176,11 +176,18 @@ static bool xe_i2c_irq_present(struct xe_device *xe) */ void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { - if (!xe_i2c_irq_present(xe)) + struct xe_mmio *mmio = xe_root_tile_mmio(xe); + + if (!(master_ctl & I2C_IRQ) || !xe_i2c_irq_present(xe)) return; - if (master_ctl & I2C_IRQ) - generic_handle_irq_safe(xe->i2c->adapter_irq); + /* Forward interrupt to I2C adapter */ + generic_handle_irq_safe(xe->i2c->adapter_irq); + + /* Deassert after I2C adapter clears the interrupt */ + xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_INTX_DISABLE); + /* Reassert to allow subsequent interrupt generation */ + xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); } void xe_i2c_irq_reset(struct xe_device *xe) @@ -190,6 +197,7 @@ void xe_i2c_irq_reset(struct xe_device *xe) if (!xe_i2c_irq_present(xe)) return; + xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_INTX_DISABLE); xe_mmio_rmw32(mmio, I2C_BRIDGE_PCICFGCTL, ACPI_INTR_EN, 0); } @@ -201,6 +209,7 @@ void xe_i2c_irq_postinstall(struct xe_device *xe) return; xe_mmio_rmw32(mmio, I2C_BRIDGE_PCICFGCTL, 0, ACPI_INTR_EN); + xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); } static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq, -- cgit v1.2.3 From 65fcf19cb36bf43c893c3444d5bd120006cb843d Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Wed, 11 Mar 2026 16:04:58 -0700 Subject: drm/xe: Include running dword offset in default_lrc dumps Printing a running dword offset in the default_lrc_* debugfs entries makes it easier for developers to find the right offsets to use in regs/xe_lrc_layout.h and/or compare the default LRC contents against the bspec-documented LRC layout. Reviewed-by: Dnyaneshwar Bhadane Link: https://patch.msgid.link/20260311-default_lrc_offsets-v1-1-58d8ed3aa081@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/xe/xe_lrc.c | 65 +++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 73a503d88217..7fb59386d1c5 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1902,6 +1902,7 @@ static int instr_dw(u32 cmd_header) static int dump_mi_command(struct drm_printer *p, struct xe_gt *gt, + u32 *start, u32 *dw, int remaining_dw) { @@ -1917,15 +1918,18 @@ static int dump_mi_command(struct drm_printer *p, while (num_noop < remaining_dw && (*(++dw) & REG_GENMASK(31, 23)) == MI_NOOP) num_noop++; - drm_printf(p, "[%#010x] MI_NOOP (%d dwords)\n", inst_header, num_noop); + drm_printf(p, "LRC[%#5lx] = [%#010x] MI_NOOP (%d dwords)\n", + dw - num_noop - start, inst_header, num_noop); return num_noop; case MI_TOPOLOGY_FILTER: - drm_printf(p, "[%#010x] MI_TOPOLOGY_FILTER\n", inst_header); + drm_printf(p, "LRC[%#5lx] = [%#010x] MI_TOPOLOGY_FILTER\n", + dw - start, inst_header); return 1; case MI_BATCH_BUFFER_END: - drm_printf(p, "[%#010x] MI_BATCH_BUFFER_END\n", inst_header); + drm_printf(p, "LRC[%#5lx] = [%#010x] MI_BATCH_BUFFER_END\n", + dw - start, inst_header); /* Return 'remaining_dw' to consume the rest of the LRC */ return remaining_dw; } @@ -1939,39 +1943,43 @@ static int dump_mi_command(struct drm_printer *p, switch (inst_header & MI_OPCODE) { case MI_LOAD_REGISTER_IMM: - drm_printf(p, "[%#010x] MI_LOAD_REGISTER_IMM: %d regs\n", - inst_header, (numdw - 1) / 2); + drm_printf(p, "LRC[%#5lx] = [%#010x] MI_LOAD_REGISTER_IMM: %d regs\n", + dw - start, inst_header, (numdw - 1) / 2); for (int i = 1; i < numdw; i += 2) - drm_printf(p, " - %#6x = %#010x\n", dw[i], dw[i + 1]); + drm_printf(p, "LRC[%#5lx] = - %#6x = %#010x\n", + &dw[i] - start, dw[i], dw[i + 1]); return numdw; case MI_LOAD_REGISTER_MEM & MI_OPCODE: - drm_printf(p, "[%#010x] MI_LOAD_REGISTER_MEM: %s%s\n", - inst_header, + drm_printf(p, "LRC[%#5lx] = [%#010x] MI_LOAD_REGISTER_MEM: %s%s\n", + dw - start, inst_header, dw[0] & MI_LRI_LRM_CS_MMIO ? "CS_MMIO " : "", dw[0] & MI_LRM_USE_GGTT ? "USE_GGTT " : ""); if (numdw == 4) - drm_printf(p, " - %#6x = %#010llx\n", + drm_printf(p, "LRC[%#5lx] = - %#6x = %#010llx\n", + dw - start, dw[1], ((u64)(dw[3]) << 32 | (u64)(dw[2]))); else - drm_printf(p, " - %*ph (%s)\n", - (int)sizeof(u32) * (numdw - 1), dw + 1, - numdw < 4 ? "truncated" : "malformed"); + drm_printf(p, "LRC[%#5lx] = - %*ph (%s)\n", + dw - start, (int)sizeof(u32) * (numdw - 1), + dw + 1, numdw < 4 ? "truncated" : "malformed"); return numdw; case MI_FORCE_WAKEUP: - drm_printf(p, "[%#010x] MI_FORCE_WAKEUP\n", inst_header); + drm_printf(p, "LRC[%#5lx] = [%#010x] MI_FORCE_WAKEUP\n", + dw - start, inst_header); return numdw; default: - drm_printf(p, "[%#010x] unknown MI opcode %#x, likely %d dwords\n", - inst_header, opcode, numdw); + drm_printf(p, "LRC[%#5lx] = [%#010x] unknown MI opcode %#x, likely %d dwords\n", + dw - start, inst_header, opcode, numdw); return numdw; } } static int dump_gfxpipe_command(struct drm_printer *p, struct xe_gt *gt, + u32 *start, u32 *dw, int remaining_dw) { @@ -1990,11 +1998,13 @@ static int dump_gfxpipe_command(struct drm_printer *p, switch (*dw & GFXPIPE_MATCH_MASK) { #define MATCH(cmd) \ case cmd: \ - drm_printf(p, "[%#010x] " #cmd " (%d dwords)\n", *dw, numdw); \ + drm_printf(p, "LRC[%#5lx] = [%#010x] " #cmd " (%d dwords)\n", \ + dw - start, *dw, numdw); \ return numdw #define MATCH3D(cmd) \ case CMD_##cmd: \ - drm_printf(p, "[%#010x] " #cmd " (%d dwords)\n", *dw, numdw); \ + drm_printf(p, "LRC[%#5lx] = [%#010x] " #cmd " (%d dwords)\n", \ + dw - start, *dw, numdw); \ return numdw MATCH(STATE_BASE_ADDRESS); @@ -2126,14 +2136,15 @@ static int dump_gfxpipe_command(struct drm_printer *p, MATCH3D(3DSTATE_SLICE_TABLE_STATE_POINTER_2); default: - drm_printf(p, "[%#010x] unknown GFXPIPE command (pipeline=%#x, opcode=%#x, subopcode=%#x), likely %d dwords\n", - *dw, pipeline, opcode, subopcode, numdw); + drm_printf(p, "LRC[%#5lx] = [%#010x] unknown GFXPIPE command (pipeline=%#x, opcode=%#x, subopcode=%#x), likely %d dwords\n", + dw - start, *dw, pipeline, opcode, subopcode, numdw); return numdw; } } static int dump_gfx_state_command(struct drm_printer *p, struct xe_gt *gt, + u32 *start, u32 *dw, int remaining_dw) { @@ -2151,8 +2162,8 @@ static int dump_gfx_state_command(struct drm_printer *p, MATCH(STATE_WRITE_INLINE); default: - drm_printf(p, "[%#010x] unknown GFX_STATE command (opcode=%#x), likely %d dwords\n", - *dw, opcode, numdw); + drm_printf(p, "LRC[%#5lx] = [%#010x] unknown GFX_STATE command (opcode=%#x), likely %d dwords\n", + dw - start, *dw, opcode, numdw); return numdw; } } @@ -2161,7 +2172,7 @@ void xe_lrc_dump_default(struct drm_printer *p, struct xe_gt *gt, enum xe_engine_class hwe_class) { - u32 *dw; + u32 *dw, *start; int remaining_dw, num_dw; if (!gt->default_lrc[hwe_class]) { @@ -2174,18 +2185,20 @@ void xe_lrc_dump_default(struct drm_printer *p, * hardware status page. */ dw = gt->default_lrc[hwe_class] + LRC_PPHWSP_SIZE; + start = dw; remaining_dw = (xe_gt_lrc_size(gt, hwe_class) - LRC_PPHWSP_SIZE) / 4; while (remaining_dw > 0) { if ((*dw & XE_INSTR_CMD_TYPE) == XE_INSTR_MI) { - num_dw = dump_mi_command(p, gt, dw, remaining_dw); + num_dw = dump_mi_command(p, gt, start, dw, remaining_dw); } else if ((*dw & XE_INSTR_CMD_TYPE) == XE_INSTR_GFXPIPE) { - num_dw = dump_gfxpipe_command(p, gt, dw, remaining_dw); + num_dw = dump_gfxpipe_command(p, gt, start, dw, remaining_dw); } else if ((*dw & XE_INSTR_CMD_TYPE) == XE_INSTR_GFX_STATE) { - num_dw = dump_gfx_state_command(p, gt, dw, remaining_dw); + num_dw = dump_gfx_state_command(p, gt, start, dw, remaining_dw); } else { num_dw = min(instr_dw(*dw), remaining_dw); - drm_printf(p, "[%#10x] Unknown instruction of type %#x, likely %d dwords\n", + drm_printf(p, "LRC[%#5lx] = [%#10x] Unknown instruction of type %#x, likely %d dwords\n", + dw - start, *dw, REG_FIELD_GET(XE_INSTR_CMD_TYPE, *dw), num_dw); } -- cgit v1.2.3 From 1d123587525db86cc8f0d2beb35d9e33ca3ade83 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 5 Mar 2026 17:15:48 +0000 Subject: drm/xe: Skip over non leaf pte for PRL generation The check using xe_child->base.children was insufficient in determining if a pte was a leaf node. So explicitly skip over every non-leaf pt and conditionally abort if there is a scenario where a non-leaf pt is interleaved between leaf pt, which results in the page walker skipping over some leaf pt. Note that the behavior being targeted for abort is PD[0] = 2M PTE PD[1] = PT -> 512 4K PTEs PD[2] = 2M PTE results in abort, page walker won't descend PD[1]. With new abort, ensuring valid PRL before handling a second abort. v2: - Revert to previous assert. - Revised non-leaf handling for interleaf child pt and leaf pte. - Update comments to specifications. (Stuart) - Remove unnecessary XE_PTE_PS64. (Matthew B) v3: - Modify secondary abort to only check non-leaf PTEs. (Matthew B) Fixes: b912138df299 ("drm/xe: Create page reclaim list on unbind") Signed-off-by: Brian Nguyen Reviewed-by: Matthew Brost Cc: Stuart Summers Link: https://patch.msgid.link/20260305171546.67691-6-brian3.nguyen@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/xe/xe_pt.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 13b355fadd58..2d9ce2c4cb4f 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1655,14 +1655,35 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, XE_WARN_ON(!level); /* Check for leaf node */ if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && - (!xe_child->base.children || !xe_child->base.children[first])) { + xe_child->level <= MAX_HUGEPTE_LEVEL) { struct iosys_map *leaf_map = &xe_child->bo->vmap; pgoff_t count = xe_pt_num_entries(addr, next, xe_child->level, walk); for (pgoff_t i = 0; i < count; i++) { - u64 pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64); + u64 pte; int ret; + /* + * If not a leaf pt, skip unless non-leaf pt is interleaved between + * leaf ptes which causes the page walk to skip over the child leaves + */ + if (xe_child->base.children && xe_child->base.children[first + i]) { + u64 pt_size = 1ULL << walk->shifts[xe_child->level]; + bool edge_pt = (i == 0 && !IS_ALIGNED(addr, pt_size)) || + (i == count - 1 && !IS_ALIGNED(next, pt_size)); + + if (!edge_pt) { + xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, + xe_walk->prl, + "PT is skipped by walk at level=%u offset=%lu", + xe_child->level, first + i); + break; + } + continue; + } + + pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64); + /* * In rare scenarios, pte may not be written yet due to racy conditions. * In such cases, invalidate the PRL and fallback to full PPC invalidation. @@ -1674,9 +1695,8 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, } /* Ensure it is a defined page */ - xe_tile_assert(xe_walk->tile, - xe_child->level == 0 || - (pte & (XE_PTE_PS64 | XE_PDE_PS_2M | XE_PDPE_PS_1G))); + xe_tile_assert(xe_walk->tile, xe_child->level == 0 || + (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G))); /* An entry should be added for 64KB but contigious 4K have XE_PTE_PS64 */ if (pte & XE_PTE_PS64) @@ -1701,11 +1721,11 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, killed = xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk); /* - * Verify PRL is active and if entry is not a leaf pte (base.children conditions), - * there is a potential need to invalidate the PRL if any PTE (num_live) are dropped. + * Verify if any PTE are potentially dropped at non-leaf levels, either from being + * killed or the page walk covers the region. */ - if (xe_walk->prl && level > 1 && xe_child->num_live && - xe_child->base.children && xe_child->base.children[first]) { + if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && + xe_child->level > MAX_HUGEPTE_LEVEL && xe_child->num_live) { bool covered = xe_pt_covers(addr, next, xe_child->level, &xe_walk->base); /* -- cgit v1.2.3 From d88fa967dc4335196d9ec2e07b2269bb53feec0d Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 5 Mar 2026 17:15:49 +0000 Subject: drm/xe: Move page reclaim done_handler to own func Originally, page reclamation is handled by the same fence as tlb invalidation and uses its seqno, so there was no reason to separate out the handlers. However in hindsight, for readability, and possible future changes, it seems more beneficial to move this all out to its own function. Signed-off-by: Brian Nguyen Reviewed-by: Matthew Brost Reviewed-by: Shuicheng Lin Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260305171546.67691-7-brian3.nguyen@intel.com Signed-off-by: Matt Roper --- drivers/gpu/drm/xe/xe_guc_ct.c | 23 ++++++++--------------- drivers/gpu/drm/xe/xe_page_reclaim.c | 20 ++++++++++++++++++++ drivers/gpu/drm/xe/xe_page_reclaim.h | 3 +++ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c index 3b1c03743f83..a11cff7a20be 100644 --- a/drivers/gpu/drm/xe/xe_guc_ct.c +++ b/drivers/gpu/drm/xe/xe_guc_ct.c @@ -31,6 +31,7 @@ #include "xe_guc_submit.h" #include "xe_guc_tlb_inval.h" #include "xe_map.h" +#include "xe_page_reclaim.h" #include "xe_pm.h" #include "xe_sleep.h" #include "xe_sriov_vf.h" @@ -1630,17 +1631,11 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len) ret = xe_guc_pagefault_handler(guc, payload, adj_len); break; case XE_GUC_ACTION_TLB_INVALIDATION_DONE: - case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: - /* - * Page reclamation is an extension of TLB invalidation. Both - * operations share the same seqno and fence. When either - * action completes, we need to signal the corresponding - * fence. Since the handling logic (lookup fence by seqno, - * fence signalling) is identical, we use the same handler - * for both G2H events. - */ ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len); break; + case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: + ret = xe_guc_page_reclaim_done_handler(guc, payload, adj_len); + break; case XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF: ret = xe_guc_relay_process_guc2pf(&guc->relay, hxg, hxg_len); break; @@ -1848,15 +1843,13 @@ static void g2h_fast_path(struct xe_guc_ct *ct, u32 *msg, u32 len) ret = xe_guc_pagefault_handler(guc, payload, adj_len); break; case XE_GUC_ACTION_TLB_INVALIDATION_DONE: - case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: - /* - * Seqno and fence handling of page reclamation and TLB - * invalidation is identical, so we can use the same handler - * for both actions. - */ __g2h_release_space(ct, len); ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len); break; + case XE_GUC_ACTION_PAGE_RECLAMATION_DONE: + __g2h_release_space(ct, len); + ret = xe_guc_page_reclaim_done_handler(guc, payload, adj_len); + break; default: xe_gt_warn(gt, "NOT_POSSIBLE\n"); } diff --git a/drivers/gpu/drm/xe/xe_page_reclaim.c b/drivers/gpu/drm/xe/xe_page_reclaim.c index e13c71a89da2..60b0fda59ce3 100644 --- a/drivers/gpu/drm/xe/xe_page_reclaim.c +++ b/drivers/gpu/drm/xe/xe_page_reclaim.c @@ -11,6 +11,7 @@ #include "xe_page_reclaim.h" #include "xe_gt_stats.h" +#include "xe_guc_tlb_inval.h" #include "xe_macros.h" #include "xe_pat.h" #include "xe_sa.h" @@ -130,3 +131,22 @@ int xe_page_reclaim_list_alloc_entries(struct xe_page_reclaim_list *prl) return page ? 0 : -ENOMEM; } + +/** + * xe_guc_page_reclaim_done_handler() - Page reclaim done handler + * @guc: guc + * @msg: message indicating page reclamation done + * @len: length of message + * + * Page reclamation is an extension of TLB invalidation. Both + * operations share the same seqno and fence. When either + * action completes, we need to signal the corresponding + * fence. Since the handling logic is currently identical, this + * function delegates to the TLB invalidation handler. + * + * Return: 0 on success, -EPROTO for malformed messages. + */ +int xe_guc_page_reclaim_done_handler(struct xe_guc *guc, u32 *msg, u32 len) +{ + return xe_guc_tlb_inval_done_handler(guc, msg, len); +} diff --git a/drivers/gpu/drm/xe/xe_page_reclaim.h b/drivers/gpu/drm/xe/xe_page_reclaim.h index 3dd103e37beb..0412611f3af7 100644 --- a/drivers/gpu/drm/xe/xe_page_reclaim.h +++ b/drivers/gpu/drm/xe/xe_page_reclaim.h @@ -20,6 +20,7 @@ struct xe_tlb_inval; struct xe_tlb_inval_fence; struct xe_tile; struct xe_gt; +struct xe_guc; struct xe_vma; struct xe_guc_page_reclaim_entry { @@ -122,4 +123,6 @@ static inline void xe_page_reclaim_entries_put(struct xe_guc_page_reclaim_entry put_page(virt_to_page(entries)); } +int xe_guc_page_reclaim_done_handler(struct xe_guc *guc, u32 *msg, u32 len); + #endif /* _XE_PAGE_RECLAIM_H_ */ -- cgit v1.2.3 From 1b12096b4bc5177d685ae098fdb90260ffd5db6b Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 5 Mar 2026 17:15:50 +0000 Subject: drm/xe: Skip adding PRL entry to NULL VMA NULL VMAs h