drm/xe/guc_ads: allocate UM queues in a separate BO

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: 9c57bc0865 ("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>
This commit is contained in:
Jia Yao 2026-08-04 16:50:55 +00:00 committed by Thomas Hellström
parent fc64875790
commit 99b0181595
4 changed files with 83 additions and 32 deletions

View 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

View 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;
}

View 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) {
/*

View 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 */