All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tejas Upadhyay <tejas.upadhyay@intel.com>
To: intel-xe@lists.freedesktop.org
Cc: himal.prasad.ghimiray@intel.com, rodrigo.vivi@intel.com,
	Tejas Upadhyay <tejas.upadhyay@intel.com>
Subject: [PATCH V19 09/15] drm/xe/vram: Add VRAM page offline fault handler
Date: Mon, 31 Aug 2026 12:19:51 +0530	[thread overview]
Message-ID: <20260831064942.315720-26-tejas.upadhyay@intel.com> (raw)
In-Reply-To: <20260831064942.315720-17-tejas.upadhyay@intel.com>

Add the core VRAM page offlining logic to handle HW-reported faulty
physical addresses:

- xe_ttm_vram_purge_page(): Purges the BO containing the faulty
  address. Bans the associated VM (if page table BO) and exec queue
  (if LRC BO). Moves xe_exec_queue_kill() outside xe_bo_lock() to
  avoid AB-BA deadlock with vm->lock. Uses READ_ONCE(bo->q) to
  safely access the exec queue pointer.

- xe_ttm_vram_page_already_processed(): Checks if an address is
  already tracked in offlined_pages or queued_pages lists to avoid
  double-processing.

- xe_ttm_vram_reserve_page_at_addr(): Two-phase reservation that
  first queues the page, purges the BO outside the lock, then
  reserves the buddy block. Handles both allocated (BO present)
  and free page cases. Returns -EIO for critical kernel BOs to
  trigger system reset.

- xe_ttm_vram_addr_to_region(): Maps a DPA to its corresponding
  VRAM region. Checks if the address falls within usable space,
  or infrastructure zones (CCS, GSM, DSM) where it returns NULL
  to flag a reset path. If the target address is outside any
  known region returns ERR_PTR(-EOPNOTSUPP)

- xe_ttm_vram_handle_addr_fault(): Entry point called by RAS.
  Returns -EEXIST if already processed, -EIO for GSM/critical BO,
  -EOPNOTSUPP if out of bounds.

v13(Himal):
- Remove redundant code
v12(Sashiko):
- Handle multi tile and add assert for 4K align
- Remove unaligned action comment for ENXIO
v11(Himal):
- match everywhere with enum vs bool for status member
- Fix comment and remove unused var
- if purge fail let next alloc confirm failure
- pass absolute address, useful for multi tile

Signed-off-by: Tejas Upadhyay <tejas.upadhyay@intel.com>
---
 drivers/gpu/drm/xe/xe_ttm_vram_mgr.c | 266 +++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_ttm_vram_mgr.h |   1 +
 2 files changed, 267 insertions(+)

diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
index 1253989a8d06..e5994c14487d 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
@@ -13,10 +13,16 @@
 #include <drm/ttm/ttm_placement.h>
 #include <drm/ttm/ttm_range_manager.h>
 
+#include "regs/xe_regs.h"
 #include "xe_bo.h"
+#include "xe_configfs.h"
 #include "xe_device.h"
+#include "xe_exec_queue.h"
+#include "xe_lrc.h"
+#include "xe_mmio.h"
 #include "xe_pm.h"
 #include "xe_res_cursor.h"
+#include "xe_ttm_stolen_mgr.h"
 #include "xe_ttm_vram_mgr.h"
 #include "xe_vram_types.h"
 
@@ -572,3 +578,263 @@ u64 xe_ttm_vram_get_avail(struct ttm_resource_manager *man)
 
 	return avail;
 }
+
+static int xe_ttm_vram_purge_page(struct xe_device *xe, struct xe_bo *bo)
+{
+	struct ttm_operation_ctx ctx = {};
+	struct xe_exec_queue *q_to_put = NULL;
+	struct xe_exec_queue *q = NULL;
+	struct xe_vm *vm = NULL;
+	u32	flags;
+	int ret = 0;
+
+	xe_bo_lock(bo, false);
+	if (bo->vm)
+		vm = xe_vm_get(bo->vm);
+	flags = bo->flags;
+	xe_bo_unlock(bo);
+	/*  Ban VM if BO is PPGTT */
+	if (vm && (flags & XE_BO_FLAG_PAGETABLE)) {
+		down_write(&vm->lock);
+		xe_vm_kill(vm, true);
+		up_write(&vm->lock);
+	}
+	if (vm)
+		xe_vm_put(vm);
+
+	xe_bo_lock(bo, false);
+	q = READ_ONCE(bo->q);
+	/*  Ban exec queue if BO is lrc */
+	if (q && xe_exec_queue_get_unless_zero(q)) {
+		/* ban queue */
+		q_to_put = q;
+	}
+
+	if (bo->purgeable.state == XE_MADV_PURGEABLE_PURGED) {
+		/* Already purged by shrinker during unlocked window — nothing to do */
+		xe_bo_unlock(bo);
+		goto out;
+	}
+
+	xe_bo_set_purgeable_state(bo, XE_MADV_PURGEABLE_DONTNEED);
+	ttm_bo_unmap_virtual(&bo->ttm);   /* nuke CPU mmap + VRAM IO mappings */
+	if (xe_bo_is_pinned(bo))
+		xe_bo_unpin(bo);
+	ret = xe_ttm_bo_purge(&bo->ttm, &ctx);
+	xe_bo_unlock(bo);
+
+out:
+	if (q_to_put) {
+		xe_exec_queue_kill(q_to_put);
+		xe_exec_queue_put(q_to_put);
+	}
+
+	return ret;
+}
+
+static bool xe_ttm_vram_page_already_processed(struct xe_ttm_vram_mgr *mgr,
+					       u64 addr)
+{
+	struct xe_ttm_vram_offline_resource *pos;
+
+	lockdep_assert_held(&mgr->lock);
+
+	list_for_each_entry(pos, &mgr->offlined_pages, offlined_link) {
+		if (pos->addr == addr)
+			return true;
+	}
+
+	list_for_each_entry(pos, &mgr->queued_pages, queued_link) {
+		if (pos->addr == addr)
+			return true;
+	}
+
+	return false;
+}
+
+static int xe_ttm_vram_reserve_page_at_addr(struct xe_device *xe, u64 addr,
+					    struct xe_ttm_vram_mgr *vram_mgr, struct gpu_buddy *mm)
+{
+	struct xe_ttm_vram_offline_resource *pos, *n;
+	struct xe_ttm_vram_offline_resource *nentry;
+	struct ttm_buffer_object *tbo = NULL;
+	struct xe_bo *pbo_to_put = NULL;
+	struct gpu_buddy_block *block;
+	u64 size = SZ_4K;
+	int ret = 0;
+
+	scoped_guard(mutex, &vram_mgr->lock) {
+		if (xe_ttm_vram_page_already_processed(vram_mgr, addr))
+			return -EEXIST;
+		block = gpu_buddy_allocated_addr_to_block(mm, addr);
+		if (WARN_ON(IS_ERR(block)))
+			return PTR_ERR(block);
+
+		nentry = kzalloc_obj(*nentry);
+		if (!nentry)
+			return -ENOMEM;
+		INIT_LIST_HEAD(&nentry->blocks);
+		nentry->status = XE_PAGE_RESERVE_PENDING;
+		nentry->addr = addr;
+
+		if (block) {
+			struct xe_bo *pbo;
+
+			if (!block->private) {
+				/* Race: another thread just reserved this block */
+				kfree(nentry);
+				return -EEXIST;
+			}
+			tbo = block->private;
+			pbo = ttm_to_xe_bo(tbo);
+
+			/* Get reference safely - BO may have zero refcount */
+			if (!xe_bo_get_unless_zero(pbo)) {
+				kfree(nentry);
+				return -ENOENT;
+			}
+			/*
+			 * Critical kernel BO? Best-effort check without resv lock;
+			 * worst case a concurrent pin causes reset path unnecessarily.
+			 */
+			if ((pbo->ttm.type == ttm_bo_type_kernel &&
+			     !(pbo->flags & XE_BO_FLAG_PINNED_LATE_RESTORE)) ||
+			    (xe_bo_is_user(pbo) && xe_bo_is_pinned(pbo))) {
+				kfree(nentry);
+				pbo_to_put = pbo;
+				drm_err(&xe->drm,
+					"%s: addr: 0x%llx is critical kernel bo, requesting SBR\n",
+					__func__, addr);
+				break;
+			}
+		}
+		/* Queue both free and occupied (to-be-purged) pages */
+		++vram_mgr->n_queued_pages;
+		list_add_rcu(&nentry->queued_link, &vram_mgr->queued_pages);
+	}
+
+	/* Deferred put outside lock to avoid recursive deadlock */
+	if (pbo_to_put) {
+		xe_bo_put(pbo_to_put);
+		/* Hint System controller driver for reset with -EIO  */
+		return -EIO;
+	}
+
+	if (block) {
+		struct xe_bo *pbo = ttm_to_xe_bo(tbo);
+
+		/*
+		 * Purge BO containing address - reference held from above.
+		 * Note: brief window between purge (freeing blocks) and re-reserve
+		 * below. If another allocation claims the block, buddy_alloc fails
+		 * and the status will be shown as failed reservation.
+		 */
+		ret = xe_ttm_vram_purge_page(xe, pbo);
+		xe_bo_put(pbo);
+		if (ret)
+			drm_warn(&xe->drm, "Purge failed at addr:0x%llx, ret:%d\n", addr, ret);
+	}
+
+	scoped_guard(mutex, &vram_mgr->lock) {
+		ret = xe_ttm_vram_buddy_alloc(vram_mgr, addr, addr + size,
+					      size, size, &nentry->blocks,
+					      GPU_BUDDY_RANGE_ALLOCATION,
+					      NULL, &nentry->used_visible_size);
+		if (ret) {
+			drm_warn(&xe->drm,
+				 "Could not reserve page at addr:0x%llx, ret:%d\n",
+				 addr, ret);
+			nentry->status = XE_PAGE_RESERVE_FAIL;
+			return ret;
+		}
+
+		list_for_each_entry_safe(pos, n, &vram_mgr->queued_pages, queued_link) {
+			if (pos->addr == nentry->addr) {
+				--vram_mgr->n_queued_pages;
+				list_del_rcu(&pos->queued_link);
+				break;
+			}
+		}
+		++vram_mgr->n_offlined_pages;
+		list_add_rcu(&nentry->offlined_link, &vram_mgr->offlined_pages);
+		/* RAS will send command to FW for offlining page based on ret value */
+	}
+	/* Success */
+	return ret;
+}
+
+static struct xe_vram_region *xe_ttm_vram_addr_to_region(struct xe_device *xe, u64 addr)
+{
+	struct xe_tile *tile;
+	u8 id;
+
+	for_each_tile(tile, xe, id) {
+		struct xe_vram_region *vr = tile->mem.vram;
+
+		if (!vr)
+			continue;
+
+		if (addr >= vr->dpa_base && addr < (vr->dpa_base + vr->usable_size))
+			return vr;
+
+		/* CCS, GSM, or DSM — infrastructure zone, needs reset */
+		if (addr >= (vr->dpa_base + vr->usable_size) &&
+		    addr < (vr->dpa_base + vr->actual_physical_size))
+			return NULL;
+	}
+
+	/*
+	 * Return an explicit error pointer so the caller knows the addr
+	 * is invalid and should be ignored, NOT SBR.
+	 */
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
+/**
+ * xe_ttm_vram_handle_addr_fault - Handle vram physical address error flaged
+ * @xe: pointer to parent device
+ * @addr: physical faulty address
+ *
+ * Handle the physcial faulty address error on specific tile.
+ *
+ * Returns 0 for success, negative error code otherwise as follow:
+ * * %-EIO - critical BO or address outside any VRAM region; next action is reset.
+ * * %-EOPNOTSUPP - log-only policy or unknown address; no further action.
+ * * %-ENOMEM - allocation failure; next action is reset.
+ * * %-ENXIO - address not found in buddy; no further action.
+ * * %-EEXIST - address already processed; no further action.
+ */
+int xe_ttm_vram_handle_addr_fault(struct xe_device *xe, u64 addr)
+{
+	struct xe_ttm_vram_mgr *vram_mgr;
+	struct xe_vram_region *vr;
+	struct gpu_buddy *mm;
+
+	/* Assert that the address is 4K aligned */
+	if (WARN_ON_ONCE(!IS_ALIGNED(addr, SZ_4K))) {
+		drm_err(&xe->drm, "Address %llx is not 4K aligned!\n", addr);
+		return -EINVAL;
+	}
+
+	vr = xe_ttm_vram_addr_to_region(xe, addr);
+	if (IS_ERR(vr)) {
+		/*
+		 * The addr is outside VRAM and GSM.
+		 * Log a debug message if needed, and safely exit/ignore.
+		 */
+		drm_dbg(&xe->drm, "Address %llx is out of bounds, ignoring fault.\n", addr);
+		return PTR_ERR(vr);
+	}
+	if (!vr) {
+		drm_err(&xe->drm, "%s:%d GSM addr:%llx error requesting SBR\n",
+			__func__, __LINE__, addr);
+		/* Hint System controller driver for reset with -EIO  */
+		return -EIO;
+	}
+	vram_mgr = &vr->ttm;
+	mm = &vram_mgr->mm;
+
+	/* Reserve page at address */
+	return xe_ttm_vram_reserve_page_at_addr(xe, addr - vr->dpa_base, vram_mgr, mm);
+}
+EXPORT_SYMBOL(xe_ttm_vram_handle_addr_fault);
diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h
index 87b7fae5edba..d5392beff30c 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.h
@@ -31,6 +31,7 @@ u64 xe_ttm_vram_get_cpu_visible_size(struct ttm_resource_manager *man);
 void xe_ttm_vram_get_used(struct ttm_resource_manager *man,
 			  u64 *used, u64 *used_visible);
 
+int xe_ttm_vram_handle_addr_fault(struct xe_device *xe, u64 addr);
 static inline struct xe_ttm_vram_mgr_resource *
 to_xe_ttm_vram_mgr_resource(struct ttm_resource *res)
 {
-- 
2.52.0


  parent reply	other threads:[~2026-08-31  6:51 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31  6:49 [PATCH V19 00/15] Add memory page offlining support Tejas Upadhyay
2026-08-31  6:49 ` [PATCH V19 01/15] drm/xe: Link VRAM object with gpu buddy Tejas Upadhyay
2026-08-31  6:49 ` [PATCH V19 02/15] drm/xe: Link LRC BO and its execution Queue Tejas Upadhyay
2026-08-31  7:24   ` sashiko-bot
2026-08-31  6:49 ` [PATCH V19 03/15] drm/xe: Export xe_ttm_bo_purge() Tejas Upadhyay
2026-09-02  0:33   ` Matthew Brost
2026-08-31  6:49 ` [PATCH V19 04/15] drm/xe: Handle NULL resource and allow purging of VRAM pages Tejas Upadhyay
2026-08-31  6:52   ` Ghimiray, Himal Prasad
2026-08-31  7:42   ` sashiko-bot
2026-08-31  6:49 ` [PATCH V19 05/15] drm/xe/bo: Make xe_bo_is_user() public Tejas Upadhyay
2026-08-31  6:49 ` [PATCH V19 06/15] drm/xe: Guard teardown paths against purged BOs Tejas Upadhyay
2026-08-31  6:53   ` Ghimiray, Himal Prasad
2026-08-31  8:03   ` sashiko-bot
2026-09-01  5:05     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 07/15] drm/xe/vram: Extract buddy alloc and free helpers Tejas Upadhyay
2026-08-31  8:08   ` sashiko-bot
2026-09-01  5:10     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 08/15] drm/xe/vram: Add page offline data structures and lifecycle Tejas Upadhyay
2026-08-31  6:49 ` Tejas Upadhyay [this message]
2026-08-31  6:57   ` [PATCH V19 09/15] drm/xe/vram: Add VRAM page offline fault handler Ghimiray, Himal Prasad
2026-08-31  8:37   ` sashiko-bot
2026-09-01  5:48     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 10/15] drm/xe/configfs: Add disable_vram_page_offline attribute Tejas Upadhyay
2026-08-31  8:45   ` sashiko-bot
2026-09-01  5:25     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 11/15] drm/xe/ras: Cache disable_vram_page_offline policy at init Tejas Upadhyay
2026-08-31  9:02   ` sashiko-bot
2026-09-01  5:22     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 12/15] drm/xe/vram: Check disable_vram_page_offline policy in fault handler Tejas Upadhyay
2026-08-31  6:49 ` [PATCH V19 13/15] drm/xe: Expose bad VRAM pages via debugfs Tejas Upadhyay
2026-08-31  6:55   ` Ghimiray, Himal Prasad
2026-08-31  9:18   ` sashiko-bot
2026-09-01  5:13     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 14/15] drm/xe/uapi: Expose ban reason in EXEC_QUEUE_GET_PROPERTY_BAN Tejas Upadhyay
2026-08-31  9:40   ` sashiko-bot
2026-09-01  9:40     ` Upadhyay, Tejas
2026-08-31  6:49 ` [PATCH V19 15/15] drm/xe: Add fault-inject based VRAM page offline injection Tejas Upadhyay
2026-08-31  6:54   ` Ghimiray, Himal Prasad
2026-08-31 11:53 ` ✓ CI.KUnit: success for Add memory page offlining support (rev22) Patchwork
2026-08-31 13:28 ` ✓ Xe.CI.BAT: " Patchwork
2026-08-31 13:58 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-08-31 14:04 ` [PATCH V19 00/15] Add memory page offlining support Rodrigo Vivi
2026-08-31 14:58   ` Matthew Brost
2026-09-01  4:09   ` Upadhyay, Tejas
2026-08-31 17:44 ` ✓ CI.KUnit: success for Add memory page offlining support (rev23) Patchwork
2026-08-31 18:32 ` ✓ Xe.CI.BAT: " Patchwork
2026-08-31 22:22 ` ✗ Xe.CI.FULL: failure " Patchwork

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=20260831064942.315720-26-tejas.upadhyay@intel.com \
    --to=tejas.upadhyay@intel.com \
    --cc=himal.prasad.ghimiray@intel.com \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=rodrigo.vivi@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.