Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Stuart Summers" <stuart.summers@intel.com>
Cc: intel-xe@lists.freedesktop.org
Subject: Re: [PATCH 09/11] drm/xe: Fall back to CPU copies when the tile cannot blit
Date: Wed, 11 Nov 2026 04:25:04 +0000	[thread overview]
Message-ID: <sashiko-outbox-208281@kernel.org> (raw)
In-Reply-To: <20261110231442.218721-22-stuart.summers@intel.com>

Thank you for your contribution! Sashiko AI review found 6 potential
issues to consider:
- [High] drm/xe: WARN_ON and -EINVAL in xe_bo_move() on initial VRAM BO
  clear
- [High] drm/xe: invalid BAR mapping in xe_bo_move() for non-contiguous
  VRAM BOs
- [High] drm/xe: WARN_ON in xe_bo_vmap() during pinned BO save and
  restore
- [High] drm/xe: missing fence wait before ttm_bo_move_memcpy() in
  xe_bo_move()
- [Medium] drm/xe: blitter commands emitted in xe_ttm_access_memory()
  and memtest
- [Medium] drm/xe: missing fence wait before CPU copy in
  xe_bo_evict_pinned_copy()
--

commit fcb1ac36084339205a63d9c4c32dc0af3e3062ff
Author: Stuart Summers <stuart.summers@intel.com>

drm/xe: Fall back to CPU copies when the tile cannot blit

This commit adds an xe_migrate_can_blit() helper and falls back to CPU
copies in xe_bo_move(), xe_bo_evict_pinned_copy(), and
xe_bo_restore_pinned() when a tile has no primary GT copy engine.

> diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
> index 42a6aa1a2e4a0..49a3d4df77f62 100644
> --- a/drivers/gpu/drm/xe/xe_bo.c
> +++ b/drivers/gpu/drm/xe/xe_bo.c
> @@ -1275,6 +1275,19 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
>  		xe_pm_runtime_get_noresume(xe);
>  	}
>  
> +	/*
> +	 * A tile with no copy engine cannot blit, so move the data with the
> +	 * CPU instead.  For VRAM that means going over the PCIe BAR, which is
> +	 * slow, but a device with no graphics IP offers no alternative.
> +	 * ttm_bo_move_memcpy() covers the clear case too, by way of an
> +	 * unpopulated source.
> +	 */
> +	if (!xe_migrate_can_blit(migrate)) {
> +		ret = ttm_bo_move_memcpy(ttm_bo, ctx, new_mem);
> +		xe_pm_runtime_put(xe);
> +		goto out;
> +	}

[Severity: High]
Does calling ttm_bo_move_memcpy() here hit WARN_ON(!src_mem) and return
-EINVAL when clearing a newly created VRAM BO?

When xe_bo_init_locked() creates a standard user VRAM BO without
XE_BO_FLAG_DEFER_BACKING, ttm_bo_validate() calls xe_bo_move() while
ttm_bo->resource is still NULL (move_lacks_source is true and needs_clear
is true).  ttm_bo_move_memcpy() only supports clearing from an existing
non-NULL use_tt resource with an unpopulated ttm_tt, and rejects a NULL
bo->resource at the start of the function:

drivers/gpu/drm/ttm/ttm_bo_util.c:ttm_bo_move_memcpy() {
	struct ttm_resource *src_mem = bo->resource;
	...
	if (WARN_ON(!src_mem))
		return -EINVAL;
}

Every non-deferred VRAM BO allocation on a tile that cannot blit will
warn and fail with -EINVAL.

[Severity: High]
Can passing a non-contiguous VRAM BO to ttm_bo_move_memcpy() corrupt
memory at the start of the VRAM BAR?

Unpinned user VRAM BOs do not set TTM_PL_FLAG_CONTIGUOUS, so when
xe_ttm_vram_mgr_new() allocates multiple discontiguous buddy blocks it
sets vres->base.start to XE_BO_INVALID_OFFSET (LONG_MAX).
ttm_bo_move_memcpy() uses ttm_kmap_iter_linear_io_init(), which calls
xe_ttm_io_mem_reserve():

drivers/gpu/drm/xe/xe_bo.c:xe_ttm_io_mem_reserve() {
	case XE_PL_VRAM0:
	case XE_PL_VRAM1: {
		...
		mem->bus.offset = mem->start << PAGE_SHIFT;

		if (vram->mapping &&
		    mem->placement & TTM_PL_FLAG_CONTIGUOUS)
			mem->bus.addr = (u8 __force *)vram->mapping +
				mem->bus.offset;

		mem->bus.offset += vram->io_start;
		mem->bus.is_iomem = true;
}

With mem->start == LONG_MAX and TTM_PL_FLAG_CONTIGUOUS clear,
mem->bus.addr stays NULL and mem->bus.offset wraps to
vram->io_start - PAGE_SIZE.  ttm_kmap_iter_linear_io_init() then ioremaps
mem->size bytes starting one page before the VRAM BAR and
ttm_move_memcpy() linearly reads, writes, or zeroes across that address
range instead of walking the BO's buddy blocks.

[Severity: High]
Does ttm_bo_move_memcpy() also run without waiting for in-flight GPU
fences on ttm_bo->base.resv?

In the GPU migration path, xe_migrate_copy() and xe_migrate_clear() chain
the migration job behind existing fences in ttm_bo->base.resv.
ttm_bo_move_memcpy(), however, performs the synchronous CPU copy in
ttm_move_memcpy() before ttm_bo_move_sync_cleanup() waits on the
reservation object.  When xe_bo_trigger_rebind() does not idle the BO
(for example on a non-fault-mode VM when !xe_device_is_l2_flush_optimized(xe),
or when move_lacks_source is true and new_mem carries pipelined eviction
fences), the CPU copy or clear races with active media GT jobs and ignores
ctx->no_wait_gpu unless ttm_bo_wait_ctx(ttm_bo, ctx) is called first.

> @@ -1576,17 +1589,18 @@ int xe_bo_notifier_unprepare_pinned(struct xe_bo *bo)
>  static int xe_bo_evict_pinned_copy(struct xe_bo *bo, struct xe_bo *backup)
>  {
>  	struct xe_device *xe = xe_bo_device(bo);
> +	struct xe_migrate *migrate;
>  	bool unmap = false;
>  	int ret = 0;
>  
> -	if (xe_bo_is_user(bo) || (bo->flags & XE_BO_FLAG_PINNED_LATE_RESTORE)) {
> -		struct xe_migrate *migrate;
> -		struct dma_fence *fence;
> +	if (bo->tile)
> +		migrate = bo->tile->migrate;
> +	else
> +		migrate = mem_type_to_migrate(xe, bo->ttm.resource->mem_type);
>  
> -		if (bo->tile)
> -			migrate = bo->tile->migrate;
> -		else
> -			migrate = mem_type_to_migrate(xe, bo->ttm.resource->mem_type);
> +	if ((xe_bo_is_user(bo) || (bo->flags & XE_BO_FLAG_PINNED_LATE_RESTORE)) &&
> +	    xe_migrate_can_blit(migrate)) {
> +		struct dma_fence *fence;

[Severity: Medium]
When xe_bo_evict_pinned_copy() falls back to the else branch for a
XE_BO_FLAG_PINNED_LATE_RESTORE or user BO, does the CPU copy race with
in-flight GPU work?

xe_bo_evict_all() runs before xe_gt_suspend().  The xe_migrate_copy()
branch orders the copy behind DMA_RESV_USAGE_BOOKKEEP fences in
bo->ttm.base.resv, but the CPU fallback calls xe_map_memcpy_from()
immediately without waiting on bo->ttm.base.resv, so it can snapshot
stale VRAM while media GT jobs or page-table updates are still running.

> @@ -1715,6 +1729,7 @@ int xe_bo_restore_pinned(struct xe_bo *bo)
>  	};
>  	struct xe_device *xe = ttm_to_xe_device(bo->ttm.bdev);
>  	struct xe_bo *backup = bo->backup_obj;
> +	struct xe_migrate *migrate;
>  	bool unmap = false;
>  	int ret;
>  
> @@ -1729,14 +1744,14 @@ int xe_bo_restore_pinned(struct xe_bo *bo)
>  			goto out_unlock_bo;
>  	}
>  
> -	if (xe_bo_is_user(bo) || (bo->flags & XE_BO_FLAG_PINNED_LATE_RESTORE)) {
> -		struct xe_migrate *migrate;
> -		struct dma_fence *fence;
> +	if (bo->tile)
> +		migrate = bo->tile->migrate;
> +	else
> +		migrate = mem_type_to_migrate(xe, bo->ttm.resource->mem_type);
>  
> -		if (bo->tile)
> -			migrate = bo->tile->migrate;
> -		else
> -			migrate = mem_type_to_migrate(xe, bo->ttm.resource->mem_type);
> +	if ((xe_bo_is_user(bo) || (bo->flags & XE_BO_FLAG_PINNED_LATE_RESTORE)) &&
> +	    xe_migrate_can_blit(migrate)) {
> +		struct dma_fence *fence;

[Severity: High]
Can the CPU fallback in xe_bo_evict_pinned_copy() and
xe_bo_restore_pinned() hit the drm_WARN_ON() in xe_bo_vmap() and fail
suspend/resume with -EINVAL?

When !xe_migrate_can_blit(migrate), both functions fall back to calling
xe_bo_vmap(bo) whenever iosys_map_is_null(&bo->vmap):

drivers/gpu/drm/xe/xe_bo.c:xe_bo_vmap() {
	if (drm_WARN_ON(&xe->drm, !(bo->flags & XE_BO_FLAG_NEEDS_CPU_ACCESS) ||
			!force_contiguous(bo->flags)))
		return -EINVAL;
}

User BOs pinned via xe_dma_buf_pin() -> xe_bo_pin_external() do not have
XE_BO_FLAG_PINNED set, so force_contiguous(bo->flags) is false (and they
may also be non-contiguous or lack XE_BO_FLAG_NEEDS_CPU_ACCESS).
Likewise, late-restore kernel BOs created without
XE_BO_FLAG_NEEDS_CPU_ACCESS, such as VF LMEM BOs in
pf_provision_vf_lmem(), will fail xe_bo_vmap(bo) during suspend and
resume.

> diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
> index 754592c5cf881..c06f43fc9e4fe 100644
> --- a/drivers/gpu/drm/xe/xe_migrate.c
> +++ b/drivers/gpu/drm/xe/xe_migrate.c
> @@ -2093,6 +2093,22 @@ void xe_migrate_wait(struct xe_migrate *m)
>  		dma_fence_wait(m->fence, false);
>  }
>  
> +/**
> + * xe_migrate_can_blit() - Whether a migration context can issue blits
> + * @m: The migration context
> + *
> + * xe_migrate_copy() and xe_migrate_clear() emit blitter commands, which only
> + * the copy engine understands, and the copy engine lives on the primary GT.
> + * A tile without one can still update page tables, so its migration context
> + * is usable, but callers that want to move data have to do it themselves.
> + *
> + * Return: true if blit based copy and clear are available on @m.
> + */
> +bool xe_migrate_can_blit(struct xe_migrate *m)
> +{
> +	return m->tile->primary_gt;
> +}

[Severity: Medium]
Should xe_ttm_access_memory() and xe_vram_memtest() also check
xe_migrate_can_blit() before issuing blitter commands?

In xe_ttm_access_memory(), accesses with len >= SZ_16K unconditionally
call xe_migrate_access_memory(), which emits blitter copy instructions via
xe_migrate_vram() instead of using the CPU BAR memcpy loop right below it.
In xe_vram_memtest() (when CONFIG_DRM_XE_DEBUG_MEM is enabled), the probe
path checks tile->migrate and calls xe_migrate_clear(), which emits
blitter commands to the media migration queue and fails device probe with
-EIO.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110231442.218721-13-stuart.summers@intel.com?part=9

  reply	other threads:[~2026-11-11  4:25 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10 23:14 [PATCH 00/11] Allow boot with only media GT Stuart Summers
2026-11-10 23:14 ` [PATCH 01/11] drm/xe: Detect each GMD_ID IP in a loop Stuart Summers
2026-11-10 23:14 ` [PATCH 02/11] drm/xe: Tolerate a missing graphics IP in xe_info_init() Stuart Summers
2026-11-10 23:14 ` [PATCH 03/11] drm/xe: Verify IP presence against GU_CNTL Stuart Summers
2026-11-10 23:14 ` [PATCH 04/11] drm/xe: Guard per-tile primary GT accesses Stuart Summers
2026-11-10 23:14 ` [PATCH 05/11] drm/xe: Stop assuming the root tile has a primary GT Stuart Summers
2026-11-11  2:59   ` sashiko-bot
2026-11-10 23:14 ` [PATCH 06/11] drm/xe: Do not claim flat CCS without a graphics IP Stuart Summers
2026-11-11  3:21   ` sashiko-bot
2026-11-10 23:14 ` [PATCH 07/11] drm/xe: Run page table updates on the media GT when there is no primary Stuart Summers
2026-11-11  3:53   ` sashiko-bot
2026-11-10 23:14 ` [PATCH 08/11] drm/xe: Document that gt_types_allowed=media now probes Stuart Summers
2026-11-10 23:14 ` [PATCH 09/11] drm/xe: Fall back to CPU copies when the tile cannot blit Stuart Summers
2026-11-11  4:25   ` sashiko-bot [this message]
2026-11-10 23:14 ` [PATCH 10/11] drm/xe: Require a fully visible VRAM BAR without a primary GT Stuart Summers
2026-11-11  4:38   ` sashiko-bot
2026-11-10 23:14 ` [PATCH 11/11] drm/xe: Allow probe " Stuart Summers
2026-11-11  4:57   ` sashiko-bot
2026-11-10 23:39 ` ✓ CI.KUnit: success for Allow boot with only media GT Patchwork
2026-11-11  0:14 ` ✓ Xe.CI.BAT: " Patchwork
2026-11-11  4:17 ` ✓ Xe.CI.FULL: " Patchwork
2026-11-12 12:21 ` [PATCH 00/11] " Piotr Piórkowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-208281@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=stuart.summers@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox