From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 74F0FC88E72 for ; Thu, 17 Sep 2026 20:38:56 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 27C5410F0AC; Thu, 17 Sep 2026 20:38:56 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="Ospv2LTC"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.15]) by gabe.freedesktop.org (Postfix) with ESMTPS id 495AE10F0AC for ; Thu, 17 Sep 2026 20:38:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1789677535; x=1821213535; h=from:to:subject:date:message-id:mime-version: content-transfer-encoding; bh=tBpX05ADKW+eLpnil+e+Rvyg1Pd0GujYcX/SM0lhbqE=; b=Ospv2LTC8Zcvq0k03JfozeKJH4BefX1EMMRiIHWu2xcYT0+5d/8bNdT5 wWsoWbXxw05KZTH7MtIRud1/s88hXZo5d0jpYMvww8+atCmsyVKHrCO6+ gskKlWsXzh9HsKp6MM3iTfqKwlU3+iyVpqPR7ShQo4PQNEkzy2V99syIo 2HlzSH5L1ukEuB3x4HA53HAcsiVwFrapNeahX8h5aLsJ4gRXKGHdKIwww SkTrSlxTydO0enbpxYCcgG6HImjMgTWMPMfSkcC18qSIkdz7h/UMVfLJu vqTIxxj7Z+imH95n9sUEShmOWm1ROI3vrkOpWs24N9Vxcx12O8dqXiFsy A==; X-CSE-ConnectionGUID: CzzeZgohR0Oh2a4UnZAjiw== X-CSE-MsgGUID: yE0yaYHOQ4uoUGLZwI7HDQ== X-IronPort-AV: E=McAfee;i="6800,10657,11905"; a="90275392" X-IronPort-AV: E=Sophos;i="6.27,103,1787036400"; d="scan'208";a="90275392" Received: from fmviesa006.fm.intel.com ([10.60.135.146]) by fmvoesa109.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Sep 2026 13:38:55 -0700 X-CSE-ConnectionGUID: u9zCVsAFRoaec4IhBXRgdQ== X-CSE-MsgGUID: UBnCfPxJTvWV0k8HJYmsBA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.27,103,1787036400"; d="scan'208";a="269718502" Received: from gsse-cloud1.jf.intel.com ([10.54.39.91]) by fmviesa006-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Sep 2026 13:38:55 -0700 From: Matthew Brost To: intel-xe@lists.freedesktop.org Subject: [PATCH v3] drm/xe: Add mnemonic error reason to page fault diagnostics Date: Thu, 17 Sep 2026 13:38:50 -0700 Message-Id: <20260917203850.321385-1-matthew.brost@intel.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: intel-xe@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel Xe graphics driver List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-xe-bounces@lists.freedesktop.org Sender: "Intel-xe" xe_pagefault_print() was firing without indicating why servicing of a fault failed, making it hard to distinguish benign races (e.g. a VM's file descriptor closing mid-fault) from real bugs. Add enum xe_pagefault_error, uniquely identifying the high-level point at which xe_pagefault_service(), xe_pagefault_handle_vma(), and xe_svm_handle_pagefault() fail. Since consumer.page_addr is always page (4K) aligned, steal the reserved low byte to carry this code without growing struct xe_pagefault. Add xe_pagefault_set_error() / xe_pagefault_get_error() to encode/decode it, and xe_pagefault_addr() as the single accessor for the real (masked) faulted address; update all real address consumers (xe_pagefault_match(), cache alignment, xe_vm_add_fault_entry_pf()) to go through it instead of reading consumer.page_addr directly. xe_pagefault_set_start_addr() now preserves any already-recorded error bits rather than clobbering them. xe_pagefault_print() decodes the error via a new xe_pagefault_error_to_str() and reports it in an added "Error:" line. xe_pagefault_asid_to_vm() now distinguishes an ASID with no VM at all (XE_PAGEFAULT_ERROR_VM_NOT_FOUND, e.g. the owning file descriptor was already closed) from a VM found but not in fault mode (XE_PAGEFAULT_ERROR_VM_NOT_IN_FAULT_MODE). Signed-off-by: Matthew Brost Assisted-by: GitHub_Copilot:claude-sonnet-5 --- v2: - Add more XE_PAGEFAULT_ERROR_* types for SVM v3: - Reset to XE_PAGEFAULT_ERROR_NONE on retries (Sashiko) --- drivers/gpu/drm/xe/xe_pagefault.c | 102 +++++++++++++++++++----- drivers/gpu/drm/xe/xe_pagefault.h | 67 +++++++++++++++- drivers/gpu/drm/xe/xe_pagefault_types.h | 82 +++++++++++++++++++ drivers/gpu/drm/xe/xe_svm.c | 21 ++++- drivers/gpu/drm/xe/xe_vm.c | 3 +- 5 files changed, 250 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c index aeb56ff5d58e..c4aafdbdb518 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.c +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -158,8 +158,14 @@ static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma, lockdep_assert_held(&vm->lock); needs_vram = xe_vma_need_vram_for_atomic(vm->xe, vma, atomic); - if (needs_vram < 0 || (needs_vram && xe_vma_is_userptr(vma))) - return needs_vram < 0 ? needs_vram : -EACCES; + if (needs_vram < 0) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_NEEDS_VRAM_CHECK); + return needs_vram; + } + if (needs_vram && xe_vma_is_userptr(vma)) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_ATOMIC_USERPTR); + return -EACCES; + } xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_COUNT, 1); xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_KB, @@ -178,13 +184,17 @@ static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma, } do { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_NONE); + if (xe_vma_is_userptr(vma) && xe_vma_userptr_check_repin(to_userptr_vma(vma))) { struct xe_userptr_vma *uvma = to_userptr_vma(vma); err = xe_vma_userptr_pin_pages(uvma); - if (err) + if (err) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_USERPTR_PIN); return err; + } } /* Lock VM and BOs dma-resv */ @@ -195,8 +205,10 @@ static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma, needs_vram == 1); drm_exec_retry_on_contention(&exec); xe_validation_retry_on_oom(&ctx, &err); - if (err) + if (err) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_VALIDATE); break; + } /* Bind VMA only to the GT that has faulted */ trace_xe_vma_pf_bind(vma); @@ -206,6 +218,7 @@ static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma, if (IS_ERR(fence)) { err = PTR_ERR(fence); xe_validation_retry_on_oom(&ctx, &err); + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_REBIND); break; } } @@ -230,16 +243,22 @@ xe_pagefault_access_is_atomic(enum xe_pagefault_access_type access_type) return (access_type & XE_PAGEFAULT_ACCESS_TYPE_MASK) == XE_PAGEFAULT_ACCESS_TYPE_ATOMIC; } -static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid) +static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_pagefault *pf, u32 asid) { + struct xe_device *xe = gt_to_xe(pf->gt); struct xe_vm *vm; down_read(&xe->usm.lock); vm = xa_load(&xe->usm.asid_to_vm, asid); - if (vm && xe_vm_in_fault_mode(vm)) - xe_vm_get(vm); - else + if (!vm) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VM_NOT_FOUND); + vm = ERR_PTR(-EINVAL); + } else if (!xe_vm_in_fault_mode(vm)) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VM_NOT_IN_FAULT_MODE); vm = ERR_PTR(-EINVAL); + } else { + xe_vm_get(vm); + } up_read(&xe->usm.lock); return vm; @@ -248,7 +267,6 @@ static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid) static int xe_pagefault_service(struct xe_pagefault *pf) { struct xe_gt *gt = pf->gt; - struct xe_device *xe = gt_to_xe(gt); struct xe_vm *vm; struct xe_vma *vma = NULL; int err; @@ -259,7 +277,7 @@ static int xe_pagefault_service(struct xe_pagefault *pf) if (pf->consumer.fault_type_level == XE_PAGEFAULT_TYPE_LEVEL_NACK) return -EFAULT; - vm = xe_pagefault_asid_to_vm(xe, asid); + vm = xe_pagefault_asid_to_vm(pf, asid); if (IS_ERR(vm)) return PTR_ERR(vm); @@ -267,18 +285,21 @@ static int xe_pagefault_service(struct xe_pagefault *pf) if (xe_vm_is_closed(vm)) { err = -ENOENT; + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VM_CLOSED); goto unlock_vm; } - vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr); + vma = xe_vm_find_vma_by_addr(vm, xe_pagefault_addr(pf)); if (!vma) { err = -EINVAL; + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_VMA_NOT_FOUND); goto unlock_vm; } if (xe_vma_read_only(vma) && pf->consumer.access_type != XE_PAGEFAULT_ACCESS_TYPE_READ) { err = -EPERM; + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_READ_ONLY_VIOLATION); goto unlock_vm; } @@ -286,7 +307,7 @@ static int xe_pagefault_service(struct xe_pagefault *pf) if (xe_vma_is_cpu_addr_mirror(vma)) err = xe_svm_handle_pagefault(vm, vma, pf, gt, - pf->consumer.page_addr, atomic); + xe_pagefault_addr(pf), atomic); else err = xe_pagefault_handle_vma(gt, vma, pf, atomic); @@ -375,7 +396,7 @@ static bool xe_pagefault_match(struct xe_pagefault *pf, u64 start, u64 end, u64 cache_asid) { struct xe_device *xe = gt_to_xe(pf->gt); - u64 page_addr = pf->consumer.page_addr; + u64 page_addr = xe_pagefault_addr(pf); u32 pf_asid = FIELD_GET(XE_PAGEFAULT_ASID_MASK, pf->consumer.id); xe_assert(xe, pf->consumer.alloc_state != @@ -499,7 +520,7 @@ static bool xe_pagefault_queue_pop(struct xe_pagefault_queue *pf_queue, if (FIELD_GET(XE_PAGEFAULT_REQUEUE_MASK, lpf->consumer.fault_type_level)) align = SZ_4K; - pf_work->cache.start = ALIGN_DOWN(lpf->consumer.page_addr, align); + pf_work->cache.start = ALIGN_DOWN(xe_pagefault_addr(lpf), align); pf_work->cache.end = pf_work->cache.start + align; pf_work->cache.asid = FIELD_GET(XE_PAGEFAULT_ASID_MASK, lpf->consumer.id); pf_work->cache.pf = lpf; @@ -542,10 +563,53 @@ static bool xe_pagefault_queue_pop(struct xe_pagefault_queue *pf_queue, return true; } +static const char *xe_pagefault_error_to_str(enum xe_pagefault_error error) +{ + switch (error) { + case XE_PAGEFAULT_ERROR_NONE: + return "NONE"; + case XE_PAGEFAULT_ERROR_VM_NOT_FOUND: + return "VM_NOT_FOUND"; + case XE_PAGEFAULT_ERROR_VM_NOT_IN_FAULT_MODE: + return "VM_NOT_IN_FAULT_MODE"; + case XE_PAGEFAULT_ERROR_VM_CLOSED: + return "VM_CLOSED"; + case XE_PAGEFAULT_ERROR_VMA_NOT_FOUND: + return "VMA_NOT_FOUND"; + case XE_PAGEFAULT_ERROR_READ_ONLY_VIOLATION: + return "READ_ONLY_VIOLATION"; + case XE_PAGEFAULT_ERROR_VMA_NEEDS_VRAM_CHECK: + return "VMA_NEEDS_VRAM_CHECK"; + case XE_PAGEFAULT_ERROR_VMA_ATOMIC_USERPTR: + return "VMA_ATOMIC_USERPTR"; + case XE_PAGEFAULT_ERROR_VMA_USERPTR_PIN: + return "VMA_USERPTR_PIN"; + case XE_PAGEFAULT_ERROR_VMA_VALIDATE: + return "VMA_VALIDATE"; + case XE_PAGEFAULT_ERROR_VMA_REBIND: + return "VMA_REBIND"; + case XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR: + return "SVM_GARBAGE_COLLECTOR"; + case XE_PAGEFAULT_ERROR_SVM_RANGE_NOT_FOUND: + return "SVM_RANGE_NOT_FOUND"; + case XE_PAGEFAULT_ERROR_SVM_REBIND: + return "SVM_REBIND"; + case XE_PAGEFAULT_ERROR_SVM_NEEDS_VRAM_CHECK: + return "SVM_NEEDS_VRAM_CHECK"; + case XE_PAGEFAULT_ERROR_SVM_VMA_NOT_FOUND: + return "SVM_VMA_NOT_FOUND"; + case XE_PAGEFAULT_ERROR_SVM_SERVICE_FAILED: + return "SVM_SERVICE_FAILED"; + default: + return "UNKNOWN"; + } +} + static void xe_pagefault_print(struct xe_pagefault *pf) { u8 engine_class = FIELD_GET(XE_PAGEFAULT_ENGINE_CLASS_MASK, pf->consumer.engine_class_instance); + u64 addr = xe_pagefault_addr(pf); xe_gt_info(pf->gt, "\n\tASID: %lu\n" "\tFaulted Address: 0x%08x%08x\n" @@ -554,11 +618,12 @@ static void xe_pagefault_print(struct xe_pagefault *pf) "\tFaultLevel: %lu\n" "\tEngineClass: %d %s\n" "\tEngineInstance: %lu\n" - "\tSRCID: 0x%02lx\n", + "\tSRCID: 0x%02lx\n" + "\tError: %s\n", FIELD_GET(XE_PAGEFAULT_ASID_MASK, pf->consumer.id), - upper_32_bits(pf->consumer.page_addr), - lower_32_bits(pf->consumer.page_addr), + upper_32_bits(addr), + lower_32_bits(addr), FIELD_GET(XE_PAGEFAULT_TYPE_MASK, pf->consumer.fault_type_level), FIELD_GET(XE_PAGEFAULT_ACCESS_TYPE_MASK, @@ -570,7 +635,8 @@ static void xe_pagefault_print(struct xe_pagefault *pf) FIELD_GET(XE_PAGEFAULT_ENGINE_INSTANCE_MASK, pf->consumer.engine_class_instance), FIELD_GET(XE_PAGEFAULT_SRCID_MASK, - pf->consumer.id)); + pf->consumer.id), + xe_pagefault_error_to_str(xe_pagefault_get_error(pf))); } static void xe_pagefault_save_to_vm(struct xe_device *xe, struct xe_pagefault *pf) diff --git a/drivers/gpu/drm/xe/xe_pagefault.h b/drivers/gpu/drm/xe/xe_pagefault.h index e9c5d1f03760..799c984dee84 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.h +++ b/drivers/gpu/drm/xe/xe_pagefault.h @@ -6,6 +6,8 @@ #ifndef _XE_PAGEFAULT_H_ #define _XE_PAGEFAULT_H_ +#include + #include "xe_pagefault_types.h" struct drm_printer; @@ -21,6 +23,61 @@ int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf); void xe_pagefault_print_info(struct xe_device *xe, struct drm_printer *p); +/* + * consumer.page_addr is always page (4K) aligned, so the low bits are + * reserved and unused by the real address. Steal a byte of those bits to + * record an &enum xe_pagefault_error describing the high-level point at + * which servicing of the fault failed, so it can be reported by + * xe_pagefault_print(). All real address consumers of page_addr must go + * through xe_pagefault_addr() to mask off these reserved bits. + */ +#define XE_PAGEFAULT_ERROR_MASK GENMASK_ULL(7, 0) + +/** + * xe_pagefault_set_error() - record the failure reason for a pagefault + * @pf: Pagefault entry + * @error: Failure reason + * + * Encodes @error into the reserved low bits of consumer.page_addr. Should be + * called at the high-level point a pagefault fails to service so + * xe_pagefault_print() can later report a mnemonic failure reason. + */ +static inline void +xe_pagefault_set_error(struct xe_pagefault *pf, enum xe_pagefault_error error) +{ + pf->consumer.page_addr &= ~XE_PAGEFAULT_ERROR_MASK; + pf->consumer.page_addr |= FIELD_PREP(XE_PAGEFAULT_ERROR_MASK, error); +} + +/** + * xe_pagefault_get_error() - read the failure reason for a pagefault + * @pf: Pagefault entry + * + * Return: The &enum xe_pagefault_error previously recorded via + * xe_pagefault_set_error(), or %XE_PAGEFAULT_ERROR_NONE if none was recorded. + */ +static inline enum xe_pagefault_error +xe_pagefault_get_error(struct xe_pagefault *pf) +{ + return FIELD_GET(XE_PAGEFAULT_ERROR_MASK, pf->consumer.page_addr); +} + +/** + * xe_pagefault_addr() - read the real faulted address for a pagefault + * @pf: Pagefault entry + * + * consumer.page_addr may have failure reason bits encoded into its reserved + * low bits by xe_pagefault_set_error(). This masks those bits off, returning + * the real page address. All accesses to the faulted address must go through + * this helper rather than reading consumer.page_addr directly. + * + * Return: The real (page aligned) faulted address. + */ +static inline u64 xe_pagefault_addr(struct xe_pagefault *pf) +{ + return pf->consumer.page_addr & ~XE_PAGEFAULT_ERROR_MASK; +} + #define XE_PAGEFAULT_END_ADDR_MASK (~0xfffull) /** @@ -69,11 +126,17 @@ static inline u64 xe_pagefault_end_addr(struct xe_pagefault *pf) * The pagefault consumer stores the resolved fault range so subsequent faults * hitting the same range can be immediately acknowledged without re-running * the full fault handling path. + * + * The start address shares storage with the failure reason recorded by + * xe_pagefault_set_error() and therefore must be masked with + * %XE_PAGEFAULT_ERROR_MASK before storing so any previously recorded error is + * preserved. */ static inline void xe_pagefault_set_start_addr(struct xe_pagefault *pf, u64 start_addr) { - pf->consumer.page_addr = start_addr; + pf->consumer.page_addr &= XE_PAGEFAULT_ERROR_MASK; + pf->consumer.page_addr |= (start_addr & ~XE_PAGEFAULT_ERROR_MASK); } /** @@ -87,7 +150,7 @@ xe_pagefault_set_start_addr(struct xe_pagefault *pf, u64 start_addr) */ static inline u64 xe_pagefault_start_addr(struct xe_pagefault *pf) { - return pf->consumer.page_addr; + return xe_pagefault_addr(pf); } #endif diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h index 907189b73286..8ae6b9848bdc 100644 --- a/drivers/gpu/drm/xe/xe_pagefault_types.h +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h @@ -32,6 +32,88 @@ enum xe_pagefault_type { XE_PAGEFAULT_TYPE_ATOMIC_ACCESS_VIOLATION = 2, }; +/** + * enum xe_pagefault_error - Xe page fault servicing error + * + * Uniquely identifies the high-level point at which servicing of a page + * fault failed. Encoded into the reserved low bits of + * &xe_pagefault.consumer.page_addr (which is always page aligned) so the + * failure reason can be threaded back up to xe_pagefault_print() without + * growing the size of struct xe_pagefault. See xe_pagefault_set_error() and + * xe_pagefault_error_to_str(). + */ +enum xe_pagefault_error { + /** @XE_PAGEFAULT_ERROR_NONE: No error recorded */ + XE_PAGEFAULT_ERROR_NONE = 0, + /** + * @XE_PAGEFAULT_ERROR_VM_NOT_FOUND: VM lookup by ASID failed, e.g. + * the VM's file descriptor was already closed and the ASID has been + * torn down + */ + XE_PAGEFAULT_ERROR_VM_NOT_FOUND, + /** + * @XE_PAGEFAULT_ERROR_VM_NOT_IN_FAULT_MODE: VM found by ASID lookup + * but is not in fault mode + */ + XE_PAGEFAULT_ERROR_VM_NOT_IN_FAULT_MODE, + /** @XE_PAGEFAULT_ERROR_VM_CLOSED: VM found but already closed */ + XE_PAGEFAULT_ERROR_VM_CLOSED, + /** @XE_PAGEFAULT_ERROR_VMA_NOT_FOUND: No VMA covers the faulted address */ + XE_PAGEFAULT_ERROR_VMA_NOT_FOUND, + /** + * @XE_PAGEFAULT_ERROR_READ_ONLY_VIOLATION: Write/atomic fault on a + * read-only VMA + */ + XE_PAGEFAULT_ERROR_READ_ONLY_VIOLATION, + /** + * @XE_PAGEFAULT_ERROR_VMA_NEEDS_VRAM_CHECK: Failed determining if VMA + * requires VRAM for an atomic access + */ + XE_PAGEFAULT_ERROR_VMA_NEEDS_VRAM_CHECK, + /** + * @XE_PAGEFAULT_ERROR_VMA_ATOMIC_USERPTR: Atomic access requires VRAM + * but VMA is a userptr, which is unsupported + */ + XE_PAGEFAULT_ERROR_VMA_ATOMIC_USERPTR, + /** @XE_PAGEFAULT_ERROR_VMA_USERPTR_PIN: Userptr page pin/repin failed */ + XE_PAGEFAULT_ERROR_VMA_USERPTR_PIN, + /** + * @XE_PAGEFAULT_ERROR_VMA_VALIDATE: Failed to lock/validate VMA's BO + * or migrate it to VRAM + */ + XE_PAGEFAULT_ERROR_VMA_VALIDATE, + /** @XE_PAGEFAULT_ERROR_VMA_REBIND: Failed to rebind VMA into page tables */ + XE_PAGEFAULT_ERROR_VMA_REBIND, + /** + * @XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR: Failed processing + * pending SVM garbage collection (unmaps) prior to servicing the + * fault + */ + XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR, + /** + * @XE_PAGEFAULT_ERROR_SVM_RANGE_NOT_FOUND: Failed to find or insert + * an SVM range covering the faulted address + */ + XE_PAGEFAULT_ERROR_SVM_RANGE_NOT_FOUND, + /** @XE_PAGEFAULT_ERROR_SVM_REBIND: Failed to rebind an SVM range into page tables */ + XE_PAGEFAULT_ERROR_SVM_REBIND, + /** + * @XE_PAGEFAULT_ERROR_SVM_NEEDS_VRAM_CHECK: Failed determining if SVM + * VMA requires VRAM for an atomic access + */ + XE_PAGEFAULT_ERROR_SVM_NEEDS_VRAM_CHECK, + /** + * @XE_PAGEFAULT_ERROR_SVM_VMA_NOT_FOUND: SVM VMA re-lookup after a + * range split failed to find a covering VMA + */ + XE_PAGEFAULT_ERROR_SVM_VMA_NOT_FOUND, + /** + * @XE_PAGEFAULT_ERROR_SVM_SERVICE_FAILED: SVM range population, + * migration, or bind failed + */ + XE_PAGEFAULT_ERROR_SVM_SERVICE_FAILED, +}; + /** struct xe_pagefault_ops - Xe pagefault ops (producer) */ struct xe_pagefault_ops { /** diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index 6c3033fc4db7..f39e647512ad 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -1304,16 +1304,20 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, /* Always process UNMAPs first so view SVM ranges is current */ err = xe_svm_garbage_collector(vm); - if (err) + if (err) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_SVM_GARBAGE_COLLECTOR); return err; + } dpagemap = ctx.devmem_only ? xe_tile_local_pagemap(tile) : xe_vma_resolve_pagemap(vma, tile); ctx.device_private_page_owner = xe_svm_private_page_owner(vm, !dpagemap); range = xe_svm_range_find_or_insert(vm, fault_addr, vma, &ctx); - if (IS_ERR(range)) + if (IS_ERR(range)) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_SVM_RANGE_NOT_FOUND); return PTR_ERR(range); + } xe_svm_range_fault_count_stats_incr(gt, range); @@ -1415,6 +1419,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, err = PTR_ERR(fence); xe_validation_retry_on_oom(&vctx, &err); xe_svm_range_bind_us_stats_incr(gt, range, bind_start); + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_SVM_REBIND); break; } } @@ -1437,6 +1442,7 @@ static int __xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, err_out: if (err == -EAGAIN) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_NONE); ctx.timeslice_ms <<= 1; /* Double timeslice if we have to retry */ range_debug(range, "PAGE FAULT - RETRY BIND"); goto retry; @@ -1469,8 +1475,10 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, int need_vram, ret; retry: need_vram = xe_vma_need_vram_for_atomic(vm->xe, vma, atomic); - if (need_vram < 0) + if (need_vram < 0) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_SVM_NEEDS_VRAM_CHECK); return need_vram; + } ret = __xe_svm_handle_pagefault(vm, vma, pf, gt, fault_addr, need_vram ? true : false); @@ -1480,11 +1488,16 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma, * may have been split by xe_svm_range_set_default_attr. */ vma = xe_vm_find_vma_by_addr(vm, fault_addr); - if (!vma) + if (!vma) { + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_SVM_VMA_NOT_FOUND); return -EINVAL; + } + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_NONE); goto retry; } + if (ret && xe_pagefault_get_error(pf) == XE_PAGEFAULT_ERROR_NONE) + xe_pagefault_set_error(pf, XE_PAGEFAULT_ERROR_SVM_SERVICE_FAILED); return ret; } diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index efa5ff6cc823..17dc4debe7c1 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -29,6 +29,7 @@ #include "xe_exec_queue.h" #include "xe_gt.h" #include "xe_migrate.h" +#include "xe_pagefault.h" #include "xe_pat.h" #include "xe_pm.h" #include "xe_preempt_fence.h" @@ -643,7 +644,7 @@ void xe_vm_add_fault_entry_pf(struct xe_vm *vm, struct xe_pagefault *pf) return; } - e->address = pf->consumer.page_addr; + e->address = xe_pagefault_addr(pf); /* * TODO: * Address precision is currently always SZ_4K, but this may change -- 2.34.1