]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/xe/guc_ads: allocate UM queues in a separate BO
authorJia Yao <jia.yao@intel.com>
Tue, 4 Aug 2026 16:50:55 +0000 (16:50 +0000)
committerThomas Hellström <thomas.hellstrom@linux.intel.com>
Thu, 13 Aug 2026 12:56:33 +0000 (14:56 +0200)
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 <gwan-gyeong.mun@intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: <stable@vger.kernel.org> # v6.12+
Signed-off-by: Jia Yao <jia.yao@intel.com>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Signed-off-by: Matthew Auld <matthew.auld@intel.com>
Link: https://patch.msgid.link/20260804165057.129529-2-jia.yao@intel.com
(cherry picked from commit 6af05de0dc19bbf3aaeec2020fe48b37c834b811)
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
drivers/gpu/drm/xe/xe_bo.h
drivers/gpu/drm/xe/xe_guc.c
drivers/gpu/drm/xe/xe_guc_ads.c
drivers/gpu/drm/xe/xe_guc_ads_types.h

index 7ae1d9ac05743edad30940a032256894d153f550..57039cf42ea7f0abe4a1497f8c9a613f0e8bc136 100644 (file)
@@ -6,6 +6,7 @@
 #ifndef _XE_BO_H_
 #define _XE_BO_H_
 
+#include <drm/drm_prime.h>
 #include <drm/ttm/ttm_tt.h>
 
 #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
index 4023700ff2a9678f6168d776b49c892adc8a58e5..21cf3edef9223e1cec89fde0511ade7e53b84461 100644 (file)
@@ -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;
 }
 
index c98454545a85862e4f56df9b5c8e1f3bb2af3205..a411d6440ba619d39a31f9dff8fe1bf08990370c 100644 (file)
@@ -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) {
                /*
index 48a8e092023faffc3127f3a4d0ec3e5083381c41..845c1fbd93a4e537a302fcdc8d803f5b8fb9e154 100644 (file)
@@ -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 */