From fc648757908304aedbad74f74bf58192aec383db Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 30 Jun 2026 15:55:20 +0200 Subject: [PATCH 01/11] drm/xe: Fix DPT allocation paths. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the fallback for VRAM to system memory, I tested it and that doesn't work at all, only a black screen with pipe fault errors were observed. On systems with media GT, extra latency is added when accessing stolen memory when the GT is in MC6. Since we additionally aren't counting how much memory is used for stolen and we could in theory fill up the entire stolen area with DPT's, avoid using stolen and only use the default memory region. Using stolen may also result in random system hangs under load. Link: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7513 Fixes: 775d0adc01a5 ("drm/xe/fbdev: Limit the usage of stolen for LNL+") Cc: # v6.12+ Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260630135523.1775379-2-dev@lankhorst.se Signed-off-by: Maarten Lankhorst Acked-by: Matthew Brost #teams (cherry picked from commit a196406a3831291598fe8e73245914f7acffdfe0) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/display/xe_fb_pin.c | 33 +++++++------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index 5f4a0cd8deca..73469ea5f333 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -164,31 +164,14 @@ static int __xe_pin_fb_vma_dpt(struct drm_gem_object *obj, dpt_size = ALIGN(intel_rotation_info_size(&view->rotated) * 8, XE_PAGE_SIZE); - if (IS_DGFX(xe)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_VRAM0 | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - else - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_STOLEN | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE, - pin_params->alignment, false); - if (IS_ERR(dpt)) - dpt = xe_bo_create_pin_map_at_novm(xe, tile0, - dpt_size, ~0ull, - ttm_bo_type_kernel, - XE_BO_FLAG_SYSTEM | - XE_BO_FLAG_GGTT | - XE_BO_FLAG_PAGETABLE | - XE_BO_FLAG_FORCE_WC, - pin_params->alignment, false); + dpt = xe_bo_create_pin_map_at_novm(xe, tile0, + dpt_size, ~0ull, + ttm_bo_type_kernel, + XE_BO_FLAG_VRAM_IF_DGFX(tile0) | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_PAGETABLE | + XE_BO_FLAG_FORCE_WC, + pin_params->alignment, false); if (IS_ERR(dpt)) return PTR_ERR(dpt); From 99b01815957bd7d848420cb697f79ed11f7f215c Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:55 +0000 Subject: [PATCH 02/11] drm/xe/guc_ads: allocate UM queues in a separate BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the UM queues into a dedicated BO (ads->um_queue_bo) and avoid CPU memset operations on it, which eliminates the CPU as a potential cacheline-polluting agent and helps maintain consistency between GAM writes and GuC reads. We also need to ensure the base_dpa for the queue is contiguous on hw where this is used instead of a GGTT address. Another good reason to split this out to a separate BO. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-2-jia.yao@intel.com (cherry picked from commit 6af05de0dc19bbf3aaeec2020fe48b37c834b811) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_bo.h | 14 +++++ drivers/gpu/drm/xe/xe_guc.c | 6 ++ drivers/gpu/drm/xe/xe_guc_ads.c | 90 +++++++++++++++++---------- drivers/gpu/drm/xe/xe_guc_ads_types.h | 5 ++ 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h index 7ae1d9ac0574..57039cf42ea7 100644 --- a/drivers/gpu/drm/xe/xe_bo.h +++ b/drivers/gpu/drm/xe/xe_bo.h @@ -6,6 +6,7 @@ #ifndef _XE_BO_H_ #define _XE_BO_H_ +#include #include #include "xe_bo_types.h" @@ -548,6 +549,19 @@ void xe_bo_dev_fini(struct xe_bo_dev *bo_device); struct sg_table *xe_bo_sg(struct xe_bo *bo); +/** + * xe_bo_sg_is_contiguous() - Check if a BO's DMA address space is contiguous. + * @bo: the BO to check (must have a valid sg table, i.e. !xe_bo_is_vram()) + * @len: required contiguous length in bytes + * + * Returns true if the first @len bytes of the BO are mapped to a contiguous + * DMA address range. + */ +static inline bool xe_bo_sg_is_contiguous(struct xe_bo *bo, size_t len) +{ + return drm_prime_get_contiguous_size(xe_bo_sg(bo)) >= len; +} + /* * xe_sg_segment_size() - Provides upper limit for sg segment size. * @dev: device pointer diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 4023700ff2a9..21cf3edef922 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -714,6 +714,12 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc) if (ret) return ret; + if (guc->ads.um_queue_bo) { + ret = xe_managed_bo_reinit_in_vram(xe, tile, &guc->ads.um_queue_bo); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index c98454545a85..a411d6440ba6 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -63,10 +63,14 @@ ads_to_map(struct xe_guc_ads *ads) /* * The Additional Data Struct (ADS) has pointers for different buffers used by - * the GuC. One single gem object contains the ADS struct itself (guc_ads) and - * all the extra buffers indirectly linked via the ADS struct's entries. + * the GuC. One gem object (ads->bo) contains the ADS struct itself (guc_ads) + * and most of the extra buffers linked via the ADS struct's entries. The UM + * fault queues (PAGE_FAULT, PAGE_FAULT_RESPONSE, ACCESS_COUNTER rings) are + * kept in a separate BO (ads->um_queue_bo) so that the full memset of ads->bo + * performed on every GT reset does not discard fault descriptors already + * written into the rings by the GPU. * - * Layout of the ADS blob allocated for the GuC: + * Layout of the ADS blob (ads->bo): * * +---------------------------------------+ <== base * | guc_ads | @@ -98,10 +102,6 @@ ads_to_map(struct xe_guc_ads *ads) * +---------------------------------------+ * | padding | * +---------------------------------------+ <== 4K aligned - * | UM queues | - * +---------------------------------------+ - * | padding | - * +---------------------------------------+ <== 4K aligned * | private data | * +---------------------------------------+ * | padding | @@ -155,16 +155,6 @@ static size_t guc_ads_capture_size(struct xe_guc_ads *ads) return PAGE_ALIGN(ads->capture_size); } -static size_t guc_ads_um_queues_size(struct xe_guc_ads *ads) -{ - struct xe_device *xe = ads_to_xe(ads); - - if (!xe->info.has_usm) - return 0; - - return GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX; -} - static size_t guc_ads_private_data_size(struct xe_guc_ads *ads) { return PAGE_ALIGN(ads_to_guc(ads)->fw.private_data_size); @@ -205,22 +195,12 @@ static size_t guc_ads_capture_offset(struct xe_guc_ads *ads) return PAGE_ALIGN(offset); } -static size_t guc_ads_um_queues_offset(struct xe_guc_ads *ads) -{ - u32 offset; - - offset = guc_ads_capture_offset(ads) + - guc_ads_capture_size(ads); - - return PAGE_ALIGN(offset); -} - static size_t guc_ads_private_data_offset(struct xe_guc_ads *ads) { size_t offset; - offset = guc_ads_um_queues_offset(ads) + - guc_ads_um_queues_size(ads); + offset = guc_ads_capture_offset(ads) + + guc_ads_capture_size(ads); return PAGE_ALIGN(offset); } @@ -409,6 +389,46 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) ads->bo = bo; + if (xe->info.has_usm) { + /* + * Allocate a separate BO for the HW fault ring (UM queues). + * + * Round the size up to the next power of two so that + * (system memory, no IOMMU) the TTM pool issues a single + * alloc_pages(order=N) call, maximising the chance of getting + * a physically contiguous block. GuC requires contiguous DPA. + */ + size_t um_size = roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX); + + u32 um_flags = XE_BO_FLAG_SYSTEM | + XE_BO_FLAG_GGTT | + XE_BO_FLAG_GGTT_INVALIDATE | + XE_BO_FLAG_PINNED_NORESTORE; + + bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); + if (IS_ERR(bo)) + return PTR_ERR(bo); + + /* + * On pre-Xe3p platforms, GAM (not GuC) accesses the UM queue + * ring via base_dpa, which must be a contiguous DMA address + * range. Verify that the allocated pages are contiguous in + * DMA address space. + */ + if (!xe_bo_is_vram(bo) && + !xe_guc_using_main_gamctrl_queues(ads_to_guc(ads)) && + unlikely(!xe_bo_sg_is_contiguous(bo, + GUC_UM_QUEUE_SIZE * + GUC_UM_HW_QUEUE_MAX))) { + drm_err(&xe->drm, + "UM fault queue memory is not contiguous in DMA address space; GAM requires contiguous DPA\n"); + return -ENOMEM; + } + + ads->um_queue_bo = bo; + } + return 0; } ALLOW_ERROR_INJECTION(xe_guc_ads_init, ERRNO); /* See xe_pci_probe() */ @@ -820,7 +840,7 @@ static void guc_mmio_reg_state_init(struct xe_guc_ads *ads) static void guc_um_init_params(struct xe_guc_ads *ads) { - u32 um_queue_offset = guc_ads_um_queues_offset(ads); + struct xe_bo *um_bo = ads->um_queue_bo; struct xe_guc *guc = ads_to_guc(ads); struct xe_device *xe = ads_to_xe(ads); u64 base_dpa; @@ -830,8 +850,14 @@ static void guc_um_init_params(struct xe_guc_ads *ads) with_dpa = !xe_guc_using_main_gamctrl_queues(guc); - base_ggtt = xe_bo_ggtt_addr(ads->bo) + um_queue_offset; - base_dpa = xe_bo_main_addr(ads->bo, PAGE_SIZE) + um_queue_offset; + if (um_bo) { + /* All USM platforms: UM queues in dedicated um_queue_bo */ + base_ggtt = xe_bo_ggtt_addr(um_bo); + base_dpa = xe_bo_main_addr(um_bo, PAGE_SIZE); + } else { + /* Platform does not support USM: no UM queues, nothing to do */ + return; + } for (i = 0; i < GUC_UM_HW_QUEUE_MAX; ++i) { /* diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h index 48a8e092023f..845c1fbd93a4 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads_types.h +++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h @@ -16,6 +16,11 @@ struct xe_bo; struct xe_guc_ads { /** @bo: Xe BO for GuC ads blob */ struct xe_bo *bo; + /** + * @um_queue_bo: Dedicated BO for the HW fault ring (UM queues). + * NULL if the platform does not support USM. + */ + struct xe_bo *um_queue_bo; /** @golden_lrc_size: golden LRC size */ size_t golden_lrc_size; /** @regset_size: size of register set passed to GuC for save/restore */ From f342810a141f8a7e8b3786a6e4b6c0695a078a74 Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:56 +0000 Subject: [PATCH 03/11] drm/xe/guc_ads: allocate UM queues in VRAM on dGFX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On iGPU, the UM queue BO is allocated in system memory. On dGFX, the BO was previously created in system memory and later reallocated in xe_guc_realloc_post_hwconfig(). Allocate the UM queue BO directly in VRAM on dGFX, where it is ultimately required. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed-by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-3-jia.yao@intel.com (cherry picked from commit ace076ef0a854ab5940bacc539bf66afd61d118c) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc.c | 6 ------ drivers/gpu/drm/xe/xe_guc_ads.c | 8 +++++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 21cf3edef922..4023700ff2a9 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -714,12 +714,6 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc) if (ret) return ret; - if (guc->ads.um_queue_bo) { - ret = xe_managed_bo_reinit_in_vram(xe, tile, &guc->ads.um_queue_bo); - if (ret) - return ret; - } - return 0; } diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index a411d6440ba6..b6d07f2e73bf 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -393,15 +393,17 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) /* * Allocate a separate BO for the HW fault ring (UM queues). * - * Round the size up to the next power of two so that + * Round the size up to the next power of two so that on iGPU * (system memory, no IOMMU) the TTM pool issues a single * alloc_pages(order=N) call, maximising the chance of getting * a physically contiguous block. GuC requires contiguous DPA. */ - size_t um_size = roundup_pow_of_two(GUC_UM_QUEUE_SIZE * + size_t um_size = IS_DGFX(xe) ? + GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX : + roundup_pow_of_two(GUC_UM_QUEUE_SIZE * GUC_UM_HW_QUEUE_MAX); - u32 um_flags = XE_BO_FLAG_SYSTEM | + u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | XE_BO_FLAG_PINNED_NORESTORE; From 8d5134ae4177fa4f5a9bc8e71e6656cfc2852882 Mon Sep 17 00:00:00 2001 From: Jia Yao Date: Tue, 4 Aug 2026 16:50:57 +0000 Subject: [PATCH 04/11] drm/xe/guc_ads: use uncached mapping for UM queue BO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Pre-Xe3p platform, the GAM write the UM queue through DPA using UC. if GuC reads the queue via GGTT (WB), stale data may be observed when the cacheline has been polluted by another agent. To match the GAM's UC writes, configure the GuC mapping as UC as well. Fixes: 9c57bc08652a ("drm/xe/lnl: Drop force_probe requirement") Cc: Gwan-gyeong Mun Cc: Matthew Auld Cc: # v6.12+ Signed-off-by: Jia Yao Reviewed by: Matthew Auld Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260804165057.129529-4-jia.yao@intel.com (cherry picked from commit 9daa302a82590eeee7bdc68023ddad302df4b88c) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_ads.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index b6d07f2e73bf..886bafd31f45 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -406,7 +406,8 @@ int xe_guc_ads_init(struct xe_guc_ads *ads) u32 um_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT | XE_BO_FLAG_GGTT_INVALIDATE | - XE_BO_FLAG_PINNED_NORESTORE; + XE_BO_FLAG_PINNED_NORESTORE | + XE_BO_FLAG_NEEDS_UC; bo = xe_managed_bo_create_pin_map(xe, tile, um_size, um_flags); if (IS_ERR(bo)) From 9f83c94469ff0fa37274b873ba24922e02531fa7 Mon Sep 17 00:00:00 2001 From: Matthew Brost Date: Fri, 7 Aug 2026 10:17:16 -0700 Subject: [PATCH 05/11] drm/xe: Order ring writes before ring tail updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ring buffer and the LRC context image are both mapped WC, so the ring tail update can become visible to the device before the ring contents it is meant to publish. The GuC CT send does contain an xe_device_wmb(), so sending the H2G would flush the ring contents. The problem is that it comes too late: xe_lrc_set_ring_tail() publishes the tail before the H2G is sent, and the device samples the tail from the context image independently of it, either at context switch-in or while the context is already resident. A submitter which is interrupted between updating the tail and sending its H2G therefore leaves the device free to observe the new tail while the ring contents behind it are not yet visible: 1. Thread A emits a job into the ring, sets the tail to T_A and sends the H2G, which flushes A's ring contents. The GuC starts scheduling the context in, but it is not executing yet. 2. Thread B emits a job into ring[T_A..T_B]. Those writes are not yet visible to the device. 3. Thread B updates the ring tail to T_B. That write targets a different page and becomes visible first. 4. Thread B is interrupted before it sends its H2G, so the flush which would have published ring[T_A..T_B] has not happened yet. 5. The context is switched in and samples the ring tail from the context image, picking up T_B rather than T_A. 6. The GPU executes A's job, advances HEAD to T_A, and continues on to ring[T_A..T_B], which still holds the previous wrap's contents, so the CS parses stale commands. The result is command stream corruption, which typically manifests as a hang or a spurious pagefault rather than anything that points back at the submission path. Kernel jobs are by far the most likely to hit this. Kernel queues such as the migration queue are shared and can be driven by many threads concurrently, producing back-to-back submissions on an LRC which is already executing. User queues are typically tied to a single submitting thread, so the same interleaving is much harder to produce. Add an xe_device_wmb() at the end of xe_lrc_write_ring() so that it covers every ring tail publication site, and so the invariant is local: once xe_lrc_write_ring() returns, the ring contents are visible to the device. Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8651 Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7810 Cc: stable@vger.kernel.org Signed-off-by: Matthew Brost Assisted-by: GitHub_Copilot:claude-opus-5 Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260807171716.140475-1-matthew.brost@intel.com (cherry picked from commit 136360290f314890428a3fbf31aaa8e4f1d43567) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_lrc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index a4292a11391d..78969d3e5904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1849,6 +1849,13 @@ void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size) __xe_lrc_write_ring(lrc, ring, &noop, sizeof(noop)); } + + /* + * The ring and the LRC context image are both WC, so the ring tail + * update which publishes these writes can become visible to the device + * first. Ensure the ring contents are visible before returning. + */ + xe_device_wmb(xe); } u64 xe_lrc_descriptor(struct xe_lrc *lrc) From 51afaf53e01e01bda489fc6ffacf07a706e72783 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 20 Jul 2026 15:27:58 -0700 Subject: [PATCH 06/11] drm/xe/pxp: add termination on resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suspend/resume causes the PXP keys to become invalid, but doesn't actually kill the session. The driver also doesn't explicitly kill and re-start the session until a new PXP request comes in, which means that the "zombie" session can potentially stick around if there are no new requests from userspace. While this is not an issue for PXP, HDCP has a new behavior starting on PTL where a communication is sent to GSC if a session is active at suspend time (even if it doesn't have a valid key), which can lead to delays in the suspend flow if we suspend while the zombie session is still active. To avoid this, we can trigger a termination on resume and kill the zombie session immediately, instead of delaying the termination to the next PXP request. Due to restrictions in the rpm suspend/resume flow, we can't call the termination flow from within the resume call itself, so the pxp irq worker is expanded to cover this scenario. The existing logic in the worker doesn't work as-is for the new flow, because the pm_get_if_active will fail if the worker runs before the pci_resume call has completed (which is possible, since we queue it from within that call) or after we're started to suspend again. Given that we always want to run the worker after a resume (differently from the irq case, where we want to skip if we're suspended), we can solve this by just taking the PM reference before queueing the worker. As part of this rework, the pxp->events variable has been moved to atomic, to avoid having to take xe->irq.lock from non-irq related paths. Fixes: b1dcec9bd8a1 ("drm/xe/ptl: Enable PXP for PTL") Signed-off-by: Daniele Ceraolo Spurio Cc: Rodrigo Vivi Cc: Julia Filipchuk Cc: Alan Previn Reviewed-by: Alan Previn Link: https://patch.msgid.link/20260720222757.3876338-2-daniele.ceraolospurio@intel.com (cherry picked from commit 757bda2b8b93fa36ad9b2c7993081d5f9d0d6e3b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_pxp.c | 162 ++++++++++++++++++++++-------- drivers/gpu/drm/xe/xe_pxp_types.h | 27 +++-- 2 files changed, 139 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 968b7e70b3f9..d17ab6e59df5 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -8,6 +8,8 @@ #include #include +#include + #include "xe_bo.h" #include "xe_bo_types.h" #include "xe_device_types.h" @@ -164,16 +166,9 @@ static void mark_termination_in_progress(struct xe_pxp *pxp) pxp->status = XE_PXP_TERMINATION_IN_PROGRESS; } -static void pxp_terminate(struct xe_pxp *pxp) +static bool pxp_prep_for_termination(struct xe_pxp *pxp) { - int ret = 0; - struct xe_device *xe = pxp->xe; - - if (!wait_for_completion_timeout(&pxp->activation, - msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) - drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); - - mutex_lock(&pxp->mutex); + lockdep_assert_held(&pxp->mutex); if (pxp->status == XE_PXP_ACTIVE) pxp->key_instance++; @@ -182,10 +177,8 @@ static void pxp_terminate(struct xe_pxp *pxp) * we'll mark the status as needing termination on resume, so no need to * emit a termination now. */ - if (pxp->status == XE_PXP_SUSPENDED) { - mutex_unlock(&pxp->mutex); - return; - } + if (pxp->status == XE_PXP_SUSPENDED) + return false; /* * If we have a termination already in progress, we need to wait for @@ -195,15 +188,44 @@ static void pxp_terminate(struct xe_pxp *pxp) */ if (pxp->status == XE_PXP_TERMINATION_IN_PROGRESS) { pxp->status = XE_PXP_NEEDS_ADDITIONAL_TERMINATION; - mutex_unlock(&pxp->mutex); - return; + return false; } mark_termination_in_progress(pxp); - mutex_unlock(&pxp->mutex); + return true; +} - pxp_invalidate_queues(pxp); +static void pxp_terminate(struct xe_pxp *pxp, bool hw_only) +{ + struct xe_device *xe = pxp->xe; + int ret = 0; + + if (!wait_for_completion_timeout(&pxp->activation, + msecs_to_jiffies(PXP_ACTIVATION_TIMEOUT_MS))) + drm_err(&xe->drm, "failed to wait for PXP start before termination\n"); + + if (!hw_only) { + bool prep_ok; + + mutex_lock(&pxp->mutex); + + prep_ok = pxp_prep_for_termination(pxp); + + mutex_unlock(&pxp->mutex); + + if (!prep_ok) + return; + + pxp_invalidate_queues(pxp); + } else { + /* + * The caller of the HW-only termination should have already + * called pxp_prep_for_termination and marked the termination as + * in progress. + */ + xe_assert(xe, !completion_done(&pxp->termination)); + } ret = pxp_terminate_hw(pxp); if (ret) { @@ -249,33 +271,46 @@ static void pxp_terminate_complete(struct xe_pxp *pxp) mutex_unlock(&pxp->mutex); } -static void pxp_irq_work(struct work_struct *work) +static void pxp_events_work(struct work_struct *work) { - struct xe_pxp *pxp = container_of(work, typeof(*pxp), irq.work); + struct xe_pxp *pxp = container_of(work, typeof(*pxp), events.work); struct xe_device *xe = pxp->xe; + bool hw_only = false; u32 events = 0; - spin_lock_irq(&xe->irq.lock); - events = pxp->irq.events; - pxp->irq.events = 0; - spin_unlock_irq(&xe->irq.lock); + events = atomic_xchg(&pxp->events.pending, 0); if (!events) return; /* - * If we're processing a termination irq while suspending then don't - * bother, we're going to re-init everything on resume anyway. + * If the termination request comes from an irq while we're suspending, + * then we can defer it to the resume path instead of waking the device + * up. + * In the case of the termination on resume the pm reference is taken + * in xe_pxp_pm_resume() and released here. + * Note that we do not expect both events to be set at the same time, + * but if it does happen due to a spurious interrupt we want to behave + * as if the only request we got was the one from the resume path; this + * is because the termination prep has already been done in + * xe_pxp_pm_resume() and it is impossible for any PXP operations to + * occur between the prep and the termination completion, so there is no + * need for a new SW prep. */ - if ((events & PXP_TERMINATION_REQUEST) && !xe_pm_runtime_get_if_active(xe)) + if (events & PXP_TERMINATION_REQUEST_ON_RESUME) { + events &= ~PXP_TERMINATION_REQUEST_IRQ; + hw_only = true; + } + + if ((events & PXP_TERMINATION_REQUEST_IRQ) && !xe_pm_runtime_get_if_active(xe)) return; if (events & PXP_TERMINATION_REQUEST) { - events &= ~PXP_TERMINATION_COMPLETE; - pxp_terminate(pxp); + events &= ~PXP_TERMINATION_COMPLETE_IRQ; + pxp_terminate(pxp, hw_only); } - if (events & PXP_TERMINATION_COMPLETE) + if (events & PXP_TERMINATION_COMPLETE_IRQ) pxp_terminate_complete(pxp); if (events & PXP_TERMINATION_REQUEST) @@ -296,20 +331,18 @@ void xe_pxp_irq_handler(struct xe_device *xe, u16 iir) return; } - lockdep_assert_held(&xe->irq.lock); - if (unlikely(!iir)) return; if (iir & (KCR_PXP_STATE_TERMINATED_INTERRUPT | KCR_APP_TERMINATED_PER_FW_REQ_INTERRUPT)) - pxp->irq.events |= PXP_TERMINATION_REQUEST; + atomic_or(PXP_TERMINATION_REQUEST_IRQ, &pxp->events.pending); if (iir & KCR_PXP_STATE_RESET_COMPLETE_INTERRUPT) - pxp->irq.events |= PXP_TERMINATION_COMPLETE; + atomic_or(PXP_TERMINATION_COMPLETE_IRQ, &pxp->events.pending); - if (pxp->irq.events) - queue_work(pxp->irq.wq, &pxp->irq.work); + if (atomic_read(&pxp->events.pending)) + queue_work(pxp->events.wq, &pxp->events.work); } static int kcr_pxp_set_status(const struct xe_pxp *pxp, bool enable) @@ -340,7 +373,7 @@ static void pxp_fini(void *arg) { struct xe_pxp *pxp = arg; - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); xe_pxp_destroy_execution_resources(pxp); /* no need to explicitly disable KCR since we're going to do an FLR */ @@ -402,7 +435,7 @@ int xe_pxp_init(struct xe_device *xe) INIT_LIST_HEAD(&pxp->queues.list); spin_lock_init(&pxp->queues.lock); - INIT_WORK(&pxp->irq.work, pxp_irq_work); + INIT_WORK(&pxp->events.work, pxp_events_work); pxp->xe = xe; pxp->gt = gt; @@ -421,8 +454,8 @@ int xe_pxp_init(struct xe_device *xe) mutex_init(&pxp->mutex); - pxp->irq.wq = alloc_ordered_workqueue("pxp-wq", 0); - if (!pxp->irq.wq) { + pxp->events.wq = alloc_ordered_workqueue("pxp-wq", 0); + if (!pxp->events.wq) { err = -ENOMEM; goto out_free; } @@ -442,7 +475,7 @@ int xe_pxp_init(struct xe_device *xe) out_kcr_disable: kcr_pxp_disable(pxp); out_wq: - destroy_workqueue(pxp->irq.wq); + destroy_workqueue(pxp->events.wq); out_free: drmm_kfree(&xe->drm, pxp); out: @@ -889,6 +922,7 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) fallthrough; case XE_PXP_ACTIVE: pxp->key_instance++; + pxp->needs_termination_on_resume = true; needs_queue_inval = true; break; } @@ -924,6 +958,7 @@ int xe_pxp_pm_suspend(struct xe_pxp *pxp) */ void xe_pxp_pm_resume(struct xe_pxp *pxp) { + bool has_pm = false; int err; if (!xe_pxp_is_enabled(pxp)) @@ -931,14 +966,57 @@ void xe_pxp_pm_resume(struct xe_pxp *pxp) err = kcr_pxp_enable(pxp); + /* + * We want to avoid the device runtime suspending before we're done with + * the termination queued below, so we need a runtime PM reference; we + * can't call the rpm functions from within the PXP lock, so we take the + * ref here. Note that we don't want the rpm resume code to actually run + * here as that would call back into this function, but as long as we + * don't enable DPM_FLAG_SMART_SUSPEND (which we currently do not) we're + * guaranteed to not be runtime suspended at this point, so we can + * safely use the get_noresume variant. + */ + if (pxp->needs_termination_on_resume) { + has_pm = true; + + xe_assert(pxp->xe, !dev_pm_smart_suspend(pxp->xe->drm.dev)); + xe_pm_runtime_get_noresume(pxp->xe); + } + mutex_lock(&pxp->mutex); xe_assert(pxp->xe, pxp->status == XE_PXP_SUSPENDED); - if (err) + if (err) { pxp->status = XE_PXP_ERROR; - else + } else { pxp->status = XE_PXP_NEEDS_TERMINATION; + if (pxp->needs_termination_on_resume) { + pxp->needs_termination_on_resume = false; + + /* + * We can't call pxp_terminate_hw directly from here + * because we're not allowed to do allocations within + * the rpm resume call, so we defer the termination to + * the worker that we use for the termination irqs. + * However, we do not want any PXP ops to go through + * between the suspend completing and the worker + * starting, so we need to do the termination prep + * immediately, which will mark the termination as in + * progress and stall PXP ops. + */ + if (pxp_prep_for_termination(pxp)) { + has_pm = false; /* move PM ref ownership to worker */ + + atomic_or(PXP_TERMINATION_REQUEST_ON_RESUME, &pxp->events.pending); + queue_work(pxp->events.wq, &pxp->events.work); + } + } + } + mutex_unlock(&pxp->mutex); + + if (has_pm) + xe_pm_runtime_put(pxp->xe); } diff --git a/drivers/gpu/drm/xe/xe_pxp_types.h b/drivers/gpu/drm/xe/xe_pxp_types.h index ec86306e16f4..8132a9750b6e 100644 --- a/drivers/gpu/drm/xe/xe_pxp_types.h +++ b/drivers/gpu/drm/xe/xe_pxp_types.h @@ -85,17 +85,20 @@ struct xe_pxp { /** @gsc_res: kernel-owned objects for PXP submissions to the GSCCS */ struct xe_pxp_gsc_client_resources gsc_res; - /** @irq: wrapper for the worker and queue used for PXP irq support */ + /** @events: wrapper for the worker and queue used for PXP event handling */ struct { - /** @irq.work: worker that manages irq events. */ + /** @events.work: worker that manages termination events. */ struct work_struct work; - /** @irq.wq: workqueue on which to queue the irq work. */ + /** @events.wq: workqueue on which to queue the work. */ struct workqueue_struct *wq; - /** @irq.events: pending events, protected with xe->irq.lock. */ - u32 events; -#define PXP_TERMINATION_REQUEST BIT(0) -#define PXP_TERMINATION_COMPLETE BIT(1) - } irq; + /** @events.pending: pending events */ + atomic_t pending; +#define PXP_TERMINATION_REQUEST_IRQ BIT(0) +#define PXP_TERMINATION_REQUEST_ON_RESUME BIT(1) +#define PXP_TERMINATION_REQUEST (PXP_TERMINATION_REQUEST_IRQ | \ + PXP_TERMINATION_REQUEST_ON_RESUME) +#define PXP_TERMINATION_COMPLETE_IRQ BIT(2) + } events; /** @mutex: protects the pxp status and the queue list */ struct mutex mutex; @@ -130,6 +133,14 @@ struct xe_pxp { * suspend cycles. */ u32 last_suspend_key_instance; + /** + * @needs_termination_on_resume: indicates if PXP termination is needed + * on resume. This is set if PXP was active when we suspend and it is + * cleared when we queue the termination on resume. Since the suspend + * and resume calls cannot execute at the same time, this variable does + * not need to be protected by the PXP lock. + */ + bool needs_termination_on_resume; }; #endif /* _XE_PXP_TYPES_H_ */ From cb4afddf9e018a83fec8614d8e337d313871569f Mon Sep 17 00:00:00 2001 From: Gajendra Uttamchand Date: Mon, 10 Aug 2026 07:18:14 +0000 Subject: [PATCH 07/11] drm/xe/lrc: document sentinel and make CTX_TIMESTAMP read TOCTOU-safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: CTX_TIMESTAMP MMIO reads could be stale if a context switched out between check and read; LRC stores a sentinel while a context starts that must not be treated as a real timestamp. Fix: Check the LRC-stored sentinel before and after the MMIO read; return the LRC value if the context switched out to avoid TOCTOU. Note: Keep XE_LRC_CTX_TIMESTAMP_ACTIVE in xe_lrc.h as the canonical sentinel. Fixes: d243ef6a39c6 ("drm/xe/lrc: Refactor xe_lrc_timestamp to simplify logic") Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/7956 Assisted-by: GitHub-Copilot:claude-sonnet-5 Signed-off-by: Gajendra Uttamchand Reviewed-by: Umesh Nerlige Ramappa Acked-by: Matthew Brost Signed-off-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260810071812.213358-4-gajendra.uttamchand@intel.com (cherry picked from commit a806534474df071a730d930df479976a812b699d) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_lrc.c | 22 ++++++++++++++-------- drivers/gpu/drm/xe/xe_lrc.h | 7 +++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index 78969d3e5904..9f8217ff1904 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -1096,7 +1096,7 @@ static void xe_lrc_finish(struct xe_lrc *lrc) * on until it is scheduled, we also read the ENGINE_ID MMIO in the WA BB and * store it in the PPHSWP. */ -#define CONTEXT_ACTIVE 1ULL +#define CONTEXT_ACTIVE XE_LRC_CTX_TIMESTAMP_ACTIVE static ssize_t setup_utilization_wa(struct xe_lrc *lrc, struct xe_hw_engine *hwe, u32 *batch, @@ -2727,21 +2727,27 @@ static u64 xe_lrc_update_multi_queue_timestamp(struct xe_lrc *lrc, u64 *old_ts) static u64 xe_lrc_context_timestamp(struct xe_lrc *lrc) { u64 reg_ts, new_ts = lrc->ctx_timestamp; + u64 stored; /* CTX_TIMESTAMP mmio read is invalid on VF, so return the LRC value */ if (IS_SRIOV_VF(lrc_to_xe(lrc))) return xe_lrc_ctx_timestamp(lrc); - if (context_active(lrc) && - !get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) + /* Safely read CTX_TIMESTAMP: Avoid TOCTOU on LRC-stored CONTEXT_ACTIVE sentinel */ + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; + + /* Context is active: read the live timestamp from the engine's MMIO register */ + if (!get_ctx_timestamp(lrc, xe_lrc_engine_id(lrc), ®_ts)) new_ts = reg_ts; - /* - * If context swicthed out while we were here, just return the latest - * LRC CTX TIMESTAMP value. + /* If the context switched out prefer using the value + * from context-save over the stale MMIO read. */ - if (!context_active(lrc)) - return xe_lrc_ctx_timestamp(lrc); + stored = xe_lrc_ctx_timestamp(lrc); + if (stored != CONTEXT_ACTIVE) + return stored; return new_ts; } diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index 0a3a611391ee..7be5e3da8bc8 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -9,6 +9,13 @@ #include "xe_lrc_types.h" +/* + * Sentinel value stored in lrc->ctx_timestamp while a context is starting. + * The hardware hasn't yet written the real CTX_TIMESTAMP, so this is not a + * valid elapsed-time sample and must not be used as one. + */ +#define XE_LRC_CTX_TIMESTAMP_ACTIVE 1ULL + struct drm_printer; struct xe_bb; struct xe_device; From 8d33c4987cd162527375a3905017ae129ba7c3fe Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Fri, 31 Jul 2026 09:19:32 +0800 Subject: [PATCH 08/11] drm/xe/oa: Fix sync entry leak on OA config emit failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xe_oa_emit_oa_config() releases the sync entries and the syncs array only on its success path. When it fails before the point of no return (fence allocation, config buffer allocation or batch submission), it returns without touching stream->syncs. The stream open path handles such failures in the caller, but xe_oa_config_locked() propagates the error without any cleanup, so the syncs array and the fence references held by the parsed entries are leaked. The next config ioctl overwrites stream->syncs, making the memory unreachable for good. Clean up the parsed syncs when xe_oa_emit_oa_config() fails, matching the cleanup done by the stream open error path. Fixes: 9920c8b88c5c ("drm/xe/oa: Add syncs support to OA config ioctl") Signed-off-by: Linmao Li Reviewed-by: Ashutosh Dixit Signed-off-by: Ashutosh Dixit Link: https://patch.msgid.link/20260731011932.3426219-1-lilinmao@kylinos.cn (cherry picked from commit 8af97b3da2cfce04e6b457c6eb17ed3c1daf912b) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 2dce6a47202c..9cf962b71cb6 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -1594,6 +1594,10 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg) config = xchg(&stream->oa_config, config); drm_dbg(&stream->oa->xe->drm, "changed to oa config uuid=%s\n", stream->oa_config->uuid); + } else { + while (param.num_syncs--) + xe_sync_entry_cleanup(¶m.syncs[param.num_syncs]); + kfree(param.syncs); } err_config_put: From f110dbbfa2a94c91704bf19806907a98fd73ca14 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Mon, 13 Jul 2026 16:30:53 +0800 Subject: [PATCH 09/11] drm/xe/oa: Check managed mutex initialization errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drmm_mutex_init() can fail while registering its managed cleanup action. On failure, the reset path destroys the mutex, so continuing OA setup leaves an unusable lock that later paths may acquire. Return the error from per-GT OA initialization and abort device-wide OA initialization if the metrics lock cannot be initialized. Fixes: a9f905ae7b6f ("drm/xe/oa/uapi: Initialize OA units") Fixes: cdf02fe1a94a ("drm/xe/oa/uapi: Add/remove OA config perf ops") Signed-off-by: Linmao Li Reviewed-by: Umesh Nerlige Ramappa Signed-off-by: Umesh Nerlige Ramappa Link: https://patch.msgid.link/20260713083053.321091-1-lilinmao@kylinos.cn (cherry picked from commit 360b293de27bfdd0d07047f8efd5ba8e91fa90b7) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_oa.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 9cf962b71cb6..18d990c5d4ec 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -2717,9 +2717,7 @@ static int xe_oa_init_gt(struct xe_gt *gt) __xe_oa_init_oa_units(gt); - drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); - - return 0; + return drmm_mutex_init(>_to_xe(gt)->drm, >->oa.gt_lock); } static void xe_oa_print_gt_oa_units(struct xe_gt *gt) @@ -2859,7 +2857,10 @@ int xe_oa_init(struct xe_device *xe) oa->xe = xe; oa->oa_formats = oa_formats; - drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + ret = drmm_mutex_init(&oa->xe->drm, &oa->metrics_lock); + if (ret) + goto exit; + idr_init_base(&oa->metrics_idr, 1); ret = xe_oa_init_oa_units(oa); From 5cf82c8cec90056511eb881a267aab6101eaf57a Mon Sep 17 00:00:00 2001 From: Vinay Belgaumkar Date: Wed, 5 Aug 2026 16:46:49 -0700 Subject: [PATCH 10/11] drm/xe: Fix a bug in pc_adjust_freq_bounds() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cases where min frequency was actually greater than BMG_MIN_FREQ, we were not using the updated min frequency as there was a missing call to pc_action_query_task_state() between the two settings of min frequency. Since we know what min_freq was last set, use that cached value while comparing to BMG_MIN_FREQ to fix this issue. v2: pc->freq_ready is not set until after pc_adjust_freq_bounds(). Stay with pc_action_query_task_state() instead. v3: Update commit message (Stuart) Fixes: bdde16c9ac5c ("drm/xe/bmg: Update Wa_14022085890") Signed-off-by: Balasubramani Vivekanandan Signed-off-by: Vinay Belgaumkar Reviewed-by: Stuart Summers Link: https://patch.msgid.link/20260805234649.2076384-1-vinay.belgaumkar@intel.com (cherry picked from commit a2c2d2b13a9ea9494d2d76b46273833111749507) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_guc_pc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c index 59f2fa79ad42..7cf8f4858598 100644 --- a/drivers/gpu/drm/xe/xe_guc_pc.c +++ b/drivers/gpu/drm/xe/xe_guc_pc.c @@ -911,6 +911,7 @@ static bool pc_needs_min_freq_change(struct xe_guc_pc *pc) static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) { int ret; + u32 min_freq; lockdep_assert_held(&pc->freq_lock); @@ -933,8 +934,14 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * Same thing happens for Server platforms where min is listed as * RPMax */ - if (pc_get_min_freq(pc) > pc->rp0_freq) + min_freq = pc_get_min_freq(pc); + if (min_freq > pc->rp0_freq) { ret = pc_set_min_freq(pc, pc->rp0_freq); + if (ret) + goto out; + + min_freq = pc->rp0_freq; + } /* * Setting GT RP min frequency to 1.2GHz by default for @@ -947,8 +954,8 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc) * we aren't expecting high power output across board * */ - if (pc_needs_min_freq_change(pc)) - ret = pc_set_min_freq(pc, max(BMG_MIN_FREQ, pc_get_min_freq(pc))); + if (pc_needs_min_freq_change(pc) && min_freq < BMG_MIN_FREQ) + ret = pc_set_min_freq(pc, BMG_MIN_FREQ); out: return ret; From ef526d122b62af5afa437f095aa6661a953676c4 Mon Sep 17 00:00:00 2001 From: Raag Jadav Date: Mon, 10 Aug 2026 18:08:20 +0530 Subject: [PATCH 11/11] drm/xe: Fix xe_device_probe() failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, xe_device_probe() jumps to err_unregister_display label in case of failure except for its last call, which directly returns the error without required cleanup handling. This results in stale drm device that isn't cleaned up on unwind. Fix it. [ 810.194180] sysfs: cannot create duplicate filename '/devices/pci0000:00/0000:00:01.0/0000:01:00.0/0000:02:01.0/0000:03:00.0/drm/renderD128' [ 810.194183] CPU: 9 UID: 0 PID: 5616 Comm: modprobe Kdump: loaded Tainted: G S U E 7.2.0-rc2-xe #382 PREEMPT(full) [ 810.194185] Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER, [E]=UNSIGNED_MODULE [ 810.194186] Hardware name: ASUS System Product Name/PRIME Z790-P WIFI, BIOS 1805 10/30/2024 [ 810.194186] Call Trace: [ 810.194187] [ 810.194188] dump_stack_lvl+0xe0/0x100 [ 810.194195] dump_stack+0x14/0x20 [ 810.194197] sysfs_warn_dup+0x5f/0x80 [ 810.194204] sysfs_create_dir_ns+0xbe/0xd0 [ 810.194210] kobject_add_internal+0xbc/0x2b0 [ 810.194215] kobject_add+0x7c/0xe0 [ 810.194220] ? get_device_parent+0xcf/0x1e0 [ 810.194227] device_add+0xe3/0x870 [ 810.194231] ? __pfx_drm_gem_name_info+0x10/0x10 [drm] [ 810.194280] drm_minor_register+0x73/0x130 [drm] [ 810.194322] drm_dev_register+0x76/0x2a0 [drm] Cc: stable@vger.kernel.org Fixes: da3799c97572 ("drm/xe: Use GuC to do GGTT invalidations for the GuC firmware") Signed-off-by: Raag Jadav Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260810123821.105605-1-raag.jadav@intel.com Signed-off-by: Matt Roper (cherry picked from commit 5ce3042c67c539480882567137ff8d56118885d6) Signed-off-by: Thomas Hellström --- drivers/gpu/drm/xe/xe_device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index dcb48caa485d..a2b076b1dc60 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1102,7 +1102,11 @@ int xe_device_probe(struct xe_device *xe) if (err) goto err_unregister_display; - return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + err = devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe); + if (err) + goto err_unregister_display; + + return 0; err_unregister_display: xe_display_unregister(xe);