Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7
@ 2026-02-23 14:02 Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 01/22] drm/xe/eudebug: Introduce eudebug interface Mika Kuoppala
                   ` (26 more replies)
  0 siblings, 27 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:02 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

Hi,

This is the v7 patch series for Intel Xe GPU debug support (eudebug).

This series continues from the following previous submissions:
- v1: https://lists.freedesktop.org/archives/intel-xe/2024-July/043605.html
- v2: https://lists.freedesktop.org/archives/intel-xe/2024-October/052260.html
- v3: https://lists.freedesktop.org/archives/intel-xe/2024-December/061476.html
- v4: https://lists.freedesktop.org/archives/intel-xe/2025-August/091645.html
- v5: https://lists.freedesktop.org/archives/intel-xe/2025-October/097859.html
- v6: https://lists.freedesktop.org/archives/intel-xe/2025-December/106405.html

# Major Changes from v6

Added documentation. Two new documentation patches introduce .rst
documentation under Documentation/gpu/xe/ and kernel-doc for the
eudebug interface. The eudebug source now includes DOC sections
covering connection establishment, security model, file descriptor
acquisition methods, and the event interface.

UAPI header documentation in xe_drm_eudebug.h has been significantly
expanded with kernel-doc comments for interface structures and ioctls.

Eliminated the separate xe_eudebug_resources struct, removing a level
of indirection. The resources lock has been consolidated with the
target lock, which is now a mutex instead of a spinlock.

Improved input validation: seqno fields are now checked to be zero on
EU control and read_event entry. The read_event ioctl validation was
simplified to only check for DRM_XE_EUDEBUG_EVENT_READ type.

Replaced drm_file->authenticated check with xe_file and device validation,
allowing render node access for debug targets.

# Major Changes from v5

With v5, when relaying vm bind with the associated ops, the transient
bind relay state was held in struct xe_vm. As debug metadata add
and remove ops simplifies the way binds are handled wrt v3,
this gave us opportunity to use vm_ops for relaying instead of
using eudebug baked bind ops state. This change removes ~200 lines.

Debuggable GuC context support was reworked. Now GuC version
is required to be 70.49.4 or higher for eudebug support.

Eudebug pagefaults were reworked on top of producer/consumer
pagefaults, introduced in core xe side. We reduced the footprint
eudebug pagefaults have in xe pagefault handler side.

# Major Changes from v4

v4 omitted page fault support, it is reworked from v3 and included
in this series.

### Major Changes from v3

#### 1. Elimination of ptrace_may_access() and pid

In previous series, the connection attempt was made using the process ID
(PID) as the target. Access was checked using the `ptrace_may_access()`
helper to achieve security parity with CPU-side debugging.

In v4, this has been changed to connect to a DRM client, using a file
descriptor as the target. This approach eliminates the need for the
`ptrace_may_access()` symbol export, as access control is now managed
through the debugger process's access to the file descriptor. For example,
accessing a remote DRM client requires the debugger process to
successfully call `pidfd_getfd()` to obtain a duplicate of the target
file descriptor. The 1:1 mapping between DRM clients and their debuggers
eliminates the need for `EVENT_OPEN` and simplifies overall connection
tracking.

#### 2. ELF binaries not held in kernel memory

In v4, debug data is delivered as a VM bind 'OP_ADD_DEBUG_DATA' extension.
The ELF binaries are no longer stored within the Xe KMD but are instead
kept in a file. The file path is passed as part of an extension in
the newly introduced 'OP_ADD_DEBUG_DATA' VM bind operation. Alternatively
pseudo-paths can be used to annotate special address ranges similar to
/proc/<pid>/maps.

#### 3. Debug metadata not carried in VMA struct

Instead of attaching debug data to vma created by 'OP_MAP',
we introduce separate ops for managing the metadata.
Debug data is no longer held in the VMA struct. xe_vm contains a
list of all associated debug data.

#### 4. Reading debug data via debugfs

This revision introduces the possibility to access debug data using per
client debugfs entries. The intent was to achieve similar interface to
'/proc/<pid>/maps'

### Supported Hardware
- Lunarlake (LNL)
- Battlemage (BMG)
- Pantherlake (PTL)

The code for this submission can be found at:
https://gitlab.freedesktop.org/miku/kernel/-/tree/eudebug-v7

Tests:
https://gitlab.freedesktop.org/cmanszew/igt-gpu-tools/-/tree/eudebug-dev-next


Christoph Manszewski (5):
  drm/xe: Introduce ADD_DEBUG_DATA and REMOVE_DEBUG_DATA vm bind ops
  drm/xe/eudebug: Introduce vm bind and vm bind debug data events
  drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test
  drm/xe: Implement SR-IOV and eudebug exclusivity
  drm/xe: Add xe_client_debugfs and introduce debug_data file

Dominik Grzegorzek (5):
  drm/xe/eudebug: Introduce exec_queue events
  drm/xe: Add EUDEBUG_ENABLE exec queue property
  drm/xe/eudebug: hw enablement for eudebug
  drm/xe/eudebug: Introduce EU control interface
  drm/xe/eudebug: Introduce per device attention scan worker

Gwan-gyeong Mun (4):
  drm/xe/eudebug: Add read/count/compare helper for eu attention
  drm/xe/vm: Support for adding null page VMA to VM on request
  drm/xe/eudebug: Introduce EU pagefault handling interface
  drm/xe/eudebug: Enable EU pagefault handling

Mika Kuoppala (8):
  drm/xe/eudebug: Introduce eudebug interface
  drm/xe/eudebug: Add documentation
  drm/xe/eudebug: Add connection establishment documentation
  drm/xe/eudebug: Introduce discovery for resources
  drm/xe/eudebug: Mark guc contexts as debuggable
  drm/xe/eudebug: Add UFENCE events with acks
  drm/xe/eudebug: vm open/pread/pwrite
  drm/xe/eudebug: userptr vm pread/pwrite

 Documentation/gpu/xe/index.rst              |    1 +
 Documentation/gpu/xe/xe_eudebug.rst         |   83 +
 drivers/gpu/drm/xe/Kconfig                  |   10 +
 drivers/gpu/drm/xe/Makefile                 |    7 +-
 drivers/gpu/drm/xe/abi/guc_actions_abi.h    |    5 +
 drivers/gpu/drm/xe/abi/guc_klvs_abi.h       |    1 +
 drivers/gpu/drm/xe/regs/xe_engine_regs.h    |    5 +
 drivers/gpu/drm/xe/regs/xe_gt_regs.h        |   43 +
 drivers/gpu/drm/xe/tests/xe_eudebug.c       |  193 ++
 drivers/gpu/drm/xe/tests/xe_live_test_mod.c |    5 +
 drivers/gpu/drm/xe/xe_client_debugfs.c      |  118 +
 drivers/gpu/drm/xe/xe_client_debugfs.h      |   19 +
 drivers/gpu/drm/xe/xe_debug_data.c          |  314 +++
 drivers/gpu/drm/xe/xe_debug_data.h          |   22 +
 drivers/gpu/drm/xe/xe_debug_data_types.h    |   25 +
 drivers/gpu/drm/xe/xe_device.c              |   30 +-
 drivers/gpu/drm/xe/xe_device.h              |   42 +
 drivers/gpu/drm/xe/xe_device_types.h        |   41 +
 drivers/gpu/drm/xe/xe_eudebug.c             | 2292 +++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug.h             |  111 +
 drivers/gpu/drm/xe/xe_eudebug_hw.c          |  746 ++++++
 drivers/gpu/drm/xe/xe_eudebug_hw.h          |   32 +
 drivers/gpu/drm/xe/xe_eudebug_pagefault.c   |  440 ++++
 drivers/gpu/drm/xe/xe_eudebug_pagefault.h   |   47 +
 drivers/gpu/drm/xe/xe_eudebug_types.h       |  236 ++
 drivers/gpu/drm/xe/xe_eudebug_vm.c          |  434 ++++
 drivers/gpu/drm/xe/xe_eudebug_vm.h          |    8 +
 drivers/gpu/drm/xe/xe_exec_queue.c          |   56 +-
 drivers/gpu/drm/xe/xe_exec_queue.h          |    2 +
 drivers/gpu/drm/xe/xe_exec_queue_types.h    |    7 +
 drivers/gpu/drm/xe/xe_gt_debug.c            |  244 ++
 drivers/gpu/drm/xe/xe_gt_debug.h            |   39 +
 drivers/gpu/drm/xe/xe_gt_debug_types.h      |   23 +
 drivers/gpu/drm/xe/xe_guc.c                 |   17 +
 drivers/gpu/drm/xe/xe_guc.h                 |    3 +
 drivers/gpu/drm/xe/xe_guc_ads.c             |   17 +
 drivers/gpu/drm/xe/xe_guc_pagefault.c       |    8 +
 drivers/gpu/drm/xe/xe_guc_submit.c          |   34 +
 drivers/gpu/drm/xe/xe_guc_submit.h          |    1 +
 drivers/gpu/drm/xe/xe_hw_engine.h           |   14 +
 drivers/gpu/drm/xe/xe_lrc.c                 |   10 +
 drivers/gpu/drm/xe/xe_pagefault.c           |   31 +-
 drivers/gpu/drm/xe/xe_pagefault_types.h     |   13 +
 drivers/gpu/drm/xe/xe_reg_sr.c              |   21 +-
 drivers/gpu/drm/xe/xe_reg_sr.h              |    4 +-
 drivers/gpu/drm/xe/xe_reg_whitelist.c       |    2 +-
 drivers/gpu/drm/xe/xe_rtp.c                 |    2 +-
 drivers/gpu/drm/xe/xe_sync.c                |   43 +-
 drivers/gpu/drm/xe/xe_sync.h                |    7 +-
 drivers/gpu/drm/xe/xe_sync_types.h          |   28 +-
 drivers/gpu/drm/xe/xe_userptr.c             |    4 +
 drivers/gpu/drm/xe/xe_userptr.h             |   32 +
 drivers/gpu/drm/xe/xe_vm.c                  |  201 +-
 drivers/gpu/drm/xe/xe_vm.h                  |    3 +
 drivers/gpu/drm/xe/xe_vm_types.h            |   19 +
 drivers/gpu/drm/xe/xe_wa_oob.rules          |    5 +-
 include/uapi/drm/xe_drm.h                   |   95 +
 include/uapi/drm/xe_drm_eudebug.h           |  377 +++
 58 files changed, 6625 insertions(+), 47 deletions(-)
 create mode 100644 Documentation/gpu/xe/xe_eudebug.rst
 create mode 100644 drivers/gpu/drm/xe/tests/xe_eudebug.c
 create mode 100644 drivers/gpu/drm/xe/xe_client_debugfs.c
 create mode 100644 drivers/gpu/drm/xe/xe_client_debugfs.h
 create mode 100644 drivers/gpu/drm/xe/xe_debug_data.c
 create mode 100644 drivers/gpu/drm/xe/xe_debug_data.h
 create mode 100644 drivers/gpu/drm/xe/xe_debug_data_types.h
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug.h
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_hw.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_hw.h
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.h
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_types.h
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_vm.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_vm.h
 create mode 100644 drivers/gpu/drm/xe/xe_gt_debug.c
 create mode 100644 drivers/gpu/drm/xe/xe_gt_debug.h
 create mode 100644 drivers/gpu/drm/xe/xe_gt_debug_types.h
 create mode 100644 include/uapi/drm/xe_drm_eudebug.h

-- 
2.43.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 01/22] drm/xe/eudebug: Introduce eudebug interface
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
@ 2026-02-23 14:02 ` Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 02/22] drm/xe/eudebug: Add documentation Mika Kuoppala
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:02 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala, Maarten Lankhorst, Lucas De Marchi,
	Dominik Grzegorzek, Andi Shyti, Matt Roper,
	Zbigniew Kempczyński, Jonathan Cavitt

This patch adds the eudebug interface to the Xe driver, enabling
user-space debuggers (e.g., GDB) to track and interact with GPU resources
of a DRM client. Debuggers can inspect or modify these resources,
for example, to locate ISA/ELF sections and install breakpoints in a
shader's instruction stream.

A debugger opens a connection to the Xe driver via a DRM ioctl, specifying
the target DRM client's file descriptor. This returns an anonymous file
descriptor for the connection, which can be used to listen for resource
creation/destruction events. The same file descriptor can also be used to
receive hardware state change events and control execution flow by
interrupting EU threads on the GPU (in follow-up patches).

This patch introduces the eudebug connection and event queuing,
adding client create/destroy and VM create/destroy events as a baseline.
Additional events and hardware control for full debugger operation are
needed and will be introduced in follow-up patches.

The resource tracking components are inspired by Maciej Patelczyk's work on
resource handling for i915. Chris Wilson suggested a two-way mapping
approach, which simplifies using the resource map as definitive
bookkeeping forresources relayed to the debugger during the discovery
phase (in a follow-up patch).

v2: - Kconfig support (Matthew)
    - ptraced access control (Lucas)
    - pass expected event length to user (Zbigniew)
    - only track long running VMs
    - checkpatch (Tilak)
    - include order (Andrzej)
    - 32bit fixes (Andrzej)
    - cleaner get_task_struct
    - remove xa_array and use clients.list for tracking (Mika)

v3: - adapt to removal of clients.lock (Mika)
    - create_event cleanup (Christoph)

v4: - add proper header guards (Christoph)
    - better read_event fault handling (Christoph, Mika)
    - simplify attach (Mika)
    - connect using target file descriptors
    - avoid event->seqno after queue as it is can UAF (Mika)
    - use drmm for eudebug_fini (Maciej)
    - squash dynamic enable

v6: - drm->authenticated is overzealous for render (Mika)

v7: - struct member documentation (Mika)
    - enforce seqno mbz (Mika)

Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Cc: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Cc: Andi Shyti <andi.shyti@linux.intel.com>
Cc: Matt Roper <matthew.d.roper@intel.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
Cc: Andrzej Hajda <andrzej.hajda@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
---
 drivers/gpu/drm/xe/Kconfig            |   10 +
 drivers/gpu/drm/xe/Makefile           |    3 +
 drivers/gpu/drm/xe/xe_device.c        |   14 +
 drivers/gpu/drm/xe/xe_device_types.h  |   32 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 1027 +++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug.h       |   65 ++
 drivers/gpu/drm/xe/xe_eudebug_types.h |  121 +++
 drivers/gpu/drm/xe/xe_vm.c            |    7 +-
 include/uapi/drm/xe_drm.h             |   29 +
 include/uapi/drm/xe_drm_eudebug.h     |   80 ++
 10 files changed, 1387 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug.h
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_types.h
 create mode 100644 include/uapi/drm/xe_drm_eudebug.h

diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index 4d7dcaff2b91..78d4673665b4 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -129,6 +129,16 @@ config DRM_XE_FORCE_PROBE
 
 	  Use "!*" to block the probe of the driver for all known devices.
 
+config DRM_XE_EUDEBUG
+	bool "Enable gdb debugger support (eudebug)"
+	depends on DRM_XE
+	default y
+	help
+	  Choose this option if you want to add support for debugger (gdb) to
+	  attach into process using Xe and debug the gpu/gpgpu programs.
+	  With debugger support, Xe will provide interface for a debugger to
+	  process to track, inspect and modify resources.
+
 menu "drm/Xe Debugging"
 depends on DRM_XE
 depends on EXPERT
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 41ec698b3cc1..eaf6b28a592c 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -150,6 +150,9 @@ xe-$(CONFIG_I2C)	+= xe_i2c.o
 xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
 xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
 
+# debugging shaders with gdb (eudebug) support
+xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o
+
 # graphics hardware monitoring (HWMON) support
 xe-$(CONFIG_HWMON) += xe_hwmon.o
 
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 3462645ca13c..0e7b978d7efb 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -32,6 +32,7 @@
 #include "xe_dma_buf.h"
 #include "xe_drm_client.h"
 #include "xe_drv.h"
+#include "xe_eudebug.h"
 #include "xe_exec.h"
 #include "xe_exec_queue.h"
 #include "xe_force_wake.h"
@@ -107,6 +108,11 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file)
 	mutex_init(&xef->exec_queue.lock);
 	xa_init_flags(&xef->exec_queue.xa, XA_FLAGS_ALLOC1);
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	mutex_init(&xef->eudebug.lock);
+	INIT_LIST_HEAD(&xef->eudebug.target_link);
+#endif
+
 	file->driver_priv = xef;
 	kref_init(&xef->refcount);
 
@@ -129,6 +135,9 @@ static void xe_file_destroy(struct kref *ref)
 	xa_destroy(&xef->vm.xa);
 	mutex_destroy(&xef->vm.lock);
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	mutex_destroy(&xef->eudebug.lock);
+#endif
 	xe_drm_client_put(xef->client);
 	kfree(xef->process_name);
 	kfree(xef);
@@ -170,6 +179,8 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
 
 	guard(xe_pm_runtime)(xe);
 
+	xe_eudebug_file_close(xef);
+
 	/*
 	 * No need for exec_queue.lock here as there is no contention for it
 	 * when FD is closing as IOCTLs presumably can't be modifying the
@@ -211,6 +222,7 @@ static const struct drm_ioctl_desc xe_ioctls[] = {
 			  DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(XE_EXEC_QUEUE_SET_PROPERTY, xe_exec_queue_set_property_ioctl,
 			  DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(XE_EUDEBUG_CONNECT, xe_eudebug_connect_ioctl, DRM_RENDER_ALLOW),
 };
 
 static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1008,6 +1020,8 @@ int xe_device_probe(struct xe_device *xe)
 	if (err)
 		goto err_unregister_display;
 
+	xe_eudebug_init(xe);
+
 	detect_preproduction_hw(xe);
 
 	return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 8f3ef836541e..1a2beffa9498 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -13,6 +13,7 @@
 #include <drm/ttm/ttm_device.h>
 
 #include "xe_devcoredump_types.h"
+#include "xe_eudebug_types.h"
 #include "xe_heci_gsc.h"
 #include "xe_late_bind_fw_types.h"
 #include "xe_oa_types.h"
@@ -566,6 +567,23 @@ struct xe_device {
 		spinlock_t lock;
 	} uncore;
 #endif
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	/** @debugger connection list and globals for device */
+	struct {
+		/** @eudebug.session_count: session counter to track connections */
+		u64 session_count;
+
+		/** @eudebug.available: is the debugging functionality available */
+		enum xe_eudebug_state state;
+
+		/** @eudebug.targets: this is list for xe_files for each target */
+		struct list_head targets;
+
+		/** @eudebug.lock: protects state and targets */
+		struct mutex lock;
+	} eudebug;
+#endif
 };
 
 /**
@@ -627,6 +645,20 @@ struct xe_file {
 
 	/** @refcount: ref count of this xe file */
 	struct kref refcount;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	/** @eudebug: struct to hold eudebug connection specifics */
+	struct {
+		/** @eudebug.debugger: the debugger connection into this xe_file */
+		struct xe_eudebug *debugger;
+
+		/** @eudebug.lock: protecting debugger */
+		struct mutex lock;
+
+		/** @target_link: link into xe_device.eudebug.targets */
+		struct list_head target_link;
+	} eudebug;
+#endif
 };
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
new file mode 100644
index 000000000000..2b7e4afa9b54
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -0,0 +1,1027 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#include <linux/anon_inodes.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+
+#include <drm/drm_managed.h>
+#include <uapi/drm/xe_drm.h>
+
+#include "xe_assert.h"
+#include "xe_device.h"
+#include "xe_eudebug.h"
+#include "xe_eudebug_types.h"
+#include "xe_macros.h"
+#include "xe_vm.h"
+
+/*
+ * If there is no detected event read by userspace, during this period, assume
+ * userspace problem and disconnect debugger to allow forward progress.
+ */
+#define XE_EUDEBUG_NO_READ_DETECTED_TIMEOUT_MS (25 * 1000)
+
+#define cast_event(T, event) container_of((event), typeof(*(T)), base)
+
+static struct drm_xe_eudebug_event *
+event_fifo_pending(struct xe_eudebug *d)
+{
+	struct drm_xe_eudebug_event *event;
+
+	if (kfifo_peek(&d->events.fifo, &event))
+		return event;
+
+	return NULL;
+}
+
+/*
+ * This is racy as we dont take the lock for read but all the
+ * callsites can handle the race so we can live without lock.
+ */
+__no_kcsan
+static unsigned int
+event_fifo_num_events_peek(const struct xe_eudebug * const d)
+{
+	return kfifo_len(&d->events.fifo);
+}
+
+static bool
+xe_eudebug_detached(struct xe_eudebug *d)
+{
+	return READ_ONCE(d->target.xef) == NULL;
+}
+
+static unsigned int
+event_fifo_has_events(struct xe_eudebug *d)
+{
+	/* Allow all waiters to proceed to check their state */
+	if (xe_eudebug_detached(d))
+		return 1;
+
+	return event_fifo_num_events_peek(d);
+}
+
+static const struct rhashtable_params rhash_res = {
+	.head_offset = offsetof(struct xe_eudebug_handle, rh_head),
+	.key_len = sizeof_field(struct xe_eudebug_handle, key),
+	.key_offset = offsetof(struct xe_eudebug_handle, key),
+	.automatic_shrinking = true,
+};
+
+static struct xe_eudebug_resource *
+resource_from_type(struct xe_eudebug *d, int t)
+{
+	return &d->target.res[t];
+}
+
+static int
+xe_eudebug_resources_init(struct xe_eudebug *d)
+{
+	int ret;
+	int i;
+
+	ret = 0;
+	for (i = 0; i < XE_EUDEBUG_RES_TYPE_COUNT; i++) {
+		struct xe_eudebug_resource *r = resource_from_type(d, i);
+
+		xa_init_flags(&r->xa, XA_FLAGS_ALLOC1);
+		ret = rhashtable_init(&r->rh, &rhash_res);
+
+		if (ret)
+			break;
+	}
+
+	if (!ret)
+		return 0;
+
+	while (i--) {
+		struct xe_eudebug_resource *r = resource_from_type(d, i);
+
+		xa_destroy(&r->xa);
+		rhashtable_destroy(&r->rh);
+	}
+
+	return ret;
+}
+
+static void res_free_fn(void *ptr, void *arg)
+{
+	XE_WARN_ON(ptr);
+	kfree(ptr);
+}
+
+static void
+xe_eudebug_resources_destroy(struct xe_eudebug *d)
+{
+	unsigned long j;
+	int err;
+	int i;
+
+	mutex_lock(&d->target.lock);
+	for (i = 0; i < XE_EUDEBUG_RES_TYPE_COUNT; i++) {
+		struct xe_eudebug_resource *r = resource_from_type(d, i);
+		struct xe_eudebug_handle *h;
+
+		xa_for_each(&r->xa, j, h) {
+			struct xe_eudebug_handle *t;
+
+			err = rhashtable_remove_fast(&r->rh,
+						     &h->rh_head,
+						     rhash_res);
+			xe_eudebug_assert(d, !err);
+			t = xa_erase(&r->xa, h->id);
+			xe_eudebug_assert(d, t == h);
+			kfree(t);
+		}
+	}
+	mutex_unlock(&d->target.lock);
+
+	for (i = 0; i < XE_EUDEBUG_RES_TYPE_COUNT; i++) {
+		struct xe_eudebug_resource *r = resource_from_type(d, i);
+
+		rhashtable_free_and_destroy(&r->rh, res_free_fn, NULL);
+		xe_eudebug_assert(d, xa_empty(&r->xa));
+		xa_destroy(&r->xa);
+	}
+}
+
+static void xe_eudebug_free(struct kref *ref)
+{
+	struct xe_eudebug *d = container_of(ref, typeof(*d), ref);
+	struct drm_xe_eudebug_event *event;
+
+	xe_assert(d->xe, xe_eudebug_detached(d));
+
+	while (kfifo_get(&d->events.fifo, &event))
+		kfree(event);
+
+	xe_eudebug_resources_destroy(d);
+	mutex_destroy(&d->target.lock);
+	XE_WARN_ON(d->target.xef);
+
+	xe_eudebug_assert(d, !kfifo_len(&d->events.fifo));
+
+	kfree(d);
+}
+
+static void xe_eudebug_put(struct xe_eudebug *d)
+{
+	kref_put(&d->ref, xe_eudebug_free);
+}
+
+static void remove_debugger(struct xe_file *xef)
+{
+	struct xe_eudebug *d;
+
+	if (XE_WARN_ON(!xef))
+		return;
+
+	mutex_lock(&xef->eudebug.lock);
+	d = xef->eudebug.debugger;
+	if (d)
+		xef->eudebug.debugger = NULL;
+	mutex_unlock(&xef->eudebug.lock);
+
+	if (d) {
+		struct xe_device *xe = d->xe;
+
+		mutex_lock(&xe->eudebug.lock);
+		list_del_init(&xef->eudebug.target_link);
+		mutex_unlock(&xe->eudebug.lock);
+
+		eu_dbg(d, "debugger removed");
+
+		xe_eudebug_put(d);
+	}
+}
+
+static bool xe_eudebug_detach(struct xe_device *xe,
+			      struct xe_eudebug *d,
+			      const int err)
+{
+	struct xe_file *target = NULL;
+
+	XE_WARN_ON(err > 0);
+
+	mutex_lock(&d->target.lock);
+	if (d->target.xef) {
+		target = d->target.xef;
+		d->target.err = err;
+		WRITE_ONCE(d->target.xef, NULL);
+	}
+	mutex_unlock(&d->target.lock);
+
+	if (!target)
+		return false;
+
+	eu_dbg(d, "session %lld detached with %d", d->session, err);
+
+	remove_debugger(target);
+	xe_file_put(target);
+
+	return true;
+}
+
+static int _xe_eudebug_disconnect(struct xe_eudebug *d,
+				  const int err)
+{
+	wake_up_all(&d->events.write_done);
+	wake_up_all(&d->events.read_done);
+
+	return xe_eudebug_detach(d->xe, d, err);
+}
+
+#define xe_eudebug_disconnect(_d, _err) ({ \
+	if (_xe_eudebug_disconnect((_d), (_err))) { \
+		if ((_err) == 0 || (_err) == -ETIMEDOUT) \
+			eu_dbg((_d), "Session closed (%d)", (_err));	\
+		else \
+			eu_err((_d), "Session disconnected, err = %d (%s:%d)", \
+			       (_err), __func__, __LINE__); \
+	} \
+})
+
+static struct xe_eudebug *
+xe_eudebug_get(struct xe_file *xef)
+{
+	struct xe_eudebug *d;
+
+	mutex_lock(&xef->eudebug.lock);
+	d = xef->eudebug.debugger;
+	if (d && !kref_get_unless_zero(&d->ref))
+		d = NULL;
+	mutex_unlock(&xef->eudebug.lock);
+
+	if (!d)
+		return NULL;
+
+	if (xe_eudebug_detached(d)) {
+		xe_eudebug_put(d);
+		return NULL;
+	}
+
+	return d;
+}
+
+static int xe_eudebug_queue_event(struct xe_eudebug *d,
+				  struct drm_xe_eudebug_event *event)
+{
+	const u64 wait_jiffies = msecs_to_jiffies(1000);
+	u64 last_read_detected_ts, last_head_seqno, start_ts;
+	const u64 event_seqno = event->seqno;
+
+	xe_eudebug_assert(d, event->len > sizeof(struct drm_xe_eudebug_event));
+	xe_eudebug_assert(d, event->type);
+	xe_eudebug_assert(d, event->type != DRM_XE_EUDEBUG_EVENT_READ);
+
+	start_ts = ktime_get();
+	last_read_detected_ts = start_ts;
+	last_head_seqno = 0;
+
+	do  {
+		struct drm_xe_eudebug_event *head;
+		u64 head_seqno;
+		bool was_queued;
+
+		if (xe_eudebug_detached(d))
+			break;
+
+		spin_lock(&d->events.lock);
+		head = event_fifo_pending(d);
+		if (head)
+			head_seqno = event->seqno;
+		else
+			head_seqno = 0;
+
+		was_queued = kfifo_in(&d->events.fifo, &event, 1);
+		spin_unlock(&d->events.lock);
+
+		wake_up_all(&d->events.write_done);
+
+		if (was_queued) {
+			eu_dbg(d, "queued event with seqno %lld (head %lld)\n",
+			       event_seqno, head_seqno);
+			event = NULL;
+			break;
+		}
+
+		XE_WARN_ON(!head_seqno);
+
+		/* If we detect progress, restart timeout */
+		if (last_head_seqno != head_seqno)
+			last_read_detected_ts = ktime_get();
+
+		last_head_seqno = head_seqno;
+
+		wait_event_interruptible_timeout(d->events.read_done,
+						 !kfifo_is_full(&d->events.fifo),
+						 wait_jiffies);
+
+	} while (ktime_ms_delta(ktime_get(), last_read_detected_ts) <
+		 XE_EUDEBUG_NO_READ_DETECTED_TIMEOUT_MS);
+
+	if (event) {
+		eu_dbg(d,
+		       "event %llu queue failed (blocked %lld ms, avail %d)",
+		       event ? event->seqno : 0,
+		       ktime_ms_delta(ktime_get(), start_ts),
+		       kfifo_avail(&d->events.fifo));
+
+		kfree(event);
+
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static struct xe_eudebug_handle *
+alloc_handle(const int type, const u64 key)
+{
+	struct xe_eudebug_handle *h;
+
+	h = kzalloc_obj(*h, GFP_ATOMIC);
+	if (!h)
+		return NULL;
+
+	h->key = key;
+
+	return h;
+}
+
+static struct xe_eudebug_handle *
+__find_handle(struct xe_eudebug_resource *r,
+	      const u64 key)
+{
+	struct xe_eudebug_handle *h;
+
+	h = rhashtable_lookup_fast(&r->rh,
+				   &key,
+				   rhash_res);
+	return h;
+}
+
+static int _xe_eudebug_add_handle(struct xe_eudebug *d,
+				  int type,
+				  void *p,
+				  u64 *seqno,
+				  int *handle)
+{
+	const u64 key = (uintptr_t)p;
+	struct xe_eudebug_resource *r;
+	struct xe_eudebug_handle *h, *o;
+	int err;
+
+	if (XE_WARN_ON(!p))
+		return -EINVAL;
+
+	if (xe_eudebug_detached(d))
+		return -ENOTCONN;
+
+	h = alloc_handle(type, key);
+	if (!h)
+		return -ENOMEM;
+
+	r = resource_from_type(d, type);
+
+	mutex_lock(&d->target.lock);
+	o = __find_handle(r, key);
+	if (!o) {
+		err = xa_alloc(&r->xa, &h->id, h, xa_limit_31b, GFP_KERNEL);
+
+		if (h->id >= INT_MAX) {
+			xa_erase(&r->xa, h->id);
+			err = -ENOSPC;
+		}
+
+		if (!err)
+			err = rhashtable_insert_fast(&r->rh,
+						     &h->rh_head,
+						     rhash_res);
+
+		if (err) {
+			xa_erase(&r->xa, h->id);
+		} else {
+			if (seqno)
+				*seqno = atomic_long_inc_return(&d->events.seqno);
+		}
+	} else {
+		xe_eudebug_assert(d, o->id);
+		err = -EEXIST;
+	}
+	mutex_unlock(&d->target.lock);
+
+	if (handle)
+		*handle = o ? o->id : h->id;
+
+	if (err) {
+		kfree(h);
+		XE_WARN_ON(err > 0);
+		return err;
+	}
+
+	xe_eudebug_assert(d, h->id);
+
+	return h->id;
+}
+
+static int xe_eudebug_add_handle(struct xe_eudebug *d,
+				 int type,
+				 void *p,
+				 u64 *seqno)
+{
+	int ret;
+
+	ret = _xe_eudebug_add_handle(d, type, p, seqno, NULL);
+
+	eu_dbg(d, "handle type %d handle %p added: %d\n", type, p, ret);
+
+	return ret;
+}
+
+static int _xe_eudebug_remove_handle(struct xe_eudebug *d, int type, void *p,
+				     u64 *seqno)
+{
+	const u64 key = (uintptr_t)p;
+	struct xe_eudebug_resource *r;
+	struct xe_eudebug_handle *h, *xa_h;
+	int ret;
+
+	if (XE_WARN_ON(!key))
+		return -EINVAL;
+
+	if (xe_eudebug_detached(d))
+		return -ENOTCONN;
+
+	r = resource_from_type(d, type);
+
+	mutex_lock(&d->target.lock);
+	h = __find_handle(r, key);
+	if (h) {
+		ret = rhashtable_remove_fast(&r->rh,
+					     &h->rh_head,
+					     rhash_res);
+		xe_eudebug_assert(d, !ret);
+		xa_h = xa_erase(&r->xa, h->id);
+		xe_eudebug_assert(d, xa_h == h);
+		if (!ret) {
+			ret = h->id;
+			if (seqno)
+				*seqno = atomic_long_inc_return(&d->events.seqno);
+		}
+	} else {
+		ret = -ENOENT;
+	}
+	mutex_unlock(&d->target.lock);
+
+	kfree(h);
+
+	xe_eudebug_assert(d, ret);
+
+	return ret;
+}
+
+static int xe_eudebug_remove_handle(struct xe_eudebug *d, int type, void *p,
+				    u64 *seqno)
+{
+	int ret;
+
+	ret = _xe_eudebug_remove_handle(d, type, p, seqno);
+
+	eu_dbg(d, "handle type %d handle %p removed: %d\n", type, p, ret);
+
+	return ret;
+}
+
+static struct drm_xe_eudebug_event *
+xe_eudebug_create_event(struct xe_eudebug *d, u16 type, u64 seqno, u16 flags,
+			u32 len)
+{
+	const u16 known_flags =
+		DRM_XE_EUDEBUG_EVENT_CREATE |
+		DRM_XE_EUDEBUG_EVENT_DESTROY |
+		DRM_XE_EUDEBUG_EVENT_STATE_CHANGE |
+		DRM_XE_EUDEBUG_EVENT_NEED_ACK;
+	struct drm_xe_eudebug_event *event;
+
+	BUILD_BUG_ON(type > XE_EUDEBUG_MAX_EVENT_TYPE);
+
+	xe_eudebug_assert(d, type <= XE_EUDEBUG_MAX_EVENT_TYPE);
+	xe_eudebug_assert(d, !(~known_flags & flags));
+	xe_eudebug_assert(d, len > sizeof(*event));
+
+	event = kzalloc(len, GFP_KERNEL);
+	if (!event)
+		return NULL;
+
+	event->len = len;
+	event->type = type;
+	event->flags = flags;
+	event->seqno = seqno;
+
+	return event;
+}
+
+static int send_vm_event(struct xe_eudebug *d, u32 flags,
+			 const u64 vm_handle,
+			 const u64 seqno)
+{
+	struct drm_xe_eudebug_event *event;
+	struct drm_xe_eudebug_event_vm *e;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM,
+					seqno, flags, sizeof(*e));
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	e->vm_handle = vm_handle;
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int vm_create_event(struct xe_eudebug *d,
+			   struct xe_file *xef, struct xe_vm *vm)
+{
+	int vm_id;
+	u64 seqno;
+	int ret;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return 0;
+
+	vm_id = xe_eudebug_add_handle(d, XE_EUDEBUG_RES_TYPE_VM, vm, &seqno);
+	if (vm_id < 0)
+		return vm_id;
+
+	ret = send_vm_event(d, DRM_XE_EUDEBUG_EVENT_CREATE, vm_id, seqno);
+	if (ret)
+		eu_dbg(d, "send_vm_event create error %d\n", ret);
+
+	return ret;
+}
+
+static int vm_destroy_event(struct xe_eudebug *d,
+			    struct xe_file *xef, struct xe_vm *vm)
+{
+	int vm_id;
+	u64 seqno;
+	int ret;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return 0;
+
+	vm_id = xe_eudebug_remove_handle(d, XE_EUDEBUG_RES_TYPE_VM, vm, &seqno);
+	if (vm_id < 0)
+		return vm_id;
+
+	ret = send_vm_event(d, DRM_XE_EUDEBUG_EVENT_DESTROY, vm_id, seqno);
+	if (ret)
+		eu_dbg(d, "send_vm_event destroy error %d\n", ret);
+
+	return ret;
+}
+
+#define xe_eudebug_event_put(_d, _err) ({ \
+	if ((_err)) \
+		xe_eudebug_disconnect((_d), (_err)); \
+	xe_eudebug_put((_d)); \
+	})
+
+void xe_eudebug_vm_create(struct xe_file *xef, struct xe_vm *vm)
+{
+	struct xe_eudebug *d;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, vm_create_event(d, xef, vm));
+}
+
+void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm)
+{
+	struct xe_eudebug *d;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, vm_destroy_event(d, xef, vm));
+}
+
+static int add_debugger(struct xe_device *xe, struct xe_eudebug *d,
+			struct drm_file *target)
+{
+	struct xe_file *xef = target->driver_priv;
+	int ret = -EBUSY;
+
+	mutex_lock(&xef->eudebug.lock);
+	if (!xef->eudebug.debugger) {
+		d->target.xef = xe_file_get(xef);
+		d->target.pid = xef->pid;
+		kref_get(&d->ref);
+		xef->eudebug.debugger = d;
+		ret = 0;
+	}
+	mutex_unlock(&xef->eudebug.lock);
+
+	if (ret)
+		return ret;
+
+	mutex_lock(&xe->eudebug.lock);
+	XE_WARN_ON(!list_empty(&xef->eudebug.target_link));
+	list_add_tail(&xef->eudebug.target_link, &xef->xe->eudebug.targets);
+	mutex_unlock(&xe->eudebug.lock);
+
+	return 0;
+}
+
+static int
+xe_eudebug_attach(struct xe_device *xe, struct drm_file *parent_file,
+		  struct xe_eudebug *d, u64 target_pidfd)
+{
+	struct file *file __free(fput) = NULL;
+	struct drm_file *drm_file;
+	struct xe_file *target_xef;
+	int ret;
+
+	file = fget(target_pidfd);
+	if (XE_IOCTL_DBG(xe, !file))
+		return -EBADFD;
+
+	drm_file = file->private_data;
+	if (XE_IOCTL_DBG(xe, !drm_file))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, parent_file->filp->f_op != file->f_op))
+		return -EINVAL;
+
+	target_xef = drm_file->driver_priv;
+	if (XE_IOCTL_DBG(xe, !target_xef))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, xe != target_xef->xe))
+		return -EINVAL;
+
+	ret = add_debugger(xe, d, drm_file);
+	if (XE_IOCTL_DBG(xe, ret))
+		return ret;
+
+	d->xe = xe;
+	d->session = ++xe->eudebug.session_count ?: 1;
+
+	eu_dbg(d, "session %lld attached to %s", d->session,
+	       parent_file == drm_file ? "self" : "remote");
+
+	return 0;
+}
+
+static int xe_eudebug_release(struct inode *inode, struct file *file)
+{
+	struct xe_eudebug *d = file->private_data;
+
+	xe_eudebug_disconnect(d, 0);
+	xe_eudebug_put(d);
+
+	return 0;
+}
+
+static __poll_t xe_eudebug_poll(struct file *file, poll_table *wait)
+{
+	struct xe_eudebug * const d = file->private_data;
+	__poll_t ret = 0;
+
+	poll_wait(file, &d->events.write_done, wait);
+
+	if (xe_eudebug_detached(d)) {
+		ret |= EPOLLHUP;
+		if (d->target.err)
+			ret |= EPOLLERR;
+	}
+
+	if (event_fifo_num_events_peek(d))
+		ret |= EPOLLIN;
+
+	return ret;
+}
+
+static ssize_t xe_eudebug_read(struct file *file,
+			       char __user *buf,
+			       size_t count,
+			       loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static long xe_eudebug_read_event(struct xe_eudebug *d,
+				  const u64 arg,
+				  const bool wait)
+{
+	struct xe_device *xe = d->xe;
+	struct drm_xe_eudebug_event __user * const user_orig =
+		u64_to_user_ptr(arg);
+	struct drm_xe_eudebug_event user_event;
+	struct drm_xe_eudebug_event *pending, *event_out;
+	long ret = 0;
+
+	if (XE_IOCTL_DBG(xe, copy_from_user(&user_event, user_orig, sizeof(user_event))))
+		return -EFAULT;
+
+	if (XE_IOCTL_DBG(xe, user_event.type != DRM_XE_EUDEBUG_EVENT_READ))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, user_event.len < sizeof(*user_orig)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, user_event.flags))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, user_event.seqno))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, user_event.reserved))
+		return -EINVAL;
+
+	/* XXX: define wait time in connect arguments ? */
+	if (wait) {
+		ret = wait_event_interruptible_timeout(d->events.write_done,
+						       event_fifo_has_events(d),
+						       msecs_to_jiffies(5 * 1000));
+
+		if (XE_IOCTL_DBG(xe, ret < 0))
+			return ret;
+	}
+
+	if (XE_IOCTL_DBG(xe, xe_eudebug_detached(d)))
+		return -ENOTCONN;
+
+	event_out = NULL;
+	spin_lock(&d->events.lock);
+	pending = event_fifo_pending(d);
+	if (!pending)
+		ret = wait ? -ETIMEDOUT : -EAGAIN;
+	else if (user_event.len < pending->len)
+		ret = -EMSGSIZE;
+	else if (access_ok(user_orig, pending->len))
+		ret = kfifo_out(&d->events.fifo, &event_out, 1) == 1 ? 0 : -EIO;
+	else
+		ret = -EFAULT;
+
+	wake_up_all(&d->events.read_done);
+	spin_unlock(&d->events.lock);
+
+	if (!pending)
+		return ret;
+
+	if (ret == -EMSGSIZE) {
+		if (XE_IOCTL_DBG(xe, put_user(pending->len, &user_orig->len)))
+			return -EFAULT;
+
+		return -EMSGSIZE;
+	}
+
+	if (XE_IOCTL_DBG(xe, ret)) {
+		xe_eudebug_disconnect(d, (int)ret);
+		return ret;
+	}
+
+	XE_WARN_ON(pending != event_out);
+
+	if (__copy_to_user(user_orig, event_out, event_out->len)) {
+		ret = -EFAULT;
+		/* We can't rollback anymore, disconnect */
+		xe_eudebug_disconnect(d, -EFAULT);
+	}
+
+	eu_dbg(d, "event read=%ld: type=%u, flags=0x%x, seqno=%llu", ret,
+	       event_out->type, event_out->flags, event_out->seqno);
+
+	kfree(event_out);
+
+	return ret;
+}
+
+static long xe_eudebug_ioctl(struct file *file,
+			     unsigned int cmd,
+			     unsigned long arg)
+{
+	struct xe_eudebug * const d = file->private_data;
+	long ret;
+
+	switch (cmd) {
+	case DRM_XE_EUDEBUG_IOCTL_READ_EVENT:
+		ret = xe_eudebug_read_event(d, arg,
+					    !(file->f_flags & O_NONBLOCK));
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct file_operations fops = {
+	.owner		= THIS_MODULE,
+	.release	= xe_eudebug_release,
+	.poll		= xe_eudebug_poll,
+	.read		= xe_eudebug_read,
+	.unlocked_ioctl	= xe_eudebug_ioctl,
+};
+
+static int
+xe_eudebug_connect(struct xe_device *xe,
+		   struct drm_file *file,
+		   struct drm_xe_eudebug_connect *param)
+{
+	const u64 known_open_flags = 0;
+	unsigned long f_flags = 0;
+	struct xe_eudebug *d;
+	int fd, err;
+
+	if (XE_IOCTL_DBG(xe, param->extensions))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !param->fd))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, param->flags & ~known_open_flags))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, param->version &&
+			 param->version != DRM_XE_EUDEBUG_VERSION))
+		return -EINVAL;
+
+	param->version = DRM_XE_EUDEBUG_VERSION;
+
+	mutex_lock(&xe->eudebug.lock);
+	err = xe_eudebug_is_enabled(xe) ? 0 : -EOPNOTSUPP;
+	mutex_unlock(&xe->eudebug.lock);
+
+	if (XE_IOCTL_DBG(xe, err))
+		return err;
+
+	d = kzalloc_obj(*d, GFP_KERNEL);
+	if (XE_IOCTL_DBG(xe, !d))
+		return -ENOMEM;
+
+	kref_init(&d->ref);
+	mutex_init(&d->target.lock);
+	init_waitqueue_head(&d->events.write_done);
+	init_waitqueue_head(&d->events.read_done);
+
+	spin_lock_init(&d->events.lock);
+	INIT_KFIFO(d->events.fifo);
+
+	err = xe_eudebug_resources_init(d);
+	if (XE_IOCTL_DBG(xe, err))
+		goto err_free;
+
+	err = xe_eudebug_attach(xe, file, d, param->fd);
+	if (XE_IOCTL_DBG(xe, err))
+		goto err_free_res;
+
+	fd = anon_inode_getfd("[xe_eudebug]", &fops, d, f_flags);
+	if (XE_IOCTL_DBG(xe, fd < 0)) {
+		err = fd;
+		goto err_detach;
+	}
+
+	eu_dbg(d, "connected session %lld", d->session);
+
+	return fd;
+
+err_detach:
+	xe_eudebug_detach(xe, d, err);
+err_free_res:
+	xe_eudebug_resources_destroy(d);
+err_free:
+	kfree(d);
+
+	return err;
+}
+
+void xe_eudebug_file_close(struct xe_file *xef)
+{
+	remove_debugger(xef);
+}
+
+bool xe_eudebug_is_enabled(struct xe_device *xe)
+{
+	return READ_ONCE(xe->eudebug.state) == XE_EUDEBUG_ENABLED;
+}
+
+int xe_eudebug_enable(struct xe_device *xe, bool enable)
+{
+	mutex_lock(&xe->eudebug.lock);
+
+	if (xe->eudebug.state == XE_EUDEBUG_NOT_SUPPORTED) {
+		mutex_unlock(&xe->eudebug.lock);
+		return -EPERM;
+	}
+
+	if (!enable && !list_empty(&xe->eudebug.targets)) {
+		mutex_unlock(&xe->eudebug.lock);
+		return -EBUSY;
+	}
+
+	if (enable == xe_eudebug_is_enabled(xe)) {
+		mutex_unlock(&xe->eudebug.lock);
+		return 0;
+	}
+
+	xe->eudebug.state = enable ?
+		XE_EUDEBUG_ENABLED : XE_EUDEBUG_DISABLED;
+	mutex_unlock(&xe->eudebug.lock);
+
+	return 0;
+}
+
+static ssize_t enable_eudebug_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
+
+	return sysfs_emit(buf, "%u\n", xe_eudebug_is_enabled(xe));
+}
+
+static ssize_t enable_eudebug_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
+	bool enable;
+	int ret;
+
+	ret = kstrtobool(buf, &enable);
+	if (ret)
+		return ret;
+
+	ret = xe_eudebug_enable(xe, enable);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(enable_eudebug);
+
+static void xe_eudebug_sysfs_fini(void *arg)
+{
+	struct xe_device *xe = arg;
+	struct drm_device *dev = &xe->drm;
+
+	sysfs_remove_file(&dev->dev->kobj,
+			  &dev_attr_enable_eudebug.attr);
+}
+
+void xe_eudebug_init(struct xe_device *xe)
+{
+	struct drm_device *dev = &xe->drm;
+	int err;
+
+	INIT_LIST_HEAD(&xe->eudebug.targets);
+
+	xe->eudebug.state = XE_EUDEBUG_NOT_SUPPORTED;
+
+	err = drmm_mutex_init(dev, &xe->eudebug.lock);
+	if (err)
+		goto out_err;
+
+	err = sysfs_create_file(&dev->dev->kobj,
+				&dev_attr_enable_eudebug.attr);
+	if (err)
+		goto out_err;
+
+	err = devm_add_action_or_reset(dev->dev, xe_eudebug_sysfs_fini, xe);
+	if (err)
+		goto out_err;
+
+	xe->eudebug.state = XE_EUDEBUG_DISABLED;
+
+	return;
+
+out_err:
+	drm_warn(&xe->drm, "eudebug disabled, init fail: %d\n", err);
+}
+
+int xe_eudebug_connect_ioctl(struct drm_device *dev,
+			     void *data,
+			     struct drm_file *file)
+{
+	struct xe_device *xe = to_xe_device(dev);
+	struct drm_xe_eudebug_connect * const param = data;
+
+	return xe_eudebug_connect(xe, file, param);
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
new file mode 100644
index 000000000000..22fbb2ff24da
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#ifndef _XE_EUDEBUG_H_
+#define _XE_EUDEBUG_H_
+
+#include <linux/types.h>
+
+struct drm_device;
+struct drm_file;
+struct xe_device;
+struct xe_file;
+struct xe_vm;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+
+#define XE_EUDEBUG_DBG_STR "eudbg: %lld:%lu:%s (%d/%d) -> (%d): "
+#define XE_EUDEBUG_DBG_ARGS(d) (d)->session, \
+		atomic_long_read(&(d)->events.seqno), \
+		!READ_ONCE(d->target.xef) ? "disconnected" : "", \
+		current->pid, \
+		task_tgid_nr(current), \
+		READ_ONCE(d->target.xef) ? d->target.xef->pid : -1
+
+#define eu_err(d, fmt, ...) drm_err(&(d)->xe->drm, XE_EUDEBUG_DBG_STR # fmt, \
+				    XE_EUDEBUG_DBG_ARGS(d), ##__VA_ARGS__)
+#define eu_warn(d, fmt, ...) drm_warn(&(d)->xe->drm, XE_EUDEBUG_DBG_STR # fmt, \
+				      XE_EUDEBUG_DBG_ARGS(d), ##__VA_ARGS__)
+#define eu_dbg(d, fmt, ...) drm_dbg(&(d)->xe->drm, XE_EUDEBUG_DBG_STR # fmt, \
+				    XE_EUDEBUG_DBG_ARGS(d), ##__VA_ARGS__)
+
+#define xe_eudebug_assert(d, ...) xe_assert((d)->xe, ##__VA_ARGS__)
+
+int xe_eudebug_connect_ioctl(struct drm_device *dev,
+			     void *data,
+			     struct drm_file *file);
+
+void xe_eudebug_init(struct xe_device *xe);
+bool xe_eudebug_is_enabled(struct xe_device *xe);
+
+void xe_eudebug_file_close(struct xe_file *xef);
+
+void xe_eudebug_vm_create(struct xe_file *xef, struct xe_vm *vm);
+void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm);
+int xe_eudebug_enable(struct xe_device *xe, bool enable);
+
+#else
+
+static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
+					   void *data,
+					   struct drm_file *file) { return 0; }
+
+static inline void xe_eudebug_init(struct xe_device *xe) { }
+static inline bool xe_eudebug_is_enabled(struct xe_device *xe) { return false; }
+
+static inline void xe_eudebug_file_close(struct xe_file *xef) { }
+
+static inline void xe_eudebug_vm_create(struct xe_file *xef, struct xe_vm *vm) { }
+static inline void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm) { }
+
+#endif /* CONFIG_DRM_XE_EUDEBUG */
+
+#endif /* _XE_EUDEBUG_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
new file mode 100644
index 000000000000..a73eb6c98b02
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#ifndef _XE_EUDEBUG_TYPES_H_
+#define _XE_EUDEBUG_TYPES_H_
+
+#include <linux/completion.h>
+#include <linux/kfifo.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/rhashtable.h>
+#include <linux/wait.h>
+#include <linux/xarray.h>
+
+struct xe_device;
+struct task_struct;
+
+/**
+ * enum xe_eudebug_state - eudebug capability state
+ *
+ * @XE_EUDEBUG_NOT_SUPPORTED: eudebug feature support off
+ * @XE_EUDEBUG_DISABLED: eudebug feature supported but disabled
+ * @XE_EUDEBUG_ENABLED: eudebug enabled
+ */
+enum xe_eudebug_state {
+	XE_EUDEBUG_NOT_SUPPORTED = 0,
+	XE_EUDEBUG_DISABLED,
+	XE_EUDEBUG_ENABLED,
+};
+
+#define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
+#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_VM
+
+/**
+ * struct xe_eudebug_handle - eudebug resource handle
+ */
+struct xe_eudebug_handle {
+	/** @key: key value in rhashtable <key:id> */
+	u64 key;
+
+	/** @id: opaque handle id for xarray <id:key> */
+	int id;
+
+	/** @rh_head: rhashtable head */
+	struct rhash_head rh_head;
+};
+
+/**
+ * struct xe_eudebug_resource - Resource map for one resource
+ */
+struct xe_eudebug_resource {
+	/** @xa: xarrays for <id->key> */
+	struct xarray xa;
+
+	/** @rh: rhashtable for <key->id> */
+	struct rhashtable rh;
+};
+
+#define XE_EUDEBUG_RES_TYPE_VM		0
+#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_VM + 1)
+
+/**
+ * struct xe_eudebug - Top level struct for eudebug: the connection
+ */
+struct xe_eudebug {
+	/** @ref: kref counter for this struct */
+	struct kref ref;
+
+	/** @target: debug target specifics */
+	struct {
+		/** @xef: the target xe_file that we are debugging */
+		struct xe_file *xef;
+
+		/** @pid: pid of target */
+		pid_t pid;
+
+		/** @err: error code on disconnect */
+		int err;
+
+		/** @lock: guards access to xef and err */
+		struct mutex lock;
+
+		/** @rt: resource maps for all types */
+		struct xe_eudebug_resource res[XE_EUDEBUG_RES_TYPE_COUNT];
+	} target;
+
+	/** @xe: the parent device we are serving */
+	struct xe_device *xe;
+
+	/** @res: the resource maps we track for target_task */
+	struct xe_eudebug_resources *res;
+
+	/** @session: session number for this connection (for logs) */
+	u64 session;
+
+	/** @events: kfifo queue of to-be-delivered events */
+	struct {
+		/** @lock: guards access to fifo */
+		spinlock_t lock;
+
+		/** @fifo: queue of events pending */
+		DECLARE_KFIFO(fifo,
+			      struct drm_xe_eudebug_event *,
+			      CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE);
+
+		/** @write_done: waitqueue for signalling write to fifo */
+		wait_queue_head_t write_done;
+
+		/** @read_done: waitqueue for signalling read from fifo */
+		wait_queue_head_t read_done;
+
+		/** @event_seqno: seqno counter to stamp events for fifo */
+		atomic_long_t seqno;
+	} events;
+
+};
+
+#endif /* _XE_EUDEBUG_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 5adabfd5dc30..64368110f517 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -26,6 +26,7 @@
 #include "xe_bo.h"
 #include "xe_device.h"
 #include "xe_drm_client.h"
+#include "xe_eudebug.h"
 #include "xe_exec_queue.h"
 #include "xe_migrate.h"
 #include "xe_pat.h"
@@ -2003,6 +2004,8 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data,
 
 	args->vm_id = id;
 
+	xe_eudebug_vm_create(xef, vm);
+
 	return 0;
 
 err_close_and_put:
@@ -2034,8 +2037,10 @@ int xe_vm_destroy_ioctl(struct drm_device *dev, void *data,
 		xa_erase(&xef->vm.xa, args->vm_id);
 	mutex_unlock(&xef->vm.lock);
 
-	if (!err)
+	if (!err) {
+		xe_eudebug_vm_destroy(xef, vm);
 		xe_vm_close_and_put(vm);
+	}
 
 	return err;
 }
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index c9e70f78e723..97b479b51c73 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -107,6 +107,7 @@ extern "C" {
 #define DRM_XE_MADVISE			0x0c
 #define DRM_XE_VM_QUERY_MEM_RANGE_ATTRS	0x0d
 #define DRM_XE_EXEC_QUEUE_SET_PROPERTY	0x0e
+#define DRM_XE_EUDEBUG_CONNECT		0x0f
 
 /* Must be kept compact -- no holes */
 
@@ -125,6 +126,7 @@ extern "C" {
 #define DRM_IOCTL_XE_MADVISE			DRM_IOW(DRM_COMMAND_BASE + DRM_XE_MADVISE, struct drm_xe_madvise)
 #define DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS	DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_VM_QUERY_MEM_RANGE_ATTRS, struct drm_xe_vm_query_mem_range_attr)
 #define DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY	DRM_IOW(DRM_COMMAND_BASE + DRM_XE_EXEC_QUEUE_SET_PROPERTY, struct drm_xe_exec_queue_set_property)
+#define DRM_IOCTL_XE_EUDEBUG_CONNECT		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_EUDEBUG_CONNECT, struct drm_xe_eudebug_connect)
 
 /**
  * DOC: Xe IOCTL Extensions
@@ -2351,6 +2353,33 @@ struct drm_xe_exec_queue_set_property {
 	__u64 reserved[2];
 };
 
+/**
+ * struct drm_xe_eudebug_connect - Input of &DRM_IOCTL_XE_EUDEBUG_CONNECT
+ *
+ * This structure is used to connect to an eudebug interface of target drm file.
+ */
+struct drm_xe_eudebug_connect {
+	/** @extensions: Pointer to the first extension struct, if any */
+	__u64 extensions;
+
+	/** @fd: Debug target DRM client fd */
+	__u64 fd;
+
+	/** @flags: Flags (MBZ) */
+	__u32 flags;
+
+	/**
+	 * @version: Current ABI (ioctl / events) version.
+	 *
+	 * If zero, current version supported in return.
+	 * If non zero, the version requested.
+	 */
+	__u32 version;
+#define DRM_XE_EUDEBUG_VERSION 1
+};
+
+#include "xe_drm_eudebug.h"
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
new file mode 100644
index 000000000000..cdb4e4af4879
--- /dev/null
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef _UAPI_XE_DRM_EUDEBUG_H_
+#define _UAPI_XE_DRM_EUDEBUG_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
+
+/**
+ * struct drm_xe_eudebug_event - Base type of event delivered by xe_eudebug.
+ *
+ * Base event for xe_eudebug interface.
+ *
+ * For receiving events :c:member:`drm_xe_eudebug_event.type` has to
+ * be DRM_XE_EUDEBUG_EVENT_READ. On return, this is set to the type
+ * of event received. :c:member:`drm_xe_eudebug_event.len` has to be
+ * set to maximum size that can be received. On return, len will be set
+ * to the event size. If the pending event was larger than this size,
+ * -EMSGSIZE is returned instead of 0 and the caller should retry with a larger
+ * allocated receive length.
+ *
+ * :c:member:`drm_xe_eudebug_event.seqno` can be used to form a timeline
+ * as event delivery order does not guarantee event creation
+ * order. Must be set to zero.
+ *
+ * :c:member:`drm_xe_eudebug_event.flags` will indicate if a resource was
+ * created, destroyed, or if its state changed. Must be set to zero.
+ *
+ * If DRM_XE_EUDEBUG_EVENT_NEED_ACK is set, xe_eudebug
+ * will hold the said resource until it is acked by userspace
+ * using the acking ioctl with the seqno of the said event.
+ */
+struct drm_xe_eudebug_event {
+	/** @len: Length */
+	__u32 len;
+
+	/** @type: Type */
+	__u16 type;
+#define DRM_XE_EUDEBUG_EVENT_NONE		0
+#define DRM_XE_EUDEBUG_EVENT_READ		1
+#define DRM_XE_EUDEBUG_EVENT_VM			2
+
+	/** @flags: Flags */
+	__u16 flags;
+#define DRM_XE_EUDEBUG_EVENT_CREATE		(1 << 0)
+#define DRM_XE_EUDEBUG_EVENT_DESTROY		(1 << 1)
+#define DRM_XE_EUDEBUG_EVENT_STATE_CHANGE	(1 << 2)
+#define DRM_XE_EUDEBUG_EVENT_NEED_ACK		(1 << 3)
+
+	/** @seqno: Sequence number to form a timeline */
+	__u64 seqno;
+
+	/** @reserved: Reserved field, must be zero. */
+	__u64 reserved;
+};
+
+/**
+ * struct drm_xe_eudebug_event_vm - VM event
+ *
+ * VM event is delivered when vm is created or destroyed.
+ */
+struct drm_xe_eudebug_event_vm {
+	/** @base: base event */
+	struct drm_xe_eudebug_event base;
+
+	/** @vm_handle: unique handle for vm */
+	__u64 vm_handle;
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _UAPI_XE_DRM_EUDEBUG_H_ */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 02/22] drm/xe/eudebug: Add documentation
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 01/22] drm/xe/eudebug: Introduce eudebug interface Mika Kuoppala
@ 2026-02-23 14:02 ` Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 03/22] drm/xe/eudebug: Add connection establishment documentation Mika Kuoppala
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:02 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

Add a core documentation for connection setup and events.
Followup patches can then incrementally add their workings.

Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/index.rst      |  1 +
 Documentation/gpu/xe/xe_eudebug.rst | 45 +++++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug.c     | 41 ++++++++++++++++++++++++++
 3 files changed, 87 insertions(+)
 create mode 100644 Documentation/gpu/xe/xe_eudebug.rst

diff --git a/Documentation/gpu/xe/index.rst b/Documentation/gpu/xe/index.rst
index bc432c95d1a3..34e2f32a96ca 100644
--- a/Documentation/gpu/xe/index.rst
+++ b/Documentation/gpu/xe/index.rst
@@ -29,3 +29,4 @@ DG2, etc is provided to prototype the driver.
    xe_device
    xe-drm-usage-stats.rst
    xe_configfs
+   xe_eudebug
diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
new file mode 100644
index 000000000000..ff7fbc403ebb
--- /dev/null
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -0,0 +1,45 @@
+.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+===================================
+Xe EU Debug (eudebug)
+===================================
+
+Overview
+========
+
+The EU Debug (eudebug) subsystem provides an interface for debugging
+Execution Unit (EU) workloads on Intel Xe GPUs, independently from
+CPU process debugging. This enables tools like GDB (via gdbserver-intelgt)
+to debug GPU compute workloads through the Level-Zero Debug API.
+In practice, this means that SYCL/DPC++ workloads submitted by the
+compute-runtime can be debugged interactively at this time.
+
+
+Connection Establishment
+========================
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_eudebug.c
+   :doc: Connection Establishment
+
+Events
+======
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_eudebug.c
+   :doc: Events
+
+
+Base Event Header
+-----------------
+
+The debugger receives events by reading from the eudebug file descriptor
+using an read_event ioctl. Every event has :c:type:`drm_xe_eudebug_event`
+as a header
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event
+
+Resource Event Types
+--------------------
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event_vm
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 2b7e4afa9b54..768d07ae4af0 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -18,6 +18,25 @@
 #include "xe_macros.h"
 #include "xe_vm.h"
 
+/**
+ * DOC: Connection Establishment
+ *
+ * To debug a target DRM client, the debugger must first establish
+ * a connection using :c:type:`drm_xe_eudebug_connect`.
+ *
+ */
+
+/**
+ * DOC: Events
+ *
+ * Resource creation/destruction and hardware state changes are
+ * delivered as events for debugger.
+ *
+ * To read an event, ioctl DRM_XE_EUDEBUG_IOCTL_READ_EVENT command
+ * can be issued with prefilled :c:type:`drm_xe_eudebug_event` as argument.
+ *
+ */
+
 /*
  * If there is no detected event read by userspace, during this period, assume
  * userspace problem and disconnect debugger to allow forward progress.
@@ -812,6 +831,17 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 	return ret;
 }
 
+/**
+ * xe_eudebug_ioctl - Issue a command to eudebug interface
+ *
+ * @file : eudebug file (returned from connect)
+ * @cmd : cmd
+ * @arg : arguments depending on cmd
+ *
+ * Issue a eudebug command
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
 static long xe_eudebug_ioctl(struct file *file,
 			     unsigned int cmd,
 			     unsigned long arg)
@@ -1016,6 +1046,17 @@ void xe_eudebug_init(struct xe_device *xe)
 	drm_warn(&xe->drm, "eudebug disabled, init fail: %d\n", err);
 }
 
+/**
+ * xe_eudebug_connect_ioctl - Connect to eudebug interface
+ * @dev : DRM device
+ * @data : ioctl data, (struct drm_xe_eudebug_connect)
+ * @file : DRM file
+ *
+ * Connect to the eudebug interface.
+ *
+ * Return: eudebug filedesriptor on success, negative error code on failure.
+ *
+ */
 int xe_eudebug_connect_ioctl(struct drm_device *dev,
 			     void *data,
 			     struct drm_file *file)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 03/22] drm/xe/eudebug: Add connection establishment documentation
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 01/22] drm/xe/eudebug: Introduce eudebug interface Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 02/22] drm/xe/eudebug: Add documentation Mika Kuoppala
@ 2026-02-23 14:02 ` Mika Kuoppala
  2026-02-23 14:02 ` [PATCH 04/22] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:02 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

Add documentation for connecting to a target DRM Xe client
for debugging.

Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/xe_eudebug.rst | 11 +++++
 drivers/gpu/drm/xe/xe_eudebug.c     | 70 +++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index ff7fbc403ebb..c21fa7c47ab8 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -21,6 +21,17 @@ Connection Establishment
 .. kernel-doc:: drivers/gpu/drm/xe/xe_eudebug.c
    :doc: Connection Establishment
 
+File Descriptor Acquisition Methods
+-----------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_eudebug.c
+   :doc: File Descriptor Acquisition Methods
+
+Security Model
+--------------
+.. kernel-doc:: drivers/gpu/drm/xe/xe_eudebug.c
+   :doc: Security Model
+
 Events
 ======
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 768d07ae4af0..fa81b7a314f6 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -24,6 +24,76 @@
  * To debug a target DRM client, the debugger must first establish
  * a connection using :c:type:`drm_xe_eudebug_connect`.
  *
+ * The debug target's DRM client file descriptor is passed into the
+ * connect ioctl via :c:member:`drm_xe_eudebug_connect.fd`.
+ * This is the fd that the target process obtained from open('/dev/dri/cardX').
+ *
+ * This file descriptor must be a valid DRM client fd in the debugger's
+ * (calling) process context. For debugging an Xe DRM client in
+ * another process, pidfd_getfd() can be used to acquire a duplicate
+ * of the file descriptor from the target process. See
+ * :ref:`File Descriptor Acquisition Methods <fd_acquisition_methods>` for
+ * details on how to obtain the target's fd.
+ *
+ */
+
+/**
+ * DOC: Security Model
+ *
+ * If you are inside the same process, you can connect to your own DRM client
+ * by simply passing its fd to drm_xe_eudebug_connect.
+ *
+ * The security model for connecting to a remote process uses
+ * file descriptor access via pidfd_getfd(). The kernel enforces
+ * credentials with ptrace_may_access(). So in order to connect to a
+ * remote DRM client, the same ptrace_may_access() checks apply
+ * as in CPU process debugging with gdb.
+ *
+ */
+
+/**
+ * DOC: File Descriptor Acquisition Methods
+ * .. _fd_acquisition_methods:
+ *
+ * There are multiple ways to get the target DRM client fd for
+ * another process:
+ *
+ * Unix domain socket
+ *     The debugger can receive the DRM client fd from the target via a Unix
+ *     domain socket using SCM_RIGHTS ancillary data. This is the standard
+ *     mechanism for passing file descriptors between processes, but requires
+ *     coordination to establish the socket connection.
+ *
+ * Pipe
+ *     The debugger can acquire the DRM client fd through a pipe from the target.
+ *     This requires coordination between the processes to establish
+ *     a pipe to deliver the fd.
+ *
+ * Fork
+ *     If the debugger spawns the target process, the DRM client fd can be
+ *     inherited across fork(). The debugger opens the DRM device before
+ *     forking, and the child process inherits the fd. This is useful
+ *     when the debugger launches the target, similar to how gdb launches
+ *     programs for debugging.
+ *
+ * Ptrace
+ *     The debugger can attach to the target process using ptrace. It can
+ *     intercept the return value of the target's open() or drmOpen() call
+ *     by inspecting registers at syscall exit to catch the fd when it is
+ *     created. Alternatively, if the target has already opened the device,
+ *     the debugger can read the target's memory using PTRACE_PEEKDATA to
+ *     locate the stored fd value in a known variable or data structure.
+ *     Once the fd number is known, pidfd_getfd() can be used to acquire a
+ *     duplicate in the debugger's process context.
+ *
+ * Procfs
+ *     The debugger can look through /proc/<targetpid>/fd for file descriptors
+ *     and inspect /proc/<targetpid>/fdinfo to see which of those
+ *     are for an Xe DRM client.
+ *
+ * Brute force
+ *     The debugger can traverse /proc/<targetpid>/fd descriptors
+ *     and try to connect to each to see if it succeeds.
  */
 
 /**
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 04/22] drm/xe/eudebug: Introduce discovery for resources
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (2 preceding siblings ...)
  2026-02-23 14:02 ` [PATCH 03/22] drm/xe/eudebug: Add connection establishment documentation Mika Kuoppala
@ 2026-02-23 14:02 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 05/22] drm/xe/eudebug: Introduce exec_queue events Mika Kuoppala
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:02 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala, Dominik Grzegorzek

A debugger connection can occur after a client has created
and destroyed an arbitrary number of resources. To support
this, we need to relay all currently existing resources to
the debugger. The client is held on selected ioctls until
this discovery process, executed by a workqueue, is complete.

This patch is based on discovery work by Maciej Patelczyk
for the i915 driver.

v2: - use rw_semaphore to block DRM ioctls during discovery (Matthew)
    - only lock according to ioctl at play (Dominik)

v4: - s/discovery_lock/ioctl_lock
    - change lock to be per xe_file as is connections

Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Co-developed-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Acked-by: Matthew Brost <matthew.brost@intel.com> #locking
---
 drivers/gpu/drm/xe/xe_device.c        |  13 +++-
 drivers/gpu/drm/xe/xe_device.h        |  42 +++++++++++
 drivers/gpu/drm/xe/xe_device_types.h  |   6 ++
 drivers/gpu/drm/xe/xe_eudebug.c       | 100 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug_types.h |   7 ++
 5 files changed, 164 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 0e7b978d7efb..39a70aaaa253 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -111,6 +111,7 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file)
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 	mutex_init(&xef->eudebug.lock);
 	INIT_LIST_HEAD(&xef->eudebug.target_link);
+	init_rwsem(&xef->eudebug.ioctl_lock);
 #endif
 
 	file->driver_priv = xef;
@@ -236,8 +237,12 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 	ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
 	ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
-	if (ret >= 0)
+	if (ret >= 0) {
+		bool lock = xe_eudebug_discovery_lock(file, cmd);
 		ret = drm_ioctl(file, cmd, arg);
+		if (lock)
+			xe_eudebug_discovery_unlock(file, cmd);
+	}
 
 	return ret;
 }
@@ -254,8 +259,12 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
 
 	ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
 	ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
-	if (ret >= 0)
+	if (ret >= 0) {
+		bool lock = xe_eudebug_discovery_lock(file, cmd);
 		ret = drm_compat_ioctl(file, cmd, arg);
+		if (lock)
+			xe_eudebug_discovery_unlock(file, cmd);
+	}
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index 39464650533b..79fbaf01dad7 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -7,6 +7,7 @@
 #define _XE_DEVICE_H_
 
 #include <drm/drm_util.h>
+#include <drm/drm_ioctl.h>
 
 #include "xe_device_types.h"
 #include "xe_gt_types.h"
@@ -220,4 +221,45 @@ struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid);
 #define LNL_FLUSH_WORK(wrk__) \
 	flush_work(wrk__)
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+static inline int xe_eudebug_needs_ioctl_lock(const unsigned int cmd)
+{
+	const unsigned int xe_cmd = DRM_IOCTL_NR(cmd) - DRM_COMMAND_BASE;
+
+	switch (xe_cmd) {
+	case DRM_XE_VM_CREATE:
+	case DRM_XE_VM_DESTROY:
+	case DRM_XE_VM_BIND:
+	case DRM_XE_EXEC_QUEUE_CREATE:
+	case DRM_XE_EXEC_QUEUE_DESTROY:
+		return 1;
+	}
+
+	return 0;
+}
+
+static inline bool xe_eudebug_discovery_lock(struct file *file, unsigned int cmd)
+{
+	struct drm_file *file_priv = file->private_data;
+	struct xe_file *xef = file_priv->driver_priv;
+
+	if (!xe_eudebug_needs_ioctl_lock(cmd))
+		return false;
+
+	down_read(&xef->eudebug.ioctl_lock);
+	return true;
+}
+
+static inline void xe_eudebug_discovery_unlock(struct file *file, unsigned int cmd)
+{
+	struct drm_file *file_priv = file->private_data;
+	struct xe_file *xef = file_priv->driver_priv;
+
+	up_read(&xef->eudebug.ioctl_lock);
+}
+#else
+static inline bool xe_eudebug_discovery_lock(struct file *file, unsigned int cmd) { return false; }
+static inline void xe_eudebug_discovery_unlock(struct file *file, unsigned int cmd) { }
+#endif /* CONFIG_DRM_XE_EUDEBUG */
+
 #endif
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 1a2beffa9498..21e749c6a635 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -582,6 +582,9 @@ struct xe_device {
 
 		/** @eudebug.lock: protects state and targets */
 		struct mutex lock;
+
+		/** @wq: used for client discovery */
+		struct workqueue_struct *wq;
 	} eudebug;
 #endif
 };
@@ -657,6 +660,9 @@ struct xe_file {
 
 		/** @target_link: link into xe_device.eudebug.targets */
 		struct list_head target_link;
+
+		/** @eudebug.ioctl_lock syncing ioctl access */
+		struct rw_semaphore ioctl_lock;
 	} eudebug;
 #endif
 };
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index fa81b7a314f6..48f1e218aa63 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -35,6 +35,10 @@
  * :ref:`File Descriptor Acquisition Methods <fd_acquisition_methods>` for
  * details on how to obtain the target's fd.
  *
+ * After a successful connection, all existing resources that the target Xe DRM
+ * client already has are sent as events, as if they had been
+ * created right after the connection. This is referred to as discovery.
+ *
  */
 
 /**
@@ -242,6 +246,8 @@ static void xe_eudebug_free(struct kref *ref)
 	struct xe_eudebug *d = container_of(ref, typeof(*d), ref);
 	struct drm_xe_eudebug_event *event;
 
+	WARN_ON(work_pending(&d->discovery_work));
+
 	xe_assert(d->xe, xe_eudebug_detached(d));
 
 	while (kfifo_get(&d->events.fifo, &event))
@@ -303,6 +309,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
 	}
 	mutex_unlock(&d->target.lock);
 
+	flush_work(&d->discovery_work);
+
 	if (!target)
 		return false;
 
@@ -334,7 +342,7 @@ static int _xe_eudebug_disconnect(struct xe_eudebug *d,
 })
 
 static struct xe_eudebug *
-xe_eudebug_get(struct xe_file *xef)
+xe_eudebug_get_nolock(struct xe_file *xef)
 {
 	struct xe_eudebug *d;
 
@@ -347,7 +355,8 @@ xe_eudebug_get(struct xe_file *xef)
 	if (!d)
 		return NULL;
 
-	if (xe_eudebug_detached(d)) {
+	if (xe_eudebug_detached(d) ||
+	    !completion_done(&d->discovery)) {
 		xe_eudebug_put(d);
 		return NULL;
 	}
@@ -355,6 +364,14 @@ xe_eudebug_get(struct xe_file *xef)
 	return d;
 }
 
+static struct xe_eudebug *
+xe_eudebug_get(struct xe_file *xef)
+{
+	lockdep_assert_held(&xef->eudebug.ioctl_lock);
+
+	return xe_eudebug_get_nolock(xef);
+}
+
 static int xe_eudebug_queue_event(struct xe_eudebug *d,
 				  struct drm_xe_eudebug_event *event)
 {
@@ -578,6 +595,8 @@ static int xe_eudebug_remove_handle(struct xe_eudebug *d, int type, void *p,
 {
 	int ret;
 
+	XE_WARN_ON(!completion_done(&d->discovery));
+
 	ret = _xe_eudebug_remove_handle(d, type, p, seqno);
 
 	eu_dbg(d, "handle type %d handle %p removed: %d\n", type, p, ret);
@@ -709,6 +728,66 @@ void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm)
 	xe_eudebug_event_put(d, vm_destroy_event(d, xef, vm));
 }
 
+static struct xe_file *xe_eudebug_target_get(struct xe_eudebug *d)
+{
+	struct xe_file *xef = NULL;
+
+	mutex_lock(&d->target.lock);
+	if (d->target.xef)
+		xef = xe_file_get(d->target.xef);
+	mutex_unlock(&d->target.lock);
+
+	return xef;
+}
+
+static void discover_client(struct xe_eudebug *d)
+{
+	struct xe_file *xef;
+	struct xe_vm *vm;
+	unsigned long i;
+	unsigned int vm_count = 0;
+	int err = 0;
+
+	xef = xe_eudebug_target_get(d);
+	if (!xef)
+		return;
+
+	down_write(&xef->eudebug.ioctl_lock);
+
+	eu_dbg(d, "Discovery start for %lld", d->session);
+
+	xa_for_each(&xef->vm.xa, i, vm) {
+		err = vm_create_event(d, xef, vm);
+		if (err)
+			break;
+		vm_count++;
+	}
+
+	complete_all(&d->discovery);
+
+	eu_dbg(d, "Discovery end for %lld: %d", d->session, err);
+
+	up_write(&xef->eudebug.ioctl_lock);
+
+	if (vm_count)
+		eu_dbg(d, "Discovery found %u vms", vm_count);
+
+	xe_file_put(xef);
+}
+
+static void discovery_work_fn(struct work_struct *work)
+{
+	struct xe_eudebug *d = container_of(work, typeof(*d),
+					    discovery_work);
+
+	if (xe_eudebug_detached(d))
+		complete_all(&d->discovery);
+	else
+		discover_client(d);
+
+	xe_eudebug_put(d);
+}
+
 static int add_debugger(struct xe_device *xe, struct xe_eudebug *d,
 			struct drm_file *target)
 {
@@ -919,6 +998,10 @@ static long xe_eudebug_ioctl(struct file *file,
 	struct xe_eudebug * const d = file->private_data;
 	long ret;
 
+	if (cmd != DRM_XE_EUDEBUG_IOCTL_READ_EVENT &&
+	    !completion_done(&d->discovery))
+		return -EBUSY;
+
 	switch (cmd) {
 	case DRM_XE_EUDEBUG_IOCTL_READ_EVENT:
 		ret = xe_eudebug_read_event(d, arg,
@@ -980,9 +1063,11 @@ xe_eudebug_connect(struct xe_device *xe,
 	mutex_init(&d->target.lock);
 	init_waitqueue_head(&d->events.write_done);
 	init_waitqueue_head(&d->events.read_done);
+	init_completion(&d->discovery);
 
 	spin_lock_init(&d->events.lock);
 	INIT_KFIFO(d->events.fifo);
+	INIT_WORK(&d->discovery_work, discovery_work_fn);
 
 	err = xe_eudebug_resources_init(d);
 	if (XE_IOCTL_DBG(xe, err))
@@ -998,6 +1083,9 @@ xe_eudebug_connect(struct xe_device *xe,
 		goto err_detach;
 	}
 
+	kref_get(&d->ref);
+	queue_work(xe->eudebug.wq, &d->discovery_work);
+
 	eu_dbg(d, "connected session %lld", d->session);
 
 	return fd;
@@ -1089,6 +1177,7 @@ static void xe_eudebug_sysfs_fini(void *arg)
 void xe_eudebug_init(struct xe_device *xe)
 {
 	struct drm_device *dev = &xe->drm;
+	struct workqueue_struct *wq;
 	int err;
 
 	INIT_LIST_HEAD(&xe->eudebug.targets);
@@ -1099,6 +1188,13 @@ void xe_eudebug_init(struct xe_device *xe)
 	if (err)
 		goto out_err;
 
+	wq = drmm_alloc_ordered_workqueue(dev, "xe-eudebug", 0);
+	if (IS_ERR(wq)) {
+		err = PTR_ERR(wq);
+		goto out_err;
+	}
+	xe->eudebug.wq = wq;
+
 	err = sysfs_create_file(&dev->dev->kobj,
 				&dev_attr_enable_eudebug.attr);
 	if (err)
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index a73eb6c98b02..d7625e55d711 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -17,6 +17,7 @@
 
 struct xe_device;
 struct task_struct;
+struct workqueue_struct;
 
 /**
  * enum xe_eudebug_state - eudebug capability state
@@ -96,6 +97,12 @@ struct xe_eudebug {
 	/** @session: session number for this connection (for logs) */
 	u64 session;
 
+	/** @discovery: completion to wait for discovery */
+	struct completion discovery;
+
+	/** @discovery_work: worker to discover resources for target_task */
+	struct work_struct discovery_work;
+
 	/** @events: kfifo queue of to-be-delivered events */
 	struct {
 		/** @lock: guards access to fifo */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 05/22] drm/xe/eudebug: Introduce exec_queue events
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (3 preceding siblings ...)
  2026-02-23 14:02 ` [PATCH 04/22] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 06/22] drm/xe: Add EUDEBUG_ENABLE exec queue property Mika Kuoppala
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Dominik Grzegorzek, Mika Kuoppala

From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>

Add events to inform the debugger about the creation and destruction of
exec_queues. Use user engine class types instead of the internal
xe_engine_class enum in exec_queue events. During discovery, only advertise
exec_queues with render or compute class,excluding others.

v2: - Only track long running queues
    - Checkpatch (Tilak)
v3: __counted_by added
v4: - use helpers for filtering engines (Mika)

Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/xe_eudebug.rst   |   3 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 209 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h       |   7 +
 drivers/gpu/drm/xe/xe_eudebug_types.h |   7 +-
 drivers/gpu/drm/xe/xe_exec_queue.c    |   5 +
 drivers/gpu/drm/xe/xe_hw_engine.h     |  14 ++
 include/uapi/drm/xe_drm_eudebug.h     |  26 ++++
 7 files changed, 266 insertions(+), 5 deletions(-)

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index c21fa7c47ab8..e191e2c8ded4 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -54,3 +54,6 @@ Resource Event Types
 
 .. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
    :identifiers: drm_xe_eudebug_event_vm
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event_exec_queue
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 48f1e218aa63..d8ea7378d328 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -15,6 +15,8 @@
 #include "xe_device.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
+#include "xe_exec_queue.h"
+#include "xe_hw_engine.h"
 #include "xe_macros.h"
 #include "xe_vm.h"
 
@@ -470,6 +472,28 @@ __find_handle(struct xe_eudebug_resource *r,
 	return h;
 }
 
+static int find_handle(struct xe_eudebug *d,
+		       const int type,
+		       const void *p)
+{
+	const u64 key = (uintptr_t)p;
+	struct xe_eudebug_resource *r;
+	struct xe_eudebug_handle *h;
+	int id;
+
+	if (XE_WARN_ON(!key))
+		return -EINVAL;
+
+	r = resource_from_type(d, type);
+
+	mutex_lock(&d->target.lock);
+	h = __find_handle(r, key);
+	id = h ? h->id : -ENOENT;
+	mutex_unlock(&d->target.lock);
+
+	return id;
+}
+
 static int _xe_eudebug_add_handle(struct xe_eudebug *d,
 				  int type,
 				  void *p,
@@ -728,6 +752,174 @@ void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm)
 	xe_eudebug_event_put(d, vm_destroy_event(d, xef, vm));
 }
 
+static const u16 xe_to_user_engine_class[] = {
+	[XE_ENGINE_CLASS_RENDER] = DRM_XE_ENGINE_CLASS_RENDER,
+	[XE_ENGINE_CLASS_COPY] = DRM_XE_ENGINE_CLASS_COPY,
+	[XE_ENGINE_CLASS_VIDEO_DECODE] = DRM_XE_ENGINE_CLASS_VIDEO_DECODE,
+	[XE_ENGINE_CLASS_VIDEO_ENHANCE] = DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE,
+	[XE_ENGINE_CLASS_COMPUTE] = DRM_XE_ENGINE_CLASS_COMPUTE,
+};
+
+static int send_exec_queue_event(struct xe_eudebug *d, u32 flags,
+				 u64 vm_handle, u64 exec_queue_handle,
+				 enum xe_engine_class class,
+				 u32 width, u64 *lrc_handles, u64 seqno)
+{
+	struct drm_xe_eudebug_event *event;
+	struct drm_xe_eudebug_event_exec_queue *e;
+	const u32 sz = struct_size(e, lrc_handle, width);
+	const u32 xe_engine_class = xe_to_user_engine_class[class];
+
+	if (!xe_engine_supports_eudebug(class))
+		return -EINVAL;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE,
+					seqno, flags, sz);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	e->vm_handle = vm_handle;
+	e->exec_queue_handle = exec_queue_handle;
+	e->engine_class = xe_engine_class;
+	e->width = width;
+
+	memcpy(e->lrc_handle, lrc_handles, width);
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int exec_queue_create_event(struct xe_eudebug *d,
+				   struct xe_file *xef, struct xe_exec_queue *q)
+{
+	int h_vm, h_queue;
+	u64 h_lrc[XE_HW_ENGINE_MAX_INSTANCE], seqno;
+	int i;
+	int ret;
+
+	if (!xe_exec_queue_is_lr(q))
+		return 0;
+
+	h_vm = find_handle(d, XE_EUDEBUG_RES_TYPE_VM, q->vm);
+	if (h_vm < 0)
+		return h_vm;
+
+	if (XE_WARN_ON(q->width >= XE_HW_ENGINE_MAX_INSTANCE))
+		return -EINVAL;
+
+	for (i = 0; i < q->width; i++) {
+		int h, ret;
+
+		ret = _xe_eudebug_add_handle(d,
+					     XE_EUDEBUG_RES_TYPE_LRC,
+					     q->lrc[i],
+					     NULL,
+					     &h);
+
+		if (ret < 0 && ret != -EEXIST)
+			return ret;
+
+		XE_WARN_ON(!h);
+
+		h_lrc[i] = h;
+	}
+
+	h_queue = xe_eudebug_add_handle(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, q, &seqno);
+	if (h_queue <= 0)
+		return h_queue;
+
+	/* No need to cleanup for added handles on error as if we fail
+	 * we disconnect
+	 */
+
+	ret = send_exec_queue_event(d, DRM_XE_EUDEBUG_EVENT_CREATE,
+				    h_vm, h_queue, q->class,
+				    q->width, h_lrc, seqno);
+
+	if (ret)
+		eu_dbg(d, "send_exec_queue_event create error %d", ret);
+
+	return ret;
+}
+
+static int exec_queue_destroy_event(struct xe_eudebug *d,
+				    struct xe_file *xef,
+				    struct xe_exec_queue *q)
+{
+	int h_vm, h_queue;
+	u64 h_lrc[XE_HW_ENGINE_MAX_INSTANCE], seqno;
+	int i;
+	int ret;
+
+	if (!xe_exec_queue_is_lr(q))
+		return 0;
+
+	h_vm = find_handle(d, XE_EUDEBUG_RES_TYPE_VM, q->vm);
+	if (h_vm < 0)
+		return h_vm;
+
+	if (XE_WARN_ON(q->width >= XE_HW_ENGINE_MAX_INSTANCE))
+		return -EINVAL;
+
+	h_queue = xe_eudebug_remove_handle(d,
+					   XE_EUDEBUG_RES_TYPE_EXEC_QUEUE,
+					   q,
+					   &seqno);
+	if (h_queue <= 0)
+		return h_queue;
+
+	for (i = 0; i < q->width; i++) {
+		ret = _xe_eudebug_remove_handle(d,
+						XE_EUDEBUG_RES_TYPE_LRC,
+						q->lrc[i],
+						NULL);
+		if (ret < 0 && ret != -ENOENT)
+			return ret;
+
+		XE_WARN_ON(!ret);
+
+		h_lrc[i] = ret;
+	}
+
+	ret = send_exec_queue_event(d, DRM_XE_EUDEBUG_EVENT_DESTROY,
+				    h_vm, h_queue, q->class,
+				    q->width, h_lrc, seqno);
+
+	if (ret)
+		eu_dbg(d, "send_exec_queue_event destroy error %d\n", ret);
+
+	return ret;
+}
+
+void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q)
+{
+	struct xe_eudebug *d;
+
+	if (!xe_engine_supports_eudebug(q->class))
+		return;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, exec_queue_create_event(d, xef, q));
+}
+
+void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q)
+{
+	struct xe_eudebug *d;
+
+	if (!xe_engine_supports_eudebug(q->class))
+		return;
+
+	d = xe_eudebug_get(xef);
+	if (!d)
+		return;
+
+	xe_eudebug_event_put(d, exec_queue_destroy_event(d, xef, q));
+}
+
 static struct xe_file *xe_eudebug_target_get(struct xe_eudebug *d)
 {
 	struct xe_file *xef = NULL;
@@ -743,9 +935,10 @@ static struct xe_file *xe_eudebug_target_get(struct xe_eudebug *d)
 static void discover_client(struct xe_eudebug *d)
 {
 	struct xe_file *xef;
+	struct xe_exec_queue *q;
 	struct xe_vm *vm;
 	unsigned long i;
-	unsigned int vm_count = 0;
+	unsigned int vm_count = 0, eq_count = 0;
 	int err = 0;
 
 	xef = xe_eudebug_target_get(d);
@@ -763,14 +956,24 @@ static void discover_client(struct xe_eudebug *d)
 		vm_count++;
 	}
 
+	xa_for_each(&xef->exec_queue.xa, i, q) {
+		if (!xe_engine_supports_eudebug(q->class))
+			continue;
+
+		err = exec_queue_create_event(d, xef, q);
+		if (err)
+			break;
+	}
+
 	complete_all(&d->discovery);
 
 	eu_dbg(d, "Discovery end for %lld: %d", d->session, err);
 
 	up_write(&xef->eudebug.ioctl_lock);
 
-	if (vm_count)
-		eu_dbg(d, "Discovery found %u vms", vm_count);
+	if (vm_count || eq_count)
+		eu_dbg(d, "Discovery found %u vms, %u exec_queues",
+		       vm_count, eq_count);
 
 	xe_file_put(xef);
 }
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 22fbb2ff24da..10480a226fac 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -13,6 +13,7 @@ struct drm_file;
 struct xe_device;
 struct xe_file;
 struct xe_vm;
+struct xe_exec_queue;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -46,6 +47,9 @@ void xe_eudebug_vm_create(struct xe_file *xef, struct xe_vm *vm);
 void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm);
 int xe_eudebug_enable(struct xe_device *xe, bool enable);
 
+void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q);
+void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
@@ -60,6 +64,9 @@ static inline void xe_eudebug_file_close(struct xe_file *xef) { }
 static inline void xe_eudebug_vm_create(struct xe_file *xef, struct xe_vm *vm) { }
 static inline void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm) { }
 
+static inline void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q) { }
+static inline void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q) { }
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif /* _XE_EUDEBUG_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index d7625e55d711..9c111c483778 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -33,7 +33,7 @@ enum xe_eudebug_state {
 };
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
-#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_VM
+#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE
 
 /**
  * struct xe_eudebug_handle - eudebug resource handle
@@ -61,7 +61,9 @@ struct xe_eudebug_resource {
 };
 
 #define XE_EUDEBUG_RES_TYPE_VM		0
-#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_VM + 1)
+#define XE_EUDEBUG_RES_TYPE_EXEC_QUEUE	1
+#define XE_EUDEBUG_RES_TYPE_LRC		2
+#define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_LRC + 1)
 
 /**
  * struct xe_eudebug - Top level struct for eudebug: the connection
@@ -126,3 +128,4 @@ struct xe_eudebug {
 };
 
 #endif /* _XE_EUDEBUG_TYPES_H_ */
+
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index f8980cb7293d..24badaef9847 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -29,6 +29,7 @@
 #include "xe_trace.h"
 #include "xe_vm.h"
 #include "xe_pxp.h"
+#include "xe_eudebug.h"
 
 /**
  * DOC: Execution Queue
@@ -1247,6 +1248,8 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
 
 	args->exec_queue_id = id;
 
+	xe_eudebug_exec_queue_create(xef, q);
+
 	return 0;
 
 kill_exec_queue:
@@ -1435,6 +1438,8 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
 	if (q->vm && q->hwe->hw_engine_group)
 		xe_hw_engine_group_del_exec_queue(q->hwe->hw_engine_group, q);
 
+	xe_eudebug_exec_queue_destroy(xef, q);
+
 	xe_exec_queue_kill(q);
 
 	trace_xe_exec_queue_close(q);
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.h b/drivers/gpu/drm/xe/xe_hw_engine.h
index 6b5f9fa2a594..d8781bf79547 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.h
+++ b/drivers/gpu/drm/xe/xe_hw_engine.h
@@ -79,4 +79,18 @@ enum xe_force_wake_domains xe_hw_engine_to_fw_domain(struct xe_hw_engine *hwe);
 void xe_hw_engine_mmio_write32(struct xe_hw_engine *hwe, struct xe_reg reg, u32 val);
 u32 xe_hw_engine_mmio_read32(struct xe_hw_engine *hwe, struct xe_reg reg);
 
+static inline bool xe_engine_supports_eudebug(const enum xe_engine_class ec)
+{
+	if (ec == XE_ENGINE_CLASS_COMPUTE ||
+	    ec == XE_ENGINE_CLASS_RENDER)
+		return true;
+
+	return false;
+}
+
+static inline bool xe_hw_engine_has_eudebug(const struct xe_hw_engine *hwe)
+{
+	return xe_engine_supports_eudebug(hwe->class);
+}
+
 #endif
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index cdb4e4af4879..c9f21283ffdf 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -45,6 +45,7 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_NONE		0
 #define DRM_XE_EUDEBUG_EVENT_READ		1
 #define DRM_XE_EUDEBUG_EVENT_VM			2
+#define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE		3
 
 	/** @flags: Flags */
 	__u16 flags;
@@ -73,6 +74,31 @@ struct drm_xe_eudebug_event_vm {
 	__u64 vm_handle;
 };
 
+/**
+ * struct drm_xe_eudebug_event_exec_queue - Exec Queue resource event
+ *
+ * Resource creation/destruction event for an Exec Queue
+ */
+struct drm_xe_eudebug_event_exec_queue {
+	/** @base: base event */
+	struct drm_xe_eudebug_event base;
+
+	/** @vm_handle: the vm handle this exec queue belongs to */
+	__u64 vm_handle;
+
+	/** @exec_queue_handle: unique handle for this exec queue */
+	__u64 exec_queue_handle;
+
+	/** @engine_class: engine class for the exec queue */
+	__u32 engine_class;
+
+	/** @width: width of exec queue, how many lrcs (handles) it has */
+	__u32 width;
+
+	/** @lrc_handle: array of lrc handles */
+	__u64 lrc_handle[];
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 06/22] drm/xe: Add EUDEBUG_ENABLE exec queue property
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (4 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 05/22] drm/xe/eudebug: Introduce exec_queue events Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 07/22] drm/xe/eudebug: Mark guc contexts as debuggable Mika Kuoppala
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Dominik Grzegorzek, Mika Kuoppala

From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>

This patch introduces an immutable eudebug property for exec_queues,
using a flags value to enable eudebug-specific features. For now, the
engine LRC uses this flag to enable the runalone hardware feature.
Runalone ensures that only one hardware engine in a group
[rcs0, ccs0-3] is active on a tile.

v2: - check CONFIG_DRM_XE_EUDEBUG and LR mode (Matthew)
    - disable preempt (Dominik)
    - lrc_create remove from engine init

Cc: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/xe_eudebug.c          |  4 +--
 drivers/gpu/drm/xe/xe_exec_queue.c       | 46 +++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_exec_queue.h       |  2 ++
 drivers/gpu/drm/xe/xe_exec_queue_types.h |  7 ++++
 drivers/gpu/drm/xe/xe_lrc.c              | 10 ++++++
 include/uapi/drm/xe_drm.h                |  2 ++
 6 files changed, 68 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index d8ea7378d328..a89363544e35 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -798,7 +798,7 @@ static int exec_queue_create_event(struct xe_eudebug *d,
 	int i;
 	int ret;
 
-	if (!xe_exec_queue_is_lr(q))
+	if (!xe_exec_queue_is_debuggable(q))
 		return 0;
 
 	h_vm = find_handle(d, XE_EUDEBUG_RES_TYPE_VM, q->vm);
@@ -852,7 +852,7 @@ static int exec_queue_destroy_event(struct xe_eudebug *d,
 	int i;
 	int ret;
 
-	if (!xe_exec_queue_is_lr(q))
+	if (!xe_exec_queue_is_debuggable(q))
 		return 0;
 
 	h_vm = find_handle(d, XE_EUDEBUG_RES_TYPE_VM, q->vm);
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 24badaef9847..53cb6634248e 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -293,6 +293,9 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags)
 	if (!(exec_queue_flags & EXEC_QUEUE_FLAG_KERNEL))
 		flags |= XE_LRC_CREATE_USER_CTX;
 
+	if (q->eudebug_flags & EXEC_QUEUE_EUDEBUG_FLAG_ENABLE)
+		flags |= XE_LRC_CREATE_RUNALONE;
+
 	err = q->ops->init(q);
 	if (err)
 		return err;
@@ -851,6 +854,45 @@ static int exec_queue_set_multi_queue_priority(struct xe_device *xe, struct xe_e
 	return q->ops->set_multi_queue_priority(q, value);
 }
 
+static int exec_queue_set_eudebug(struct xe_device *xe, struct xe_exec_queue *q,
+				  u64 value)
+{
+	const u64 known_flags = DRM_XE_EXEC_QUEUE_EUDEBUG_FLAG_ENABLE;
+
+	if (XE_IOCTL_DBG(xe, (q->class != XE_ENGINE_CLASS_RENDER &&
+			      q->class != XE_ENGINE_CLASS_COMPUTE)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, (value & ~known_flags)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)))
+		return -EOPNOTSUPP;
+
+	if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_lr(q)))
+		return -EINVAL;
+	/*
+	 * We want to explicitly set the global feature if
+	 * property is set.
+	 */
+	if (XE_IOCTL_DBG(xe,
+			 !(value & DRM_XE_EXEC_QUEUE_EUDEBUG_FLAG_ENABLE)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !xe_eudebug_is_enabled(xe)))
+		return -EPERM;
+
+	q->eudebug_flags = EXEC_QUEUE_EUDEBUG_FLAG_ENABLE;
+	q->sched_props.preempt_timeout_us = 0;
+
+	return 0;
+}
+
+int xe_exec_queue_is_debuggable(struct xe_exec_queue *q)
+{
+	return q->eudebug_flags & EXEC_QUEUE_EUDEBUG_FLAG_ENABLE;
+}
+
 typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe,
 					     struct xe_exec_queue *q,
 					     u64 value);
@@ -863,6 +905,7 @@ static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = {
 	[DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP] = exec_queue_set_multi_group,
 	[DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY] =
 							exec_queue_set_multi_queue_priority,
+	[DRM_XE_EXEC_QUEUE_SET_PROPERTY_EUDEBUG] = exec_queue_set_eudebug,
 };
 
 int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data,
@@ -947,7 +990,8 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe,
 			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE &&
 			 ext.property != DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE &&
 			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP &&
-			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY))
+			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY &&
+			 ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_EUDEBUG))
 		return -EINVAL;
 
 	idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs));
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index c9e3a7c2d249..299c61fe09c1 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -178,4 +178,6 @@ static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q)
 	return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q);
 }
 
+int xe_exec_queue_is_debuggable(struct xe_exec_queue *q);
+
 #endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 3791fed34ffa..f186ea77dbf4 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -141,6 +141,13 @@ struct xe_exec_queue {
 	 */
 	unsigned long flags;
 
+	/**
+	 * @eudebug_flags: immutable eudebug flags for this exec queue.
+	 * Set up with DRM_XE_EXEC_QUEUE_SET_PROPERTY_EUDEBUG.
+	 */
+#define EXEC_QUEUE_EUDEBUG_FLAG_ENABLE		BIT(0)
+	unsigned long eudebug_flags;
+
 	union {
 		/** @multi_gt_list: list head for VM bind engines if multi-GT */
 		struct list_head multi_gt_list;
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index 9590b4605952..e9a41b401edf 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -1593,6 +1593,16 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
 	if (err)
 		goto err_lrc_finish;
 
+	if (init_flags & XE_LRC_CREATE_RUNALONE) {
+		u32 ctx_control = xe_lrc_read_ctx_reg(lrc, CTX_CONTEXT_CONTROL);
+
+		drm_dbg(&xe->drm, "read CTX_CONTEXT_CONTROL: 0x%x\n", ctx_control);
+		ctx_control |= _MASKED_BIT_ENABLE(CTX_CTRL_RUN_ALONE);
+		drm_dbg(&xe->drm, "written CTX_CONTEXT_CONTROL: 0x%x\n", ctx_control);
+
+		xe_lrc_write_ctx_reg(lrc, CTX_CONTEXT_CONTROL, ctx_control);
+	}
+
 	return 0;
 
 err_lrc_finish:
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index 97b479b51c73..630f6c15779b 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -1325,6 +1325,8 @@ struct drm_xe_exec_queue_create {
 #define   DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP		4
 #define     DRM_XE_MULTI_GROUP_CREATE				(1ull << 63)
 #define   DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY	5
+#define   DRM_XE_EXEC_QUEUE_SET_PROPERTY_EUDEBUG		6
+#define     DRM_XE_EXEC_QUEUE_EUDEBUG_FLAG_ENABLE		(1 << 0)
 	/** @extensions: Pointer to the first extension struct, if any */
 	__u64 extensions;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 07/22] drm/xe/eudebug: Mark guc contexts as debuggable
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (5 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 06/22] drm/xe: Add EUDEBUG_ENABLE exec queue property Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 08/22] drm/xe: Introduce ADD_DEBUG_DATA and REMOVE_DEBUG_DATA vm bind ops Mika Kuoppala
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala, Lucas De Marchi, Daniele Ceraolo Spurio,
	Jan Sokolowski, Dominik Grzegorzek

We need to inform to guc which contexts are debuggable
as their handling is different from ordinary contexts.

v2: void return, use xe_gt_dbg, no need for lrc (Matt)
v3: add the workaround enabling (Daniele)
v4: version needed to 70.49.4
v5: bail out early before registering eq (Daniele)
v6: export the guc action for future (Mika)

Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Jan Sokolowski <jan.sokolowski@intel.com>
Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
---
 drivers/gpu/drm/xe/abi/guc_actions_abi.h |  5 ++++
 drivers/gpu/drm/xe/abi/guc_klvs_abi.h    |  1 +
 drivers/gpu/drm/xe/xe_exec_queue.c       |  5 ++++
 drivers/gpu/drm/xe/xe_guc.c              | 17 ++++++++++++
 drivers/gpu/drm/xe/xe_guc.h              |  3 +++
 drivers/gpu/drm/xe/xe_guc_ads.c          | 17 ++++++++++++
 drivers/gpu/drm/xe/xe_guc_submit.c       | 34 ++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_guc_submit.h       |  1 +
 drivers/gpu/drm/xe/xe_wa_oob.rules       |  3 ++-
 9 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
index 83a6e7794982..918247d1f526 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
@@ -161,6 +161,7 @@ enum xe_guc_action {
 	XE_GUC_ACTION_NOTIFY_FLUSH_LOG_BUFFER_TO_FILE = 0x8003,
 	XE_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED = 0x8004,
 	XE_GUC_ACTION_NOTIFY_EXCEPTION = 0x8005,
+	XE_GUC_ACTION_EU_KERNEL_DEBUG = 0x8006,
 	XE_GUC_ACTION_TEST_G2G_SEND = 0xF001,
 	XE_GUC_ACTION_TEST_G2G_RECV = 0xF002,
 	XE_GUC_ACTION_LIMIT
@@ -284,4 +285,8 @@ enum xe_guc_g2g_type {
 /* invalid type for XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR */
 #define XE_GUC_CAT_ERR_TYPE_INVALID 0xdeadbeef
 
+enum  xe_guc_eu_kernel_debug_request_type {
+	XE_GUC_EU_KERNEL_DEBUG_ENABLE = 0x3,
+};
+
 #endif
diff --git a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
index e33bd622ab44..4970d664d5d9 100644
--- a/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_klvs_abi.h
@@ -496,6 +496,7 @@ enum xe_guc_klv_ids {
 	GUC_WA_KLV_WAKE_POWER_DOMAINS_FOR_OUTBOUND_MMIO					= 0x900a,
 	GUC_WA_KLV_RESET_BB_STACK_PTR_ON_VF_SWITCH					= 0x900b,
 	GUC_WA_KLV_RESTORE_UNSAVED_MEDIA_CONTROL_REG					= 0x900c,
+	GUC_WA_KLV_RESET_DEP_ENGINES_ON_DEBUG_CTX_SWITCH				= 0x900d,
 };
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 53cb6634248e..d7931c41da90 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -19,6 +19,7 @@
 #include "xe_gt.h"
 #include "xe_gt_sriov_pf.h"
 #include "xe_gt_sriov_vf.h"
+#include "xe_guc.h"
 #include "xe_hw_engine_class_sysfs.h"
 #include "xe_hw_engine_group.h"
 #include "xe_irq.h"
@@ -858,6 +859,7 @@ static int exec_queue_set_eudebug(struct xe_device *xe, struct xe_exec_queue *q,
 				  u64 value)
 {
 	const u64 known_flags = DRM_XE_EXEC_QUEUE_EUDEBUG_FLAG_ENABLE;
+	struct xe_guc *guc = &q->gt->uc.guc;
 
 	if (XE_IOCTL_DBG(xe, (q->class != XE_ENGINE_CLASS_RENDER &&
 			      q->class != XE_ENGINE_CLASS_COMPUTE)))
@@ -869,6 +871,9 @@ static int exec_queue_set_eudebug(struct xe_device *xe, struct xe_exec_queue *q,
 	if (XE_IOCTL_DBG(xe, !IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)))
 		return -EOPNOTSUPP;
 
+	if (XE_IOCTL_DBG(xe, !xe_guc_has_debug_contexts(guc)))
+		return -EOPNOTSUPP;
+
 	if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_lr(q)))
 		return -EINVAL;
 	/*
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index cbbb4d665b8f..58a624dacde9 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -1822,6 +1822,23 @@ bool xe_guc_using_main_gamctrl_queues(struct xe_guc *guc)
 	return GT_VER(gt) >= 35;
 }
 
+bool xe_guc_has_debug_contexts(struct xe_guc *guc)
+{
+	const struct xe_uc_fw_version required = XE_UC_FW_VERSION_DEBUG_CONTEXTS;
+	struct xe_uc_fw_version *version = &guc->fw.versions.found[XE_UC_FW_VER_RELEASE];
+	struct xe_gt *gt = guc_to_gt(guc);
+
+	if (MAKE_GUC_VER_STRUCT(*version) < MAKE_GUC_VER_STRUCT(required)) {
+		xe_gt_info(gt,
+			   "debug context unsupported in GuC interface v%u.%u.%u, need v%u.%u.%u or higher\n",
+			   version->major, version->minor, version->patch, required.major,
+			   required.minor, required.patch);
+		return false;
+	}
+
+	return true;
+}
+
 #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
 #include "tests/xe_guc_g2g_test.c"
 #endif
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index 66e7edc70ed9..a516a479b81e 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -29,6 +29,8 @@
 #define GUC_FIRMWARE_VER_AT_LEAST(guc, ver...) \
 	xe_guc_fw_version_at_least((guc), MAKE_GUC_VER_ARGS(ver))
 
+#define XE_UC_FW_VERSION_DEBUG_CONTEXTS { .major = 70, .minor = 49, .patch = 4 }
+
 struct drm_printer;
 
 void xe_guc_comm_init_early(struct xe_guc *guc);
@@ -61,6 +63,7 @@ void xe_guc_stop(struct xe_guc *guc);
 int xe_guc_start(struct xe_guc *guc);
 void xe_guc_declare_wedged(struct xe_guc *guc);
 bool xe_guc_using_main_gamctrl_queues(struct xe_guc *guc);
+bool xe_guc_has_debug_contexts(struct xe_guc *guc);
 
 #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
 int xe_guc_g2g_test_notification(struct xe_guc *guc, u32 *payload, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index f4cbc030f4c8..1b84f4bd4236 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -361,6 +361,23 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
 		guc_waklv_enable(ads, NULL, 0, &offset, &remain,
 				 GUC_WORKAROUND_KLV_DISABLE_PSMI_INTERRUPTS_AT_C6_ENTRY_RESTORE_AT_EXIT);
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	if (XE_GT_WA(gt, 14022766366)) {
+		if (xe_guc_has_debug_contexts(&gt->uc.guc)) {
+			guc_waklv_enable(ads, NULL, 0, &offset, &remain,
+					 GUC_WA_KLV_RESET_DEP_ENGINES_ON_DEBUG_CTX_SWITCH);
+		} else  {
+			const struct xe_uc_fw_version required =
+				XE_UC_FW_VERSION_DEBUG_CONTEXTS;
+
+			xe_gt_info(gt, "eudebug needs GuC version %u.%u.%u or greater\n",
+				   required.major,
+				   required.minor,
+				   required.patch);
+		}
+	}
+#endif
+
 	size = guc_ads_waklv_size(ads) - remain;
 	if (!size)
 		return;
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index ca7aa4f358d0..b0035f029fcd 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -964,6 +964,37 @@ static void __register_exec_queue(struct xe_guc *guc,
 	xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
 }
 
+int xe_guc_action_eu_kernel_debug(struct xe_guc *guc, u32 id, u32 cmd)
+{
+	const u32 action[] = {
+		XE_GUC_ACTION_EU_KERNEL_DEBUG,
+		id,
+		cmd,
+		0, /* reserved */
+	};
+
+	return xe_guc_ct_send(&guc->ct, action,
+			      ARRAY_SIZE(action), 0, 0);
+}
+
+static void set_eu_kernel_debug(struct xe_exec_queue *q)
+{
+	struct xe_guc *guc = exec_queue_to_guc(q);
+	struct xe_gt *gt = guc_to_gt(guc);
+	int ret;
+
+	ret = xe_guc_action_eu_kernel_debug(guc, q->guc->id,
+					    XE_GUC_EU_KERNEL_DEBUG_ENABLE);
+
+	if (ret)
+		xe_gt_warn(gt,
+			   "GuC ctx=%u debug enabling failed with %d",
+			   q->guc->id, ret);
+	else
+		xe_gt_dbg(gt,
+			  "GuC ctx=%u enabled for debug", q->guc->id);
+}
+
 static void register_exec_queue(struct xe_exec_queue *q, int ctx_type)
 {
 	struct xe_guc *guc = exec_queue_to_guc(q);
@@ -1024,6 +1055,9 @@ static void register_exec_queue(struct xe_exec_queue *q, int ctx_type)
 
 	if (xe_exec_queue_is_multi_queue_secondary(q))
 		xe_guc_exec_queue_group_add(guc, q);
+
+	if (xe_exec_queue_is_debuggable(q))
+		set_eu_kernel_debug(q);
 }
 
 static u32 wq_space_until_wrap(struct xe_exec_queue *q)
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h
index b3839a90c142..d296e30f9e39 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit.h
@@ -55,5 +55,6 @@ void xe_guc_register_vf_exec_queue(struct xe_exec_queue *q, int ctx_type);
 bool xe_guc_has_registered_mlrc_queues(struct xe_guc *guc);
 
 int xe_guc_contexts_hwsp_rebase(struct xe_guc *guc, void *scratch);
+int xe_guc_action_eu_kernel_debug(struct xe_guc *guc, u32 id, u32 cmd);
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index ac08f94f90a1..5527c2cf4f81 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -73,6 +73,7 @@
 15015404425_disable	PLATFORM(PANTHERLAKE), MEDIA_STEP(B0, FOREVER)
 16026007364    MEDIA_VERSION(3000)
 14020316580    MEDIA_VERSION(1301)
-
 14025883347	MEDIA_VERSION_RANGE(1301, 3503)
 		GRAPHICS_VERSION_RANGE(2004, 3005)
+14022766366	GRAPHICS_VERSION_RANGE(2001, 2004)
+		GRAPHICS_VERSION_RANGE(3000, 3005)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 08/22] drm/xe: Introduce ADD_DEBUG_DATA and REMOVE_DEBUG_DATA vm bind ops
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (6 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 07/22] drm/xe/eudebug: Mark guc contexts as debuggable Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 09/22] drm/xe/eudebug: Introduce vm bind and vm bind debug data events Mika Kuoppala
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

From: Christoph Manszewski <christoph.manszewski@intel.com>

Make it possible to add and remove per vm debug data, which can be used
to annotate vm ranges (using pseudopaths) or to associate them with
a file which can carry arbitrary debug data (e.g. binary instruction to
code line mapping). The debug data is kept separe from the vmas. Each
address can be associated with only one debug data entry i.e. debug data
entries cannot overlap. Each entry is atomic so to remove it the
creation address and range has to be passed for removal.

For debug data manipulation only the 'op' and 'extensions' field from
'struct drm_xe_vm_bind_op' is used. All required parameters are passed
through 'struct drm_xe_vm_bind_op_ext_debug_data' and a valid instance
should be present in the extension chain pointed by the 'extensions'
field.

Debug data will be accessible through the eudebug event interface,
introduced in the following patch. An alternative way to access debug data
using debugfs, without relying on eudebug, will be proposed as a follow-up
to the eudebug series.

v2: enforce empty path on unmap (Joonas, Mika)

Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/Makefile              |   1 +
 drivers/gpu/drm/xe/xe_debug_data.c       | 314 +++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_debug_data.h       |  22 ++
 drivers/gpu/drm/xe/xe_debug_data_types.h |  25 ++
 drivers/gpu/drm/xe/xe_vm.c               | 157 +++++++++++-
 drivers/gpu/drm/xe/xe_vm_types.h         |  19 ++
 include/uapi/drm/xe_drm.h                |  64 +++++
 7 files changed, 596 insertions(+), 6 deletions(-)
 create mode 100644 drivers/gpu/drm/xe/xe_debug_data.c
 create mode 100644 drivers/gpu/drm/xe/xe_debug_data.h
 create mode 100644 drivers/gpu/drm/xe/xe_debug_data_types.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index eaf6b28a592c..9d30627805f7 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -87,6 +87,7 @@ xe-y += xe_bb.o \
 	xe_irq.o \
 	xe_late_bind_fw.o \
 	xe_lrc.o \
+	xe_debug_data.o \
 	xe_migrate.o \
 	xe_mmio.o \
 	xe_mmio_gem.o \
diff --git a/drivers/gpu/drm/xe/xe_debug_data.c b/drivers/gpu/drm/xe/xe_debug_data.c
new file mode 100644
index 000000000000..3c39b2457d6e
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_debug_data.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_debug_data.h"
+#include "xe_debug_data_types.h"
+#include "xe_vm.h"
+
+const char *xe_debug_data_pseudo_path_to_string(u64 pseudopath)
+{
+	switch (pseudopath) {
+	case DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_MODULE_AREA:
+		return "[module_area]";
+	case DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SBA_AREA:
+		return "[sba_area]";
+	case DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SIP_AREA:
+		return "[sip_area]";
+	default:
+		return "[unknown]";
+	}
+}
+
+static bool
+debug_data_overlaps(const struct drm_xe_vm_bind_op_ext_debug_data *a,
+		    const struct xe_debug_data *b)
+{
+	const u64 s1 = a->addr;
+	const u64 e1 = a->addr + a->range;
+	const u64 s2 = b->addr;
+	const u64 e2 = b->addr + b->range;
+
+	return (s1 < e2) && (s2 < e1);
+}
+
+static bool
+debug_data_matches(const struct drm_xe_vm_bind_op_ext_debug_data *a,
+		   const struct xe_debug_data *b)
+{
+	return (a->addr == b->addr) && (a->range == b->range);
+}
+
+static bool
+debug_data_is_empty(const struct drm_xe_vm_bind_op_ext_debug_data *dd)
+{
+	int i;
+
+	if (dd->flags)
+		return false;
+
+	if (dd->offset)
+		return false;
+
+	for (i = 0; i < PATH_MAX; i++)
+		if (dd->pathname[i])
+			return false;
+
+	return true;
+}
+
+static int xe_debug_data_check_add(struct xe_vm *vm,
+				   const struct drm_xe_vm_bind_op_ext_debug_data *ext)
+{
+	struct xe_device *xe = vm->xe;
+	struct xe_debug_data *i;
+
+	mutex_lock(&vm->debug_data.lock);
+	list_for_each_entry(i, &vm->debug_data.list, link) {
+		if (XE_IOCTL_DBG(xe, debug_data_overlaps(ext, i))) {
+			mutex_unlock(&vm->debug_data.lock);
+			return -EINVAL;
+		}
+	}
+	mutex_unlock(&vm->debug_data.lock);
+
+	return 0;
+}
+
+static int xe_debug_data_check_remove(struct xe_vm *vm,
+				      const struct drm_xe_vm_bind_op_ext_debug_data *ext)
+{
+	struct xe_device *xe = vm->xe;
+	struct xe_debug_data *i;
+	bool found = false;
+
+	if (XE_IOCTL_DBG(xe, !debug_data_is_empty(ext)))
+		return -EINVAL;
+
+	mutex_lock(&vm->debug_data.lock);
+	list_for_each_entry(i, &vm->debug_data.list, link) {
+		found = debug_data_matches(ext, i);
+		if (found)
+			break;
+	}
+	mutex_unlock(&vm->debug_data.lock);
+
+	if (XE_IOCTL_DBG(xe, !found)) {
+		drm_dbg(&xe->drm, "Debug data to remove not found for addr 0x%llx, range 0x%llx\n",
+			ext->addr, ext->range);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+int xe_debug_data_check_extension(struct xe_vm *vm, u32 operation, u64 extension)
+{
+	const u64 __user * const address = u64_to_user_ptr(extension);
+	struct drm_xe_vm_bind_op_ext_debug_data *ext;
+	struct xe_device *xe = vm->xe;
+	int ret;
+
+	if (XE_IOCTL_DBG(xe, operation != DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA &&
+			 operation != DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA))
+		return -EINVAL;
+
+	ext = kzalloc_obj(*ext, GFP_KERNEL);
+	if (!ext)
+		return -ENOMEM;
+
+	if (copy_from_user(ext, address, sizeof(*ext))) {
+		kfree(ext);
+		return -EFAULT;
+	}
+
+	if (XE_IOCTL_DBG(xe, ext->flags & ~DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) ||
+	    XE_IOCTL_DBG(xe, ext->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO &&
+			 ext->offset != 0) ||
+	    XE_IOCTL_DBG(xe, ext->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO &&
+			 (ext->pseudopath < DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_MODULE_AREA ||
+			  ext->pseudopath > DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SIP_AREA)) ||
+	    XE_IOCTL_DBG(xe, !(ext->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) &&
+			 strnlen(ext->pathname, PATH_MAX) >= PATH_MAX)) {
+		kfree(ext);
+		return -EINVAL;
+	}
+
+	ret = operation == DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA ?
+		xe_debug_data_check_add(vm, ext) :
+		xe_debug_data_check_remove(vm, ext);
+
+	kfree(ext);
+	return ret;
+}
+
+static int xe_debug_data_add(struct xe_vm *vm, struct xe_vma_op *vma_op,
+			     struct drm_xe_vm_bind_op_ext_debug_data *ext)
+{
+	struct xe_debug_data *dd;
+
+	vm_dbg(&vm->xe->drm,
+	       "ADD_DEBUG_DATA: addr=0x%016llx, range=0x%016llx, offset=0x%08x, flags=0x%016llx, path=%s\n",
+	       ext->addr, ext->range, ext->offset, ext->flags,
+	       (ext->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) ?
+	       xe_debug_data_pseudo_path_to_string(ext->pseudopath) : ext->pathname);
+
+	dd = kzalloc_obj(*dd, GFP_KERNEL);
+	if (!dd)
+		return -ENOMEM;
+
+	dd->addr = ext->addr;
+	dd->range = ext->range;
+	dd->flags = ext->flags;
+	dd->offset = ext->offset;
+
+	if (ext->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) {
+		dd->pseudopath = ext->pseudopath;
+	} else if (strscpy(dd->pathname, ext->pathname, PATH_MAX) < 0) {
+		kfree(dd);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vm->debug_data.lock);
+	list_add_tail(&dd->link, &vm->debug_data.list);
+	mutex_unlock(&vm->debug_data.lock);
+
+	memcpy(&vma_op->modify_debug_data.debug_data, dd, sizeof(*dd));
+
+	return 0;
+}
+
+static int xe_debug_data_remove(struct xe_vm *vm, struct xe_vma_op *vma_op,
+				struct drm_xe_vm_bind_op_ext_debug_data *ext)
+{
+	struct xe_debug_data *dd;
+
+	vm_dbg(&vm->xe->drm,
+	       "REMOVE_DEBUG_DATA: addr=0x%016llx, range=0x%016llx, offset=0x%08x, flags=0x%016llx, path=%s\n",
+	       ext->addr, ext->range, ext->offset, ext->flags,
+	       (ext->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) ?
+	       xe_debug_data_pseudo_path_to_string(ext->pseudopath) : ext->pathname);
+
+	mutex_lock(&vm->debug_data.lock);
+	list_for_each_entry(dd, &vm->debug_data.list, link) {
+		if (dd->addr == ext->addr && dd->range == ext->range) {
+			list_del(&dd->link);
+			memcpy(&vma_op->modify_debug_data.debug_data, dd, sizeof(*dd));
+			kfree(dd);
+			break;
+		}
+	}
+	mutex_unlock(&vm->debug_data.lock);
+
+	return 0;
+}
+
+int xe_debug_data_process_extension(struct xe_vm *vm, struct drm_gpuva_ops *ops, u32 operation,
+				    u64 extension)
+{
+	const u64 __user * const address = u64_to_user_ptr(extension);
+	struct drm_xe_vm_bind_op_ext_debug_data *ext;
+	struct xe_vma_op *vma_op;
+	struct drm_gpuva_op *op;
+	int ret;
+
+	ext = kzalloc_obj(*ext, GFP_KERNEL);
+	if (!ext)
+		return -ENOMEM;
+
+	if (copy_from_user(ext, address, sizeof(*ext))) {
+		kfree(ext);
+		return -EFAULT;
+	}
+
+	/* We expect only a single op for debug data */
+	op = drm_gpuva_first_op(ops);
+	if (op != drm_gpuva_last_op(ops))
+		drm_warn(&vm->xe->drm, "NOT POSSIBLE");
+
+	vma_op = gpuva_op_to_vma_op(op);
+
+	if (vma_op->subop == XE_VMA_SUBOP_ADD_DEBUG_DATA)
+		ret = xe_debug_data_add(vm, vma_op, ext);
+	else
+		ret = xe_debug_data_remove(vm, vma_op, ext);
+
+	kfree(ext);
+	return ret;
+}
+
+static int xe_debug_data_op_unwind_add(struct xe_vm *vm, struct xe_vma_op *vma_op)
+{
+	const struct xe_debug_data *op_data = &vma_op->modify_debug_data.debug_data;
+	struct xe_debug_data *dd;
+
+	vm_dbg(&vm->xe->drm,
+	       "Reverting debug data add: addr=0x%016llx, range=0x%016llx, offset=0x%08x, flags=0x%016llx, path=%s\n",
+	       op_data->addr, op_data->range, op_data->offset, op_data->flags,
+	       (op_data->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) ?
+	       xe_debug_data_pseudo_path_to_string(op_data->pseudopath) : op_data->pathname);
+
+	mutex_lock(&vm->debug_data.lock);
+	list_for_each_entry(dd, &vm->debug_data.list, link) {
+		if (dd->addr == op_data->addr && dd->range == op_data->range) {
+			list_del(&dd->link);
+			kfree(dd);
+			break;
+		}
+	}
+	mutex_unlock(&vm->debug_data.lock);
+
+	return 0;
+}
+
+static int xe_debug_data_op_unwind_remove(struct xe_vm *vm, struct xe_vma_op *vma_op)
+{
+	const struct xe_debug_data *op_data = &vma_op->modify_debug_data.debug_data;
+	struct xe_debug_data *dd;
+
+	vm_dbg(&vm->xe->drm,
+	       "Reverting debug data remove: addr=0x%016llx, range=0x%016llx, offset=0x%08x, flags=0x%016llx, path=%s\n",
+	       op_data->addr, op_data->range, op_data->offset, op_data->flags,
+	       (op_data->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO) ?
+	       xe_debug_data_pseudo_path_to_string(op_data->pseudopath) : op_data->pathname);
+
+	dd = kzalloc_obj(*dd, GFP_KERNEL);
+	if (!dd)
+		return -ENOMEM;
+
+	memcpy(dd, op_data, sizeof(*dd));
+
+	mutex_lock(&vm->debug_data.lock);
+	list_add_tail(&dd->link, &vm->debug_data.list);
+	mutex_unlock(&vm->debug_data.lock);
+
+	return 0;
+}
+
+int xe_debug_data_op_unwind(struct xe_vm *vm, struct xe_vma_op *vma_op)
+{
+	switch (vma_op->subop) {
+	case XE_VMA_SUBOP_ADD_DEBUG_DATA:
+		return xe_debug_data_op_unwind_add(vm, vma_op);
+	case XE_VMA_SUBOP_REMOVE_DEBUG_DATA:
+		return xe_debug_data_op_unwind_remove(vm, vma_op);
+	default:
+		drm_err(&vm->xe->drm, "Invalid debug data subop %d\n", vma_op->subop);
+		return -EINVAL;
+	}
+}
+
+int xe_debug_data_destroy(struct xe_vm *vm)
+{
+	struct xe_debug_data *dd, *tmp;
+
+	mutex_lock(&vm->debug_data.lock);
+	list_for_each_entry_safe(dd, tmp, &vm->debug_data.list, link) {
+		list_del(&dd->link);
+		kfree(dd);
+	}
+	mutex_unlock(&vm->debug_data.lock);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_debug_data.h b/drivers/gpu/drm/xe/xe_debug_data.h
new file mode 100644
index 000000000000..3436a7023920
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_debug_data.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_DEBUG_DATA_H_
+#define _XE_DEBUG_DATA_H_
+
+#include <linux/types.h>
+
+struct drm_gpuva_ops;
+struct xe_vm;
+struct xe_vma_op;
+
+const char *xe_debug_data_pseudo_path_to_string(u64 pseudopath);
+int xe_debug_data_check_extension(struct xe_vm *vm, u32 operation, u64 extension);
+int xe_debug_data_process_extension(struct xe_vm *vm, struct drm_gpuva_ops *ops, u32 operation,
+				    u64 extension);
+int xe_debug_data_op_unwind(struct xe_vm *vm, struct xe_vma_op *vma_op);
+int xe_debug_data_destroy(struct xe_vm *vm);
+
+#endif /* _XE_DEBUG_DATA_H_ */
diff --git a/drivers/gpu/drm/xe/xe_debug_data_types.h b/drivers/gpu/drm/xe/xe_debug_data_types.h
new file mode 100644
index 000000000000..a8b430af2275
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_debug_data_types.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_DEBUG_DATA_TYPES_H_
+#define _XE_DEBUG_DATA_TYPES_H_
+
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+struct xe_debug_data {
+	struct list_head link;
+	u64 addr;
+	u64 range;
+	u64 flags;
+	u32 offset;
+	union {
+		u64 pseudopath;
+		char pathname[PATH_MAX];
+	};
+};
+
+#endif /* _XE_DEBUG_DATA_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 64368110f517..7076c91f4dfc 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -24,6 +24,7 @@
 #include "regs/xe_gtt_defs.h"
 #include "xe_assert.h"
 #include "xe_bo.h"
+#include "xe_debug_data.h"
 #include "xe_device.h"
 #include "xe_drm_client.h"
 #include "xe_eudebug.h"
@@ -1557,6 +1558,9 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
 	for_each_tile(tile, xe, id)
 		xe_range_fence_tree_init(&vm->rftree[id]);
 
+	INIT_LIST_HEAD(&vm->debug_data.list);
+	mutex_init(&vm->debug_data.lock);
+
 	vm->pt_ops = &xelp_pt_ops;
 
 	/*
@@ -1858,6 +1862,8 @@ void xe_vm_close_and_put(struct xe_vm *vm)
 	for_each_tile(tile, xe, id)
 		xe_range_fence_tree_fini(&vm->rftree[id]);
 
+	xe_debug_data_destroy(vm);
+
 	xe_vm_put(vm);
 }
 
@@ -2200,6 +2206,7 @@ static void prep_vma_destroy(struct xe_vm *vm, struct xe_vma *vma,
 #if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)
 static void print_op(struct xe_device *xe, struct drm_gpuva_op *op)
 {
+	struct xe_vma_op *vma_op;
 	struct xe_vma *vma;
 
 	switch (op->op) {
@@ -2234,6 +2241,12 @@ static void print_op(struct xe_device *xe, struct drm_gpuva_op *op)
 		vm_dbg(&xe->drm, "PREFETCH: addr=0x%016llx, range=0x%016llx",
 		       (ULL)xe_vma_start(vma), (ULL)xe_vma_size(vma));
 		break;
+	case DRM_GPUVA_OP_DRIVER:
+		vma_op = gpuva_op_to_vma_op(op);
+		if (vma_op->subop != XE_VMA_SUBOP_ADD_DEBUG_DATA &&
+		    vma_op->subop != XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			drm_warn(&xe->drm, "Unexpected vma sub op: %d", vma_op->subop);
+		break;
 	default:
 		drm_warn(&xe->drm, "NOT POSSIBLE\n");
 	}
@@ -2278,12 +2291,13 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
 			 struct xe_bo *bo, u64 bo_offset_or_userptr,
 			 u64 addr, u64 range,
 			 u32 operation, u32 flags,
-			 u32 prefetch_region, u16 pat_index)
+			 u32 prefetch_region, u16 pat_index, u64 extensions)
 {
 	struct drm_gem_object *obj = bo ? &bo->ttm.base : NULL;
 	struct drm_gpuva_ops *ops;
 	struct drm_gpuva_op *__op;
 	struct drm_gpuvm_bo *vm_bo;
+	struct xe_vma_op *vma_op;
 	u64 range_start = addr;
 	u64 range_end = addr + range;
 	int err;
@@ -2337,6 +2351,24 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
 		drm_gpuvm_bo_put(vm_bo);
 		xe_bo_unlock(bo);
 		break;
+	case DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA:
+	case DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA:
+		ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+		if (!ops)
+			return ERR_PTR(-ENOMEM);
+
+		INIT_LIST_HEAD(&ops->list);
+		vma_op = kzalloc_obj(*vma_op, GFP_KERNEL);
+		if (!vma_op) {
+			kfree(ops);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		vma_op->base.op = DRM_GPUVA_OP_DRIVER;
+		vma_op->subop = operation == DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA ?
+			XE_VMA_SUBOP_ADD_DEBUG_DATA : XE_VMA_SUBOP_REMOVE_DEBUG_DATA;
+		list_add_tail(&vma_op->base.entry, &ops->list);
+		break;
 	default:
 		drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
 		ops = ERR_PTR(-EINVAL);
@@ -2609,6 +2641,11 @@ static int xe_vma_op_commit(struct xe_vm *vm, struct xe_vma_op *op)
 	case DRM_GPUVA_OP_PREFETCH:
 		op->flags |= XE_VMA_OP_COMMITTED;
 		break;
+	case DRM_GPUVA_OP_DRIVER:
+		if (op->subop != XE_VMA_SUBOP_ADD_DEBUG_DATA &&
+		    op->subop != XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			drm_warn(&vm->xe->drm, "Unexpected vma sub op: %d", op->subop);
+		break;
 	default:
 		drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
 	}
@@ -2808,6 +2845,11 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
 				xe_vma_ops_incr_pt_update_ops(vops, op->tile_mask, 1);
 
 			break;
+		case DRM_GPUVA_OP_DRIVER:
+			if (op->subop != XE_VMA_SUBOP_ADD_DEBUG_DATA &&
+			    op->subop != XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+				drm_warn(&vm->xe->drm, "Unexpected vma sub op: %d", op->subop);
+			break;
 		default:
 			drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
 		}
@@ -2870,6 +2912,13 @@ static void xe_vma_op_unwind(struct xe_vm *vm, struct xe_vma_op *op,
 	case DRM_GPUVA_OP_PREFETCH:
 		/* Nothing to do */
 		break;
+	case DRM_GPUVA_OP_DRIVER:
+		if (op->subop == XE_VMA_SUBOP_ADD_DEBUG_DATA ||
+		    op->subop == XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			xe_debug_data_op_unwind(vm, op);
+		else
+			drm_warn(&vm->xe->drm, "Unexpected vma sub op: %d", op->subop);
+		break;
 	default:
 		drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
 	}
@@ -3054,6 +3103,11 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
 					    exec);
 		break;
 	}
+	case DRM_GPUVA_OP_DRIVER:
+		if (op->subop != XE_VMA_SUBOP_ADD_DEBUG_DATA &&
+		    op->subop != XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			drm_warn(&vm->xe->drm, "Unexpected vma sub op: %d", op->subop);
+		break;
 	default:
 		drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
 	}
@@ -3293,6 +3347,11 @@ static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op,
 	case DRM_GPUVA_OP_PREFETCH:
 		vma_add_ufence(gpuva_to_vma(op->base.prefetch.va), ufence);
 		break;
+	case DRM_GPUVA_OP_DRIVER:
+		if (op->subop != XE_VMA_SUBOP_ADD_DEBUG_DATA &&
+		    op->subop != XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			drm_warn(&vm->xe->drm, "Unexpected vma sub op: %d", op->subop);
+		break;
 	default:
 		drm_warn(&vm->xe->drm, "NOT POSSIBLE\n");
 	}
@@ -3379,6 +3438,79 @@ ALLOW_ERROR_INJECTION(vm_bind_ioctl_ops_execute, ERRNO);
 #define XE_64K_PAGE_MASK 0xffffull
 #define ALL_DRM_XE_SYNCS_FLAGS (DRM_XE_SYNCS_FLAG_WAIT_FOR_OP)
 
+#define MAX_USER_EXTENSIONS	16
+
+typedef int (*xe_vm_bind_user_extension_check_fn)(struct xe_vm *vm, u32 operation, u64 extension);
+
+typedef int (*xe_vm_bind_user_extension_process_fn)(struct xe_vm *vm, struct drm_gpuva_ops *ops,
+						    u32 operation, u64 extension);
+
+static const xe_vm_bind_user_extension_check_fn vm_bind_extension_check_funcs[] = {
+	[XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA] = xe_debug_data_check_extension,
+};
+
+static const xe_vm_bind_user_extension_process_fn vm_bind_extension_process_funcs[] = {
+	[XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA] = xe_debug_data_process_extension,
+};
+
+#define MAX_USER_EXTENSIONS 16
+static int __vm_bind_op_user_extensions(struct xe_vm *vm, struct drm_gpuva_ops *ops,
+					u32 operation, u64 extensions)
+{
+	struct xe_device *xe = vm->xe;
+	int debug_data_count = 0;
+	int ext_count = 0;
+	int err = -1;
+
+	struct drm_xe_user_extension ext;
+
+	while (extensions) {
+		u64 __user *address = u64_to_user_ptr(extensions);
+
+		if (XE_IOCTL_DBG(xe, ++ext_count >= MAX_USER_EXTENSIONS))
+			return -E2BIG;
+
+		err = copy_from_user(&ext, address, sizeof(ext));
+		if (XE_IOCTL_DBG(xe, err))
+			return -EFAULT;
+
+		if (XE_IOCTL_DBG(xe, operation != DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA &&
+				     operation != DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA &&
+				     ext.name == XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA) ||
+		    XE_IOCTL_DBG(xe, ext.name == XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA &&
+				     ++debug_data_count > 1))
+			return -EINVAL;
+
+		if (XE_IOCTL_DBG(xe, ext.pad) ||
+		    XE_IOCTL_DBG(xe, ext.name > XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA))
+			return -EINVAL;
+
+		if (!ops)
+			err = vm_bind_extension_check_funcs[ext.name](vm, operation, extensions);
+		else
+			err = vm_bind_extension_process_funcs[ext.name](vm, ops, operation,
+									extensions);
+
+		if (XE_IOCTL_DBG(xe, err))
+			return err;
+
+		extensions = ext.next_extension;
+	}
+
+	return 0;
+}
+
+static int vm_bind_ioctl_check_user_extensions(struct xe_vm *vm, u32 operation, u64 extensions)
+{
+	return __vm_bind_op_user_extensions(vm, NULL, operation, extensions);
+}
+
+static int vm_bind_ioctl_process_user_extensions(struct xe_vm *vm, struct drm_gpuva_ops *ops,
+						 u32 operation, u64 extensions)
+{
+	return __vm_bind_op_user_extensions(vm, ops, operation, extensions);
+}
+
 static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
 				    struct drm_xe_vm_bind *args,
 				    struct drm_xe_vm_bind_op **bind_ops)
@@ -3429,6 +3561,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
 		bool is_cpu_addr_mirror = flags &
 			DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR;
 		u16 pat_index = (*bind_ops)[i].pat_index;
+		u64 extensions = (*bind_ops)[i].extensions;
 		u16 coh_mode;
 		bool comp_en;
 
@@ -3458,7 +3591,7 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
 			goto free_bind_ops;
 		}
 
-		if (XE_IOCTL_DBG(xe, op > DRM_XE_VM_BIND_OP_PREFETCH) ||
+		if (XE_IOCTL_DBG(xe, op > DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA) ||
 		    XE_IOCTL_DBG(xe, flags & ~SUPPORTED_FLAGS) ||
 		    XE_IOCTL_DBG(xe, obj && (is_null || is_cpu_addr_mirror)) ||
 		    XE_IOCTL_DBG(xe, obj_offset && (is_null ||
@@ -3502,10 +3635,16 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
 		    XE_IOCTL_DBG(xe, addr & ~PAGE_MASK) ||
 		    XE_IOCTL_DBG(xe, range & ~PAGE_MASK) ||
 		    XE_IOCTL_DBG(xe, !range &&
-				 op != DRM_XE_VM_BIND_OP_UNMAP_ALL)) {
+				 op != DRM_XE_VM_BIND_OP_UNMAP_ALL &&
+				 op != DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA &&
+				 op != DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA)) {
 			err = -EINVAL;
 			goto free_bind_ops;
 		}
+
+		err = vm_bind_ioctl_check_user_extensions(vm, op, extensions);
+		if (err)
+			goto free_bind_ops;
 	}
 
 	return 0;
@@ -3779,11 +3918,17 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 		u64 obj_offset = bind_ops[i].obj_offset;
 		u32 prefetch_region = bind_ops[i].prefetch_mem_region_instance;
 		u16 pat_index = bind_ops[i].pat_index;
+		u64 extensions = bind_ops[i].extensions;
 
 		ops[i] = vm_bind_ioctl_ops_create(vm, &vops, bos[i], obj_offset,
 						  addr, range, op, flags,
-						  prefetch_region, pat_index);
-		if (IS_ERR(ops[i])) {
+						  prefetch_region, pat_index, extensions);
+
+		if (!IS_ERR(ops[i]) && extensions) {
+			err = vm_bind_ioctl_process_user_extensions(vm, ops[i], op, extensions);
+			if (err)
+				goto unwind_ops;
+		} else if (IS_ERR(ops[i])) {
 			err = PTR_ERR(ops[i]);
 			ops[i] = NULL;
 			goto unwind_ops;
@@ -3891,7 +4036,7 @@ struct dma_fence *xe_vm_bind_kernel_bo(struct xe_vm *vm, struct xe_bo *bo,
 
 	ops = vm_bind_ioctl_ops_create(vm, &vops, bo, 0, addr, xe_bo_size(bo),
 				       DRM_XE_VM_BIND_OP_MAP, 0, 0,
-				       vm->xe->pat.idx[cache_lvl]);
+				       vm->xe->pat.idx[cache_lvl], 0);
 	if (IS_ERR(ops)) {
 		err = PTR_ERR(ops);
 		goto release_vm_lock;
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 43203e90ee3e..961e4969d696 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -15,6 +15,7 @@
 #include <linux/mmu_notifier.h>
 #include <linux/scatterlist.h>
 
+#include "xe_debug_data_types.h"
 #include "xe_device_types.h"
 #include "xe_pt_types.h"
 #include "xe_range_fence.h"
@@ -364,6 +365,12 @@ struct xe_vm {
 	bool batch_invalidate_tlb;
 	/** @xef: Xe file handle for tracking this VM's drm client */
 	struct xe_file *xef;
+
+	/** @debug_data: track debug_data mapped to vm */
+	struct {
+		struct list_head list;
+		struct mutex lock;
+	} debug_data;
 };
 
 /** struct xe_vma_op_map - VMA map operation */
@@ -430,6 +437,12 @@ struct xe_vma_op_prefetch_range {
 	struct drm_pagemap *dpagemap;
 };
 
+/** struct xe_vma_op_debug_data - debug data altering operation */
+struct xe_vma_op_modify_debug_data {
+	/** @debug_data: debug data associated with that operation */
+	struct xe_debug_data debug_data;
+};
+
 /** enum xe_vma_op_flags - flags for VMA operation */
 enum xe_vma_op_flags {
 	/** @XE_VMA_OP_COMMITTED: VMA operation committed */
@@ -446,6 +459,10 @@ enum xe_vma_subop {
 	XE_VMA_SUBOP_MAP_RANGE,
 	/** @XE_VMA_SUBOP_UNMAP_RANGE: Unmap range */
 	XE_VMA_SUBOP_UNMAP_RANGE,
+	/** @XE_VMA_SUBOP_ADD_DEBUG_DATA: Add debug data to vm */
+	XE_VMA_SUBOP_ADD_DEBUG_DATA,
+	/** @XE_VMA_SUBOP_REMOVE_DEBUG_DATA: Remove debug data from vm */
+	XE_VMA_SUBOP_REMOVE_DEBUG_DATA,
 };
 
 /** struct xe_vma_op - VMA operation */
@@ -474,6 +491,8 @@ struct xe_vma_op {
 		struct xe_vma_op_unmap_range unmap_range;
 		/** @prefetch_range: VMA prefetch range operation specific data */
 		struct xe_vma_op_prefetch_range prefetch_range;
+		/** @debug_data: debug_data operation specific data */
+		struct xe_vma_op_modify_debug_data modify_debug_data;
 	};
 };
 
diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h
index 630f6c15779b..7e5a391426b9 100644
--- a/include/uapi/drm/xe_drm.h
+++ b/include/uapi/drm/xe_drm.h
@@ -6,6 +6,8 @@
 #ifndef _UAPI_XE_DRM_H_
 #define _UAPI_XE_DRM_H_
 
+#include <linux/limits.h>
+
 #include "drm.h"
 
 #if defined(__cplusplus)
@@ -1003,6 +1005,63 @@ struct drm_xe_vm_destroy {
 	__u64 reserved[2];
 };
 
+/**
+ * struct drm_xe_vm_bind_op_ext_debug_data - debug data extension struct for
+ * :c:type:`drm_xe_vm_bind_op`
+ *
+ * The GPU VM can be annotated by issuing a bind operation with the
+ * :c:member:`drm_xe_vm_bind_op.op` set to %DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA or
+ * %DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA. Each such operation has to provide a
+ * :c:type:`drm_xe_vm_bind_op_ext_debug_data` extension, which describes the
+ * debug data to add or remove.
+ *
+ * This extension can either point to a file that contains relevant debug data
+ * or annotate the VM range with a pseudopath by setting the
+ * %DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO flag and providing one of the supported
+ * pseudopath values:
+ *  - %DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_MODULE_AREA
+ *  - %DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SBA_AREA
+ *  - %DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SIP_AREA
+ *
+ */
+struct drm_xe_vm_bind_op_ext_debug_data {
+	/** @base: Base user extension */
+	struct drm_xe_user_extension base;
+
+	/** @addr: Address of the debug data mapping */
+	__u64 addr;
+
+	/** @range: Range of the debug data mapping */
+	__u64 range;
+
+#define DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO (1 << 0)
+	/** @flags: Debug data flags */
+	__u64 flags;
+
+	/**
+	 * @offset: Offset into the debug data file, MBZ when
+	 * %DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO is set
+	 */
+	__u32 offset;
+
+	/** @reserved: Reserved */
+	__u32 reserved;
+
+	union {
+#define DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_MODULE_AREA	0x1
+#define DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SBA_AREA	0x2
+#define DRM_XE_VM_BIND_DEBUG_DATA_PSEUDO_SIP_AREA	0x3
+		/**
+		 * @pseudopath: Pseudopath used when
+		 * %DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO is set
+		 */
+		__u64 pseudopath;
+
+		/** @pathname: Path to the debug data file */
+		char pathname[PATH_MAX];
+	};
+};
+
 /**
  * struct drm_xe_vm_bind_op - run bind operations
  *
@@ -1012,6 +1071,8 @@ struct drm_xe_vm_destroy {
  *  - %DRM_XE_VM_BIND_OP_MAP_USERPTR
  *  - %DRM_XE_VM_BIND_OP_UNMAP_ALL
  *  - %DRM_XE_VM_BIND_OP_PREFETCH
+ *  - %DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA
+ *  - %DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA
  *
  * and the @flags can be:
  *  - %DRM_XE_VM_BIND_FLAG_READONLY - Setup the page tables as read-only
@@ -1055,6 +1116,7 @@ struct drm_xe_vm_destroy {
  *    the memory region advised by madvise.
  */
 struct drm_xe_vm_bind_op {
+#define	XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA 0
 	/** @extensions: Pointer to the first extension struct, if any */
 	__u64 extensions;
 
@@ -1146,6 +1208,8 @@ struct drm_xe_vm_bind_op {
 #define DRM_XE_VM_BIND_OP_MAP_USERPTR	0x2
 #define DRM_XE_VM_BIND_OP_UNMAP_ALL	0x3
 #define DRM_XE_VM_BIND_OP_PREFETCH	0x4
+#define DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA	0x5
+#define DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA	0x6
 	/** @op: Bind operation to perform */
 	__u32 op;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 09/22] drm/xe/eudebug: Introduce vm bind and vm bind debug data events
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (7 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 08/22] drm/xe: Introduce ADD_DEBUG_DATA and REMOVE_DEBUG_DATA vm bind ops Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 10/22] drm/xe/eudebug: Add UFENCE events with acks Mika Kuoppala
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

From: Christoph Manszewski <christoph.manszewski@intel.com>

This patch adds events to track the bind ioctl and associated debug data add
and remove operations. As a single bind can involve multiple operations and
may fail mid-process.

Add bind event to signal to debugger when bind operation is executed.
Further add debug data add and remove operations so debugger
can keep track of regions where they reside. The bind event is
important as we will want to include ufence event further
in the series and tie it to this bind.

Only deliver bind+operations to the debugger if the vm bind
op execution chain succeeds.

v2: - avoid sending bind if there is no operations (Mika)
    - documentation (Christoph, Mika)

Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
Co-developed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/xe_eudebug.rst   |   6 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 226 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h       |   7 +
 drivers/gpu/drm/xe/xe_eudebug_types.h |   2 +-
 drivers/gpu/drm/xe/xe_vm.c            |   4 +
 include/uapi/drm/xe_drm_eudebug.h     |  85 ++++++++++
 6 files changed, 325 insertions(+), 5 deletions(-)

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index e191e2c8ded4..1f743f1d6f2a 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -57,3 +57,9 @@ Resource Event Types
 
 .. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
    :identifiers: drm_xe_eudebug_event_exec_queue
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event_vm_bind
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event_vm_bind_op_debug_data
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index a89363544e35..d0f19dbdf55b 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -12,6 +12,7 @@
 #include <uapi/drm/xe_drm.h>
 
 #include "xe_assert.h"
+#include "xe_debug_data_types.h"
 #include "xe_device.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
@@ -920,6 +921,167 @@ void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q)
 	xe_eudebug_event_put(d, exec_queue_destroy_event(d, xef, q));
 }
 
+static int send_vm_bind_event(struct xe_eudebug *d,
+			      struct xe_vm *vm,
+			      u64 vm_handle,
+			      u32 bind_flags,
+			      u32 num_ops, u64 *seqno)
+{
+	struct drm_xe_eudebug_event_vm_bind *e;
+	struct drm_xe_eudebug_event *event;
+	const u32 sz = sizeof(*e);
+	const u32 base_flags = DRM_XE_EUDEBUG_EVENT_STATE_CHANGE;
+
+	*seqno = atomic_long_inc_return(&d->events.seqno);
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM_BIND,
+					*seqno, base_flags, sz);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	e->vm_handle = vm_handle;
+	e->flags = bind_flags;
+	e->num_bind_ops = num_ops;
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int vm_bind_event(struct xe_eudebug *d,
+			 struct xe_vm *vm,
+			 u32 flags,
+			 u32 num_ops,
+			 u64 *seqno)
+{
+	int h_vm;
+
+	h_vm = find_handle(d, XE_EUDEBUG_RES_TYPE_VM, vm);
+	if (h_vm < 0)
+		return h_vm;
+
+	return send_vm_bind_event(d, vm, h_vm, flags,
+				  num_ops, seqno);
+}
+
+static int vm_bind_op_event(struct xe_eudebug *d,
+			    struct xe_vm *vm,
+			    const u32 flags,
+			    const u64 bind_ref_seqno,
+			    const u64 num_extensions,
+			    struct xe_debug_data *debug_data,
+			    u64 *op_seqno)
+{
+	struct drm_xe_eudebug_event_vm_bind_op_debug_data *e;
+	struct drm_xe_eudebug_event *event;
+	const u32 sz = sizeof(*e);
+
+	*op_seqno = atomic_long_inc_return(&d->events.seqno);
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA,
+					*op_seqno, flags, sz);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+
+	e->vm_bind_ref_seqno = bind_ref_seqno;
+	e->num_extensions = num_extensions;
+	e->addr = debug_data->addr;
+	e->range = debug_data->range;
+	e->flags = debug_data->flags;
+	e->offset = debug_data->offset;
+
+	if (debug_data->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO)
+		e->pseudopath = debug_data->pseudopath;
+	else
+		strscpy(e->pathname, debug_data->pathname, PATH_MAX);
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int vm_bind_op(struct xe_eudebug *d, struct xe_vm *vm,
+		      const u32 flags, const u64 bind_ref_seqno,
+		      struct xe_debug_data *debug_data)
+{
+	u64 op_seqno = 0;
+	u64 num_extensions = 0;
+	int ret;
+
+	ret = vm_bind_op_event(d, vm, flags, bind_ref_seqno, num_extensions,
+			       debug_data, &op_seqno);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void xe_eudebug_vm_bind_execute(struct xe_vm *vm,
+				struct xe_vma_ops *ops)
+{
+	struct xe_eudebug *d;
+	struct xe_vma_op *op;
+	u64 bind_seqno = 0;
+	u32 num_ops;
+	int err;
+
+	if (!xe_vm_in_lr_mode(vm))
+		return;
+
+	d = xe_eudebug_get(vm->xef);
+	if (!d)
+		return;
+
+	num_ops = 0;
+	list_for_each_entry(op, &ops->list, link) {
+		if (op->base.op != DRM_GPUVA_OP_DRIVER)
+			continue;
+
+		if (op->subop == XE_VMA_SUBOP_ADD_DEBUG_DATA ||
+		    op->subop == XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			num_ops++;
+	}
+
+	lockdep_assert_held_write(&vm->lock);
+
+	if (!num_ops) {
+		xe_eudebug_put(d);
+		return;
+	}
+
+	err = vm_bind_event(d, vm, 0,
+			    num_ops, &bind_seqno);
+	if (err)
+		goto out_err;
+
+	list_for_each_entry(op, &ops->list, link) {
+		u32 flags = 0;
+
+		if (op->base.op != DRM_GPUVA_OP_DRIVER)
+			continue;
+
+		if (op->subop == XE_VMA_SUBOP_ADD_DEBUG_DATA)
+			flags = DRM_XE_EUDEBUG_EVENT_CREATE;
+
+		if (op->subop == XE_VMA_SUBOP_REMOVE_DEBUG_DATA)
+			flags = DRM_XE_EUDEBUG_EVENT_DESTROY;
+
+		if (!flags)
+			continue;
+
+		err = vm_bind_op(d, vm, flags, bind_seqno,
+				 &op->modify_debug_data.debug_data);
+		if (err)
+			goto out_err;
+	}
+
+out_err:
+	if (err)
+		xe_eudebug_disconnect(d, err);
+
+	xe_eudebug_put(d);
+}
+
 static struct xe_file *xe_eudebug_target_get(struct xe_eudebug *d)
 {
 	struct xe_file *xef = NULL;
@@ -932,19 +1094,67 @@ static struct xe_file *xe_eudebug_target_get(struct xe_eudebug *d)
 	return xef;
 }
 
+static int vm_discover_binds(struct xe_eudebug *d, struct xe_vm *vm)
+{
+	struct xe_debug_data *dd;
+	struct list_head *pos;
+	unsigned int ops, count;
+	u64 ref_seqno;
+	int err;
+
+	if (list_empty(&vm->debug_data.list))
+		return 0;
+
+	count = 0;
+	list_for_each(pos, &vm->debug_data.list)
+		count++;
+
+	ops = count;
+	ref_seqno = 0;
+	err = vm_bind_event(d, vm, 0, ops, &ref_seqno);
+	if (err) {
+		eu_dbg(d, "vm_bind_event error %d\n", err);
+		return err;
+	}
+
+	list_for_each_entry(dd, &vm->debug_data.list, link) {
+		err = vm_bind_op(d, vm, DRM_XE_EUDEBUG_EVENT_CREATE, ref_seqno, dd);
+		if (err) {
+			eu_dbg(d, "vm_bind_op error %d\n", err);
+			return err;
+		}
+
+		ops--;
+	}
+
+	XE_WARN_ON(ops);
+
+	return ops ? -EIO : count;
+}
+
 static void discover_client(struct xe_eudebug *d)
 {
 	struct xe_file *xef;
 	struct xe_exec_queue *q;
 	struct xe_vm *vm;
 	unsigned long i;
-	unsigned int vm_count = 0, eq_count = 0;
+	unsigned int vm_count = 0, eq_count = 0, ops_count = 0;
 	int err = 0;
 
 	xef = xe_eudebug_target_get(d);
 	if (!xef)
 		return;
 
+	/*
+	 * xe_eudebug ref is taken for discovery worker. It will
+	 * hold target xe_file ref and xe_file holds vm and exec_queue
+	 * refs.
+	 *
+	 * The relevant ioctls through xe_file are through
+	 * down_read(&xef->eudebug.lock). That means we can peek inside
+	 * the resources without taking their respective locks by
+	 * taking write lock.
+	 */
 	down_write(&xef->eudebug.ioctl_lock);
 
 	eu_dbg(d, "Discovery start for %lld", d->session);
@@ -954,6 +1164,12 @@ static void discover_client(struct xe_eudebug *d)
 		if (err)
 			break;
 		vm_count++;
+
+		err = vm_discover_binds(d, vm);
+		if (err < 0)
+			break;
+
+		ops_count += err;
 	}
 
 	xa_for_each(&xef->exec_queue.xa, i, q) {
@@ -963,6 +1179,8 @@ static void discover_client(struct xe_eudebug *d)
 		err = exec_queue_create_event(d, xef, q);
 		if (err)
 			break;
+
+		eq_count++;
 	}
 
 	complete_all(&d->discovery);
@@ -971,9 +1189,9 @@ static void discover_client(struct xe_eudebug *d)
 
 	up_write(&xef->eudebug.ioctl_lock);
 
-	if (vm_count || eq_count)
-		eu_dbg(d, "Discovery found %u vms, %u exec_queues",
-		       vm_count, eq_count);
+	if (vm_count || eq_count || ops_count)
+		eu_dbg(d, "Discovery found %u vms, %u exec_queues, %u bind_ops",
+		       vm_count, eq_count, ops_count);
 
 	xe_file_put(xef);
 }
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 10480a226fac..9c622362c0f7 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -10,10 +10,14 @@
 
 struct drm_device;
 struct drm_file;
+struct xe_debug_data;
 struct xe_device;
 struct xe_file;
 struct xe_vm;
+struct xe_vma;
+struct xe_vma_ops;
 struct xe_exec_queue;
+struct xe_user_fence;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -50,6 +54,8 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable);
 void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q);
 void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q);
 
+void xe_eudebug_vm_bind_execute(struct xe_vm *vm, struct xe_vma_ops *ops);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
@@ -67,6 +73,7 @@ static inline void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm)
 static inline void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q) { }
 static inline void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q) { }
 
+static inline void xe_eudebug_vm_bind_execute(struct xe_vm *vm, struct xe_vma_ops *ops) { }
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif /* _XE_EUDEBUG_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 9c111c483778..1bd6eb2aa102 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -33,7 +33,7 @@ enum xe_eudebug_state {
 };
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
-#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE
+#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA
 
 /**
  * struct xe_eudebug_handle - eudebug resource handle
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index 7076c91f4dfc..e09ebd2e357c 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -3407,6 +3407,10 @@ static struct dma_fence *vm_bind_ioctl_ops_execute(struct xe_vm *vm,
 		xe_vm_set_validation_exec(vm, &exec);
 		fence = ops_execute(vm, vops);
 		xe_vm_set_validation_exec(vm, NULL);
+
+		if (!IS_ERR(fence) || PTR_ERR(fence) == -ENODATA)
+			xe_eudebug_vm_bind_execute(vm, vops);
+
 		if (IS_ERR(fence)) {
 			if (PTR_ERR(fence) == -ENODATA)
 				vm_bind_ioctl_ops_fini(vm, vops, NULL);
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index c9f21283ffdf..230c8cdcbd21 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -46,6 +46,8 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_READ		1
 #define DRM_XE_EUDEBUG_EVENT_VM			2
 #define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE		3
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND		4
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
 
 	/** @flags: Flags */
 	__u16 flags;
@@ -99,6 +101,89 @@ struct drm_xe_eudebug_event_exec_queue {
 	__u64 lrc_handle[];
 };
 
+/**
+ * struct drm_xe_eudebug_event_vm_bind - VM Bind Event
+ *
+ * When the client (debuggee) calls the vm_bind_ioctl with the
+ * DRM_XE_VM_BIND_OP_[ADD|REMOVE]_DEBUG_DATA operation, the following event
+ * sequence will be created (for the debugger)::
+ *
+ *  ┌───────────────────────┐
+ *  │  EVENT_VM_BIND        ├──────────────────┬─┬┄┐
+ *  └───────────────────────┘                  │ │ ┊
+ *      ┌──────────────────────────────────┐   │ │ ┊
+ *      │ EVENT_VM_BIND_OP_DEBUG_DATA #1   ├───┘ │ ┊
+ *      └──────────────────────────────────┘     │ ┊
+ *                      ...                      │ ┊
+ *      ┌──────────────────────────────────┐     │ ┊
+ *      │ EVENT_VM_BIND_OP_DEBUG_DATA #n   ├─────┘ ┊
+ *      └──────────────────────────────────┘       ┊
+ *                                                 ┊
+ *      ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐       ┊
+ *      ┊ EVENT_UFENCE                     ├┄┄┄┄┄┄┄┘
+ *      └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
+ *
+ * All the events below VM_BIND will reference the VM_BIND
+ * they associate with, by field .vm_bind_ref_seqno.
+ */
+struct drm_xe_eudebug_event_vm_bind {
+	/** @base: Base event */
+	struct drm_xe_eudebug_event base;
+
+	/** @vm_handle: VM handle for this bind */
+	__u64 vm_handle;
+
+	/** @flags: Bind specific flags */
+	__u32 flags;
+
+	/** @num_bind_ops: How many [ADD|REMOVE]_DEBUG_DATA operations this bind has */
+	__u32 num_bind_ops;
+};
+
+/**
+ * struct drm_xe_eudebug_event_vm_bind_op_debug_data - VM Bind Op Debug Data Event
+ *
+ * When the target drm client issues a vm bind, each operation of type
+ * %DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA or %DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA
+ * will generate an event of this type. For reference see
+ * :c:type:`drm_xe_vm_bind_op_ext_debug_data`.
+ */
+struct drm_xe_eudebug_event_vm_bind_op_debug_data {
+	/** @base: Base event */
+	struct drm_xe_eudebug_event base;
+
+	/** @vm_bind_ref_seqno: Parent :c:member:`drm_xe_eudebug_event_vm_bind.base.seqno` */
+	__u64 vm_bind_ref_seqno;
+
+	/** @num_extensions: Extension count for this op */
+	__u64 num_extensions;
+
+	/** @addr: Address of the debug data mapping */
+	__u64 addr;
+
+	/** @range: Range of the debug data mapping */
+	__u64 range;
+
+	/** @flags: Debug data flags */
+	__u64 flags;
+
+	/** @offset: Offset into the debug data file */
+	__u32 offset;
+
+	/** @reserved: Reserved, must be zero */
+	__u32 reserved;
+	union {
+		/**
+		 * @pseudopath: Pseudopath if
+		 * %DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO was set
+		 */
+		__u64 pseudopath;
+
+		/** @pathname: Path to the debug data file */
+		char pathname[PATH_MAX];
+	};
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 10/22] drm/xe/eudebug: Add UFENCE events with acks
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (8 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 09/22] drm/xe/eudebug: Introduce vm bind and vm bind debug data events Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 11/22] drm/xe/eudebug: vm open/pread/pwrite Mika Kuoppala
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

When vma is in place, debugger needs to intercept before
userspace proceeds with the workload. For example to install
a breakpoint in an eu shader.

If ufence is part of bind sequence,aAttach debugger in
xe_user_fence. When ufence signal is about to be delivered,
check if this ufence needs to be tracked by debugger.
If so, stall the delivery of the ufence signal up until
debugger has acked the ufence (event), with the ack ioctl.

v2: - return err instead of 0 to guarantee signalling (Dominik)
    - checkpatch (Tilak)
    - Kconfig (Mika, Andrzej)
    - use lock instead of cmpxchg (Mika)
v4: - improve ref handling and no ufences nodebug binds
v5: - remove overzealous warn_on on bind_ref_seqno (Christoph)
    - remove superfluous signalled (Mika)
    - fix double free on bind sequence (Mika)
    - Dont fill op fields if no debugger (Maciej)
v6: - rework to align with xe_eudebug_bind_execute()
v7: - fix setting signalled before debugger acks (Jan)

Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/xe_eudebug.rst   |   3 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 300 +++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_eudebug.h       |   9 +
 drivers/gpu/drm/xe/xe_eudebug_types.h |  10 +-
 drivers/gpu/drm/xe/xe_sync.c          |  43 ++--
 drivers/gpu/drm/xe/xe_sync.h          |   7 +-
 drivers/gpu/drm/xe/xe_sync_types.h    |  28 ++-
 include/uapi/drm/xe_drm_eudebug.h     |  58 +++++
 8 files changed, 430 insertions(+), 28 deletions(-)

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index 1f743f1d6f2a..db52945714f3 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -63,3 +63,6 @@ Resource Event Types
 
 .. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
    :identifiers: drm_xe_eudebug_event_vm_bind_op_debug_data
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event_vm_bind_ufence
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index d0f19dbdf55b..181a8041347b 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -19,6 +19,7 @@
 #include "xe_exec_queue.h"
 #include "xe_hw_engine.h"
 #include "xe_macros.h"
+#include "xe_sync.h"
 #include "xe_vm.h"
 
 /**
@@ -296,6 +297,114 @@ static void remove_debugger(struct xe_file *xef)
 	}
 }
 
+struct xe_eudebug_ack {
+	struct rb_node rb_node;
+	u64 seqno;
+	u64 ts_insert;
+	struct xe_user_fence *ufence;
+};
+
+#define fetch_ack(x) rb_entry(x, struct xe_eudebug_ack, rb_node)
+
+static int compare_ack(const u64 a, const u64 b)
+{
+	if (a < b)
+		return -1;
+	else if (a > b)
+		return 1;
+
+	return 0;
+}
+
+static int ack_insert_cmp(struct rb_node * const node,
+			  const struct rb_node * const p)
+{
+	return compare_ack(fetch_ack(node)->seqno,
+			   fetch_ack(p)->seqno);
+}
+
+static int ack_lookup_cmp(const void * const key,
+			  const struct rb_node * const node)
+{
+	return compare_ack(*(const u64 *)key,
+			   fetch_ack(node)->seqno);
+}
+
+static struct xe_eudebug_ack *remove_ack(struct xe_eudebug *d, u64 seqno)
+{
+	struct rb_root * const root = &d->acks.tree;
+	struct rb_node *node;
+
+	spin_lock(&d->acks.lock);
+	node = rb_find(&seqno, root, ack_lookup_cmp);
+	if (node)
+		rb_erase(node, root);
+	spin_unlock(&d->acks.lock);
+
+	if (!node)
+		return NULL;
+
+	return rb_entry_safe(node, struct xe_eudebug_ack, rb_node);
+}
+
+static void ufence_signal_worker(struct work_struct *w)
+{
+	struct xe_user_fence * const ufence =
+		container_of(w, struct xe_user_fence, eudebug.worker);
+
+	xe_sync_ufence_signal(ufence);
+
+	xe_sync_ufence_put(ufence);
+}
+
+static void kick_ufence_worker(struct xe_user_fence *f)
+{
+	queue_work(f->xe->eudebug.wq, &f->eudebug.worker);
+}
+
+static void handle_ack(struct xe_eudebug *d, struct xe_eudebug_ack *ack,
+		       bool on_disconnect)
+{
+	struct xe_user_fence *f = ack->ufence;
+	u64 signalled_by;
+	bool signal = false;
+
+	spin_lock(&f->eudebug.lock);
+	if (!f->eudebug.signalled_seqno) {
+		f->eudebug.signalled_seqno = ack->seqno;
+		f->eudebug.bind_ref_seqno = 0;
+		signal = true;
+	}
+	signalled_by = f->eudebug.signalled_seqno;
+	spin_unlock(&f->eudebug.lock);
+
+	if (signal)
+		kick_ufence_worker(f);
+	else
+		xe_sync_ufence_put(f);
+
+	eu_dbg(d, "ACK: seqno=%llu: signalled by %llu (%s) (held %lluus)",
+	       ack->seqno, signalled_by,
+	       on_disconnect ? "disconnect" : "debugger",
+	       ktime_us_delta(ktime_get(), ack->ts_insert));
+
+	kfree(ack);
+}
+
+static void release_acks(struct xe_eudebug *d)
+{
+	struct xe_eudebug_ack *ack, *n;
+	struct rb_root root;
+
+	spin_lock(&d->acks.lock);
+	root = d->acks.tree;
+	d->acks.tree = RB_ROOT;
+	spin_unlock(&d->acks.lock);
+
+	rbtree_postorder_for_each_entry_safe(ack, n, &root, rb_node)
+		handle_ack(d, ack, true);
+}
+
 static bool xe_eudebug_detach(struct xe_device *xe,
 			      struct xe_eudebug *d,
 			      const int err)
@@ -319,6 +428,8 @@ static bool xe_eudebug_detach(struct xe_device *xe,
 
 	eu_dbg(d, "session %lld detached with %d", d->session, err);
 
+	release_acks(d);
+
 	remove_debugger(target);
 	xe_file_put(target);
 
@@ -1016,11 +1127,134 @@ static int vm_bind_op(struct xe_eudebug *d, struct xe_vm *vm,
 	return 0;
 }
 
+void xe_eudebug_ufence_init(struct xe_user_fence *ufence)
+{
+	spin_lock_init(&ufence->eudebug.lock);
+	INIT_WORK(&ufence->eudebug.worker, ufence_signal_worker);
+	ufence->eudebug.bind_ref_seqno = 0;
+	ufence->eudebug.signalled_seqno = 0;
+}
+
+void xe_eudebug_ufence_fini(struct xe_user_fence *ufence)
+{
+	XE_WARN_ON(READ_ONCE(ufence->eudebug.bind_ref_seqno));
+
+	if (!ufence->eudebug.debugger)
+		return;
+
+	xe_eudebug_put(ufence->eudebug.debugger);
+}
+
+static int xe_eudebug_track_ufence(struct xe_eudebug *d,
+				   struct xe_user_fence *f,
+				   u64 seqno)
+{
+	struct xe_eudebug_ack *ack;
+	struct rb_node *old;
+
+	ack = kzalloc_obj(*ack, GFP_KERNEL);
+	if (!ack)
+		return -ENOMEM;
+
+	ack->seqno = seqno;
+	ack->ts_insert = ktime_get();
+
+	__xe_sync_ufence_get(f);
+
+	spin_lock(&d->acks.lock);
+	old = rb_find_add(&ack->rb_node,
+			  &d->acks.tree, ack_insert_cmp);
+	if (!old)
+		ack->ufence = f;
+	spin_unlock(&d->acks.lock);
+
+	if (ack->ufence)
+		return 0;
+
+	xe_sync_ufence_put(f);
+	kfree(ack);
+
+	return -EEXIST;
+}
+
+static int track_ufence(struct xe_eudebug *d,
+			struct xe_user_fence *ufence)
+{
+	struct drm_xe_eudebug_event *event;
+	struct drm_xe_eudebug_event_vm_bind_ufence *e;
+	const u32 sz = sizeof(*e);
+	const u32 flags = DRM_XE_EUDEBUG_EVENT_CREATE |
+		DRM_XE_EUDEBUG_EVENT_NEED_ACK;
+	u64 seqno;
+	int ret;
+
+	if (XE_WARN_ON(!ufence->eudebug.bind_ref_seqno))
+		return -EINVAL;
+
+	seqno = atomic_long_inc_return(&d->events.seqno);
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE,
+					seqno, flags, sz);
+	if (!event)
+		return -ENOMEM;
+
+	e = cast_event(e, event);
+	e->vm_bind_ref_seqno = ufence->eudebug.bind_ref_seqno;
+
+	ret = xe_eudebug_track_ufence(d, ufence, seqno);
+	if (ret) {
+		kfree(event);
+
+		eu_dbg(d, "tracking of ufence %llu failed with %d\n", seqno, ret);
+
+		return ret;
+	}
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+/**
+ * xe_eudebug_ufence_track - Track the ufence for eudebug
+ * @ufence : user fence that might be applicaple to tracking
+ *
+ * If this user fence was part of bind sequence, we need
+ * to track it so that we can hold the client signalling on behalf
+ * of debugger and thus deliver event to debugger.
+ *
+ * Return: true debugger will track, false debugger not interested
+ *
+ */
+bool xe_eudebug_ufence_track(struct xe_user_fence *ufence)
+{
+	struct xe_eudebug *d;
+	int ret;
+
+	spin_lock(&ufence->eudebug.lock);
+	d = ufence->eudebug.debugger;
+	spin_unlock(&ufence->eudebug.lock);
+
+	if (!d)
+		return false;
+
+	if (xe_eudebug_detached(d))
+		return false;
+
+	ret = track_ufence(d, ufence);
+	if (ret) {
+		xe_eudebug_disconnect(d, ret);
+		return false;
+	}
+
+	return true;
+}
+
 void xe_eudebug_vm_bind_execute(struct xe_vm *vm,
 				struct xe_vma_ops *ops)
 {
+	struct xe_user_fence *ufence = NULL;
 	struct xe_eudebug *d;
 	struct xe_vma_op *op;
+	unsigned int i;
 	u64 bind_seqno = 0;
 	u32 num_ops;
 	int err;
@@ -1032,6 +1266,15 @@ void xe_eudebug_vm_bind_execute(struct xe_vm *vm,
 	if (!d)
 		return;
 
+	for (i = 0; i < ops->num_syncs; i++) {
+		struct xe_sync_entry *se = &ops->syncs[i];
+
+		if (xe_sync_is_ufence(se)) {
+			xe_assert(vm->xe, ufence == NULL);
+			ufence = se->ufence;
+		}
+	}
+
 	num_ops = 0;
 	list_for_each_entry(op, &ops->list, link) {
 		if (op->base.op != DRM_GPUVA_OP_DRIVER)
@@ -1049,7 +1292,8 @@ void xe_eudebug_vm_bind_execute(struct xe_vm *vm,
 		return;
 	}
 
-	err = vm_bind_event(d, vm, 0,
+	err = vm_bind_event(d, vm,
+			    ufence ? DRM_XE_EUDEBUG_EVENT_VM_BIND_FLAG_UFENCE : 0,
 			    num_ops, &bind_seqno);
 	if (err)
 		goto out_err;
@@ -1075,6 +1319,14 @@ void xe_eudebug_vm_bind_execute(struct xe_vm *vm,
 			goto out_err;
 	}
 
+	if (ufence) {
+		spin_lock(&ufence->eudebug.lock);
+		kref_get(&d->ref);
+		ufence->eudebug.debugger = d;
+		ufence->eudebug.bind_ref_seqno = bind_seqno;
+		spin_unlock(&ufence->eudebug.lock);
+	}
+
 out_err:
 	if (err)
 		xe_eudebug_disconnect(d, err);
@@ -1401,6 +1653,44 @@ static long xe_eudebug_read_event(struct xe_eudebug *d,
 	return ret;
 }
 
+static long
+xe_eudebug_ack_event_ioctl(struct xe_eudebug *d,
+			   const unsigned int cmd,
+			   const u64 arg)
+{
+	struct drm_xe_eudebug_ack_event __user * const user_ptr =
+		u64_to_user_ptr(arg);
+	struct drm_xe_eudebug_ack user_arg;
+	struct xe_eudebug_ack *ack;
+	struct xe_device *xe = d->xe;
+
+	if (XE_IOCTL_DBG(xe, _IOC_SIZE(cmd) < sizeof(user_arg)))
+		return -EINVAL;
+
+	/* Userland write */
+	if (XE_IOCTL_DBG(xe, !(_IOC_DIR(cmd) & _IOC_WRITE)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, copy_from_user(&user_arg,
+					    user_ptr,
+					    sizeof(user_arg))))
+		return -EFAULT;
+
+	if (XE_IOCTL_DBG(xe, user_arg.flags))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, xe_eudebug_detached(d)))
+		return -ENOTCONN;
+
+	ack = remove_ack(d, user_arg.seqno);
+	if (XE_IOCTL_DBG(xe, !ack))
+		return -EINVAL;
+
+	handle_ack(d, ack, false);
+
+	return 0;
+}
+
 /**
  * xe_eudebug_ioctl - Issue a command to eudebug interface
  *
@@ -1428,7 +1718,10 @@ static long xe_eudebug_ioctl(struct file *file,
 		ret = xe_eudebug_read_event(d, arg,
 					    !(file->f_flags & O_NONBLOCK));
 		break;
-
+	case DRM_XE_EUDEBUG_IOCTL_ACK_EVENT:
+		ret = xe_eudebug_ack_event_ioctl(d, cmd, arg);
+		eu_dbg(d, "ioctl cmd=EVENT_ACK ret=%ld\n", ret);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -1490,6 +1783,9 @@ xe_eudebug_connect(struct xe_device *xe,
 	INIT_KFIFO(d->events.fifo);
 	INIT_WORK(&d->discovery_work, discovery_work_fn);
 
+	spin_lock_init(&d->acks.lock);
+	d->acks.tree = RB_ROOT;
+
 	err = xe_eudebug_resources_init(d);
 	if (XE_IOCTL_DBG(xe, err))
 		goto err_free;
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 9c622362c0f7..d0f1b51564dc 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -56,6 +56,10 @@ void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q)
 
 void xe_eudebug_vm_bind_execute(struct xe_vm *vm, struct xe_vma_ops *ops);
 
+void xe_eudebug_ufence_init(struct xe_user_fence *ufence);
+void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
+bool xe_eudebug_ufence_track(struct xe_user_fence *ufence);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
@@ -74,6 +78,11 @@ static inline void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_e
 static inline void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q) { }
 
 static inline void xe_eudebug_vm_bind_execute(struct xe_vm *vm, struct xe_vma_ops *ops) { }
+
+static inline void xe_eudebug_ufence_init(struct xe_user_fence *ufence) { }
+static inline void xe_eudebug_ufence_fini(struct xe_user_fence *ufence) { }
+static inline bool xe_eudebug_ufence_track(struct xe_user_fence *ufence) { return false; }
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif /* _XE_EUDEBUG_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 1bd6eb2aa102..0f18667a5ab8 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -33,7 +33,7 @@ enum xe_eudebug_state {
 };
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
-#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA
+#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE
 
 /**
  * struct xe_eudebug_handle - eudebug resource handle
@@ -125,6 +125,14 @@ struct xe_eudebug {
 		atomic_long_t seqno;
 	} events;
 
+	/** @acks: user fence acks tracked by this debugger */
+	struct {
+		/** @lock: guards access to tree */
+		spinlock_t lock;
+
+		/** @tree: pending acks by seqnos */
+		struct rb_root tree;
+	} acks;
 };
 
 #endif /* _XE_EUDEBUG_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index 24d6d9af20d6..70f94517e6cc 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -15,27 +15,20 @@
 #include <uapi/drm/xe_drm.h>
 
 #include "xe_device.h"
+#include "xe_eudebug.h"
 #include "xe_exec_queue.h"
 #include "xe_macros.h"
 #include "xe_sched_job_types.h"
 
-struct xe_user_fence {
-	struct xe_device *xe;
-	struct kref refcount;
-	struct dma_fence_cb cb;
-	struct work_struct worker;
-	struct mm_struct *mm;
-	u64 __user *addr;
-	u64 value;
-	int signalled;
-};
-
 static void user_fence_destroy(struct kref *kref)
 {
 	struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
 						 refcount);
 
 	mmdrop(ufence->mm);
+
+	xe_eudebug_ufence_fini(ufence);
+
 	kfree(ufence);
 }
 
@@ -49,7 +42,8 @@ static void user_fence_put(struct xe_user_fence *ufence)
 	kref_put(&ufence->refcount, user_fence_destroy);
 }
 
-static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
+static struct xe_user_fence *user_fence_create(struct xe_device *xe,
+					       u64 addr,
 					       u64 value)
 {
 	struct xe_user_fence *ufence;
@@ -70,14 +64,19 @@ static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
 	ufence->mm = current->mm;
 	mmgrab(ufence->mm);
 
+	xe_eudebug_ufence_init(ufence);
+
 	return ufence;
 }
 
-static void user_fence_worker(struct work_struct *w)
+void xe_sync_ufence_signal(struct xe_user_fence *ufence)
 {
-	struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
-
+	/*
+	 * Wake up waiters only after updating the ufence state, allowing the UMD
+	 * to safely reuse the same ufence without encountering -EBUSY errors.
+	 */
 	WRITE_ONCE(ufence->signalled, 1);
+
 	if (mmget_not_zero(ufence->mm)) {
 		kthread_use_mm(ufence->mm);
 		if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
@@ -88,11 +87,17 @@ static void user_fence_worker(struct work_struct *w)
 		drm_dbg(&ufence->xe->drm, "mmget_not_zero() failed, ufence wasn't signaled\n");
 	}
 
-	/*
-	 * Wake up waiters only after updating the ufence state, allowing the UMD
-	 * to safely reuse the same ufence without encountering -EBUSY errors.
-	 */
 	wake_up_all(&ufence->xe->ufence_wq);
+}
+
+static void user_fence_worker(struct work_struct *w)
+{
+	struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
+
+	/* Lets see if debugger wants to track this */
+	if (!xe_eudebug_ufence_track(ufence))
+		xe_sync_ufence_signal(ufence);
+
 	user_fence_put(ufence);
 }
 
diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h
index 6b949194acff..768c0517f104 100644
--- a/drivers/gpu/drm/xe/xe_sync.h
+++ b/drivers/gpu/drm/xe/xe_sync.h
@@ -10,8 +10,12 @@
 
 struct drm_syncobj;
 struct xe_device;
-struct xe_exec_queue;
 struct xe_file;
+struct xe_exec_queue;
+struct drm_syncobj;
+struct dma_fence;
+struct dma_fence_chain;
+struct drm_xe_sync;
 struct xe_sched_job;
 struct xe_vm;
 
@@ -45,5 +49,6 @@ struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence);
 struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync);
 void xe_sync_ufence_put(struct xe_user_fence *ufence);
 int xe_sync_ufence_get_status(struct xe_user_fence *ufence);
+void xe_sync_ufence_signal(struct xe_user_fence *ufence);
 
 #endif
diff --git a/drivers/gpu/drm/xe/xe_sync_types.h b/drivers/gpu/drm/xe/xe_sync_types.h
index b88f1833e28c..33a93a0faa72 100644
--- a/drivers/gpu/drm/xe/xe_sync_types.h
+++ b/drivers/gpu/drm/xe/xe_sync_types.h
@@ -6,13 +6,31 @@
 #ifndef _XE_SYNC_TYPES_H_
 #define _XE_SYNC_TYPES_H_
 
+#include <linux/dma-fence-array.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 
-struct drm_syncobj;
-struct dma_fence;
-struct dma_fence_chain;
-struct drm_xe_sync;
-struct user_fence;
+struct xe_user_fence {
+	struct xe_device *xe;
+	struct kref refcount;
+	struct dma_fence_cb cb;
+	struct work_struct worker;
+	struct mm_struct *mm;
+	u64 __user *addr;
+	u64 value;
+	int signalled;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	struct {
+		spinlock_t lock;
+		struct xe_eudebug *debugger;
+		u64 bind_ref_seqno;
+		u64 signalled_seqno;
+		struct work_struct worker;
+	} eudebug;
+#endif
+};
 
 struct xe_sync_entry {
 	struct drm_syncobj *syncobj;
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 230c8cdcbd21..fb53174869ef 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -11,6 +11,7 @@ extern "C" {
 #endif
 
 #define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
+#define DRM_XE_EUDEBUG_IOCTL_ACK_EVENT		_IOW('j', 0x1, struct drm_xe_eudebug_ack)
 
 /**
  * struct drm_xe_eudebug_event - Base type of event delivered by xe_eudebug.
@@ -48,6 +49,7 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE		3
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND		4
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	6
 
 	/** @flags: Flags */
 	__u16 flags;
@@ -125,6 +127,24 @@ struct drm_xe_eudebug_event_exec_queue {
  *
  * All the events below VM_BIND will reference the VM_BIND
  * they associate with, by field .vm_bind_ref_seqno.
+ * EVENT_UFENCE will only be included if the client did
+ * attach sync of type UFENCE into its vm_bind_ioctl().
+ *
+ * When EVENT_UFENCE is sent by the driver, all the OPs of
+ * the original VM_BIND are completed and the [addr,range]
+ * contained in them are present and modifiable through the
+ * vm accessors. Accessing [addr, range] before related ufence
+ * event will lead to undefined results as the actual bind
+ * operations are async and the backing storage might not
+ * be there on a moment of receiving the event.
+ *
+ * Client's UFENCE sync will be held by the driver: client's
+ * drm_xe_wait_ufence will not complete and the value of the ufence
+ * won't appear until ufence is acked by the debugger process calling
+ * DRM_XE_EUDEBUG_IOCTL_ACK_EVENT with the event_ufence.base.seqno.
+ * This will signal the fence, .value will update and the wait will
+ * complete allowing the client to continue.
+ *
  */
 struct drm_xe_eudebug_event_vm_bind {
 	/** @base: Base event */
@@ -135,6 +155,7 @@ struct drm_xe_eudebug_event_vm_bind {
 
 	/** @flags: Bind specific flags */
 	__u32 flags;
+#define DRM_XE_EUDEBUG_EVENT_VM_BIND_FLAG_UFENCE (1 << 0)
 
 	/** @num_bind_ops: How many [ADD|REMOVE]_DEBUG_DATA operations this bind has */
 	__u32 num_bind_ops;
@@ -184,6 +205,43 @@ struct drm_xe_eudebug_event_vm_bind_op_debug_data {
 	};
 };
 
+/**
+ * struct drm_xe_eudebug_event_vm_bind_ufence - User Fence Event
+ *
+ * When target drm client does vm bind with associated user fence,
+ * this event will be delivered. This event will have
+ * DRM_XE_EUDEBUG_EVENT_NEED_ACK set in :c:member:`drm_xe_eudebug_event.flags`
+ * and upon receiving this event you need to ack it with
+ * DRM_XE_EUDEBUG_IOCTL_ACK_EVENT.
+ *
+ */
+struct drm_xe_eudebug_event_vm_bind_ufence {
+	/** @base: Base event */
+	struct drm_xe_eudebug_event base;
+
+	/** @vm_bind_ref_seqno: Parent :c:member:`drm_xe_eudebug_event_vm_bind.base.seqno` */
+	__u64 vm_bind_ref_seqno;
+};
+
+/**
+ * struct drm_xe_eudebug_ack - Deliver ack for an event
+ *
+ * If event base.flags has DRM_XE_EUDEBUG_EVENT_NEED_ACK set,
+ * then the associated resource processing is held for client and
+ * thus held for the debugger. In order to release the client,
+ * ack needs to be delivered with DRM_XE_EUDEBUG_IOCTL_ACK_EVENT.
+ */
+struct drm_xe_eudebug_ack {
+	/** @type: Type, must be zero */
+	__u32 type;
+
+	/** @flags: Flags, must be zero */
+	__u32 flags;
+
+	/** @seqno: Seqno of event that is to be acked */
+	__u64 seqno;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 11/22] drm/xe/eudebug: vm open/pread/pwrite
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (9 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 10/22] drm/xe/eudebug: Add UFENCE events with acks Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 12/22] drm/xe/eudebug: userptr vm pread/pwrite Mika Kuoppala
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

Debugger needs access to the client's vm to read and write. For
example inspecting ISA/ELF and setting up breakpoints.

Add ioctl to open target vm with debugger client and vm_handle
and hook up pread/pwrite possibility.

Open will take timeout argument so that standard fsync
can be used for explicit flushing between cpu/gpu for
the target vm.

Implement this for bo backed storage. userptr will
be done in following patch.

v2: - checkpatch (Maciej)
    - 32bit fixes (Andrzej)
    - bo_vmap (Mika)
    - fix vm leak if can't allocate k_buffer (Mika)
    - assert vm write held for vma (Matthew)

v3: - fw ref, ttm_bo_access
    - timeout boundary check (Dominik)
    - dont try to copy to user on zero bytes (Mika)

v4: - offset as unsigned long (Thomas)
    - check XE_VMA_DESTROYED

v5: drm_dev_put before releasing debugger (Mika)

Cc: Matthew Brost <matthew.brost@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/xe_eudebug.rst   |   6 +
 drivers/gpu/drm/xe/Makefile           |   2 +-
 drivers/gpu/drm/xe/regs/xe_gt_regs.h  |  24 ++
 drivers/gpu/drm/xe/xe_eudebug.c       |  40 ++-
 drivers/gpu/drm/xe/xe_eudebug.h       |  12 +
 drivers/gpu/drm/xe/xe_eudebug_types.h |   6 +
 drivers/gpu/drm/xe/xe_eudebug_vm.c    | 418 ++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug_vm.h    |   8 +
 include/uapi/drm/xe_drm_eudebug.h     |  29 ++
 9 files changed, 541 insertions(+), 4 deletions(-)
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_vm.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_vm.h

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index db52945714f3..466d366c1e83 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -66,3 +66,9 @@ Resource Event Types
 
 .. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
    :identifiers: drm_xe_eudebug_event_vm_bind_ufence
+
+VM Access
+=========
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_vm_open
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 9d30627805f7..32a83300e1f2 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -152,7 +152,7 @@ xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
 xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
 
 # debugging shaders with gdb (eudebug) support
-xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o
+xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o
 
 # graphics hardware monitoring (HWMON) support
 xe-$(CONFIG_HWMON) += xe_hwmon.o
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 90b9017770ea..8e2e901a08bb 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -601,6 +601,30 @@
 #define   CCS_MODE_CSLICE(cslice, ccs) \
 	((ccs) << ((cslice) * CCS_MODE_CSLICE_WIDTH))
 
+#define RCU_ASYNC_FLUSH				XE_REG(0x149fc)
+#define   RCU_ASYNC_FLUSH_IN_PROGRESS	REG_BIT(31)
+#define   RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT	28
+#define   RCU_ASYNC_FLUSH_ENGINE_ID_DECODE1 REG_BIT(26)
+#define   RCU_ASYNC_FLUSH_AMFS		REG_BIT(8)
+#define   RCU_ASYNC_FLUSH_PREFETCH	REG_BIT(7)
+#define   RCU_ASYNC_FLUSH_DATA_PORT	REG_BIT(6)
+#define   RCU_ASYNC_FLUSH_DATA_CACHE	REG_BIT(5)
+#define   RCU_ASYNC_FLUSH_HDC_PIPELINE	REG_BIT(4)
+#define   RCU_ASYNC_INVALIDATE_HDC_PIPELINE REG_BIT(3)
+#define   RCU_ASYNC_INVALIDATE_CONSTANT_CACHE REG_BIT(2)
+#define   RCU_ASYNC_INVALIDATE_TEXTURE_CACHE REG_BIT(1)
+#define   RCU_ASYNC_INVALIDATE_INSTRUCTION_CACHE REG_BIT(0)
+#define   RCU_ASYNC_FLUSH_AND_INVALIDATE_ALL ( \
+	RCU_ASYNC_FLUSH_AMFS | \
+	RCU_ASYNC_FLUSH_PREFETCH | \
+	RCU_ASYNC_FLUSH_DATA_PORT | \
+	RCU_ASYNC_FLUSH_DATA_CACHE | \
+	RCU_ASYNC_FLUSH_HDC_PIPELINE | \
+	RCU_ASYNC_INVALIDATE_HDC_PIPELINE | \
+	RCU_ASYNC_INVALIDATE_CONSTANT_CACHE | \
+	RCU_ASYNC_INVALIDATE_TEXTURE_CACHE | \
+	RCU_ASYNC_INVALIDATE_INSTRUCTION_CACHE)
+
 #define FORCEWAKE_ACK_GT			XE_REG(0x130044)
 
 /* Applicable for all FORCEWAKE_DOMAIN and FORCEWAKE_ACK_DOMAIN regs */
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 181a8041347b..bbf01fefdc63 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -16,6 +16,7 @@
 #include "xe_device.h"
 #include "xe_eudebug.h"
 #include "xe_eudebug_types.h"
+#include "xe_eudebug_vm.h"
 #include "xe_exec_queue.h"
 #include "xe_hw_engine.h"
 #include "xe_macros.h"
@@ -145,8 +146,7 @@ event_fifo_num_events_peek(const struct xe_eudebug * const d)
 	return kfifo_len(&d->events.fifo);
 }
 
-static bool
-xe_eudebug_detached(struct xe_eudebug *d)
+bool xe_eudebug_detached(struct xe_eudebug *d)
 {
 	return READ_ONCE(d->target.xef) == NULL;
 }
@@ -266,7 +266,7 @@ static void xe_eudebug_free(struct kref *ref)
 	kfree(d);
 }
 
-static void xe_eudebug_put(struct xe_eudebug *d)
+void xe_eudebug_put(struct xe_eudebug *d)
 {
 	kref_put(&d->ref, xe_eudebug_free);
 }
@@ -740,6 +740,34 @@ static int xe_eudebug_remove_handle(struct xe_eudebug *d, int type, void *p,
 	return ret;
 }
 
+static void *find_resource__unlocked(struct xe_eudebug *d,
+				     int type,
+				     u32 id)
+{
+
+	struct xe_eudebug_resource *r;
+	struct xe_eudebug_handle *h;
+
+	r = resource_from_type(d, type);
+	h = xa_load(&r->xa, id);
+
+	return h ? (void *)(uintptr_t)h->key : NULL;
+}
+
+struct xe_vm *xe_eudebug_vm_get(struct xe_eudebug *d, u32 id)
+{
+	struct xe_vm *vm;
+
+	mutex_lock(&d->target.lock);
+	vm = find_resource__unlocked(d, XE_EUDEBUG_RES_TYPE_VM, id);
+	if (vm)
+		xe_vm_get(vm);
+	mutex_unlock(&d->target.lock);
+
+	return vm;
+}
+
+
 static struct drm_xe_eudebug_event *
 xe_eudebug_create_event(struct xe_eudebug *d, u16 type, u64 seqno, u16 flags,
 			u32 len)
@@ -1722,6 +1750,10 @@ static long xe_eudebug_ioctl(struct file *file,
 		ret = xe_eudebug_ack_event_ioctl(d, cmd, arg);
 		eu_dbg(d, "ioctl cmd=EVENT_ACK ret=%ld\n", ret);
 		break;
+	case DRM_XE_EUDEBUG_IOCTL_VM_OPEN:
+		ret = xe_eudebug_vm_open_ioctl(d, arg);
+		eu_dbg(d, "ioctl cmd=VM_OPEN ret=%ld\n", ret);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -1786,6 +1818,8 @@ xe_eudebug_connect(struct xe_device *xe,
 	spin_lock_init(&d->acks.lock);
 	d->acks.tree = RB_ROOT;
 
+	mutex_init(&d->hw.lock);
+
 	err = xe_eudebug_resources_init(d);
 	if (XE_IOCTL_DBG(xe, err))
 		goto err_free;
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index d0f1b51564dc..74171cc81fe1 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -18,6 +18,7 @@ struct xe_vma;
 struct xe_vma_ops;
 struct xe_exec_queue;
 struct xe_user_fence;
+struct xe_eudebug;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -38,6 +39,10 @@ struct xe_user_fence;
 
 #define xe_eudebug_assert(d, ...) xe_assert((d)->xe, ##__VA_ARGS__)
 
+#define xe_eudebug_for_each_hw_engine(__hwe, __gt, __id) \
+	for_each_hw_engine(__hwe, __gt, __id)	       \
+		if (xe_hw_engine_has_eudebug(__hwe))
+
 int xe_eudebug_connect_ioctl(struct drm_device *dev,
 			     void *data,
 			     struct drm_file *file);
@@ -47,10 +52,15 @@ bool xe_eudebug_is_enabled(struct xe_device *xe);
 
 void xe_eudebug_file_close(struct xe_file *xef);
 
+bool xe_eudebug_detached(struct xe_eudebug *d);
+
 void xe_eudebug_vm_create(struct xe_file *xef, struct xe_vm *vm);
 void xe_eudebug_vm_destroy(struct xe_file *xef, struct xe_vm *vm);
+
 int xe_eudebug_enable(struct xe_device *xe, bool enable);
 
+struct xe_vm *xe_eudebug_vm_get(struct xe_eudebug *d, u32 vm_id);
+
 void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q);
 void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q);
 
@@ -60,6 +70,8 @@ void xe_eudebug_ufence_init(struct xe_user_fence *ufence);
 void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
 bool xe_eudebug_ufence_track(struct xe_user_fence *ufence);
 
+void xe_eudebug_put(struct xe_eudebug *d);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 0f18667a5ab8..10d19a43ba6b 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -133,6 +133,12 @@ struct xe_eudebug {
 		/** @tree: pending acks by seqnos */
 		struct rb_root tree;
 	} acks;
+
+	/** @hw: hw access */
+	struct {
+		/** @lock: guards access to hw state */
+		struct mutex lock;
+	} hw;
 };
 
 #endif /* _XE_EUDEBUG_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_vm.c b/drivers/gpu/drm/xe/xe_eudebug_vm.c
new file mode 100644
index 000000000000..4cf6d2e79658
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_vm.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#include "xe_eudebug_vm.h"
+
+#include <linux/anon_inodes.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+
+#include <drm/drm_drv.h>
+
+#include "xe_bo.h"
+#include "xe_device.h"
+#include "xe_eudebug.h"
+#include "xe_eudebug_types.h"
+#include "xe_force_wake.h"
+#include "xe_gt.h"
+#include "xe_mmio.h"
+#include "xe_vm.h"
+
+#include "regs/xe_gt_regs.h"
+#include "regs/xe_engine_regs.h"
+
+static int xe_eudebug_vma_access(struct xe_vma *vma,
+				 unsigned long offset_in_vma,
+				 void *buf, unsigned long len, bool write)
+{
+	struct xe_bo *bo;
+	u64 bytes;
+
+	lockdep_assert_held_write(&xe_vma_vm(vma)->lock);
+
+	if (XE_WARN_ON(offset_in_vma >= xe_vma_size(vma)))
+		return -EINVAL;
+
+	if (vma->gpuva.flags & XE_VMA_DESTROYED)
+		return -EINVAL;
+
+	bytes = min_t(u64, len, xe_vma_size(vma) - offset_in_vma);
+	if (!bytes)
+		return 0;
+
+	bo = xe_bo_get(xe_vma_bo(vma));
+	if (bo) {
+		int ret;
+
+		ret = ttm_bo_access(&bo->ttm, offset_in_vma, buf, bytes, write);
+
+		xe_bo_put(bo);
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static int xe_eudebug_vm_access(struct xe_vm *vm, unsigned long offset,
+				void *buf, unsigned long len, bool write)
+{
+	struct xe_vma *vma;
+	int ret;
+
+	down_write(&vm->lock);
+
+	vma = xe_vm_find_overlapping_vma(vm, offset, len);
+	if (vma) {
+		/* XXX: why find overlapping returns below start? */
+		if (offset < xe_vma_start(vma) ||
+		    offset >= (xe_vma_start(vma) + xe_vma_size(vma))) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/* Offset into vma */
+		offset -= xe_vma_start(vma);
+		ret = xe_eudebug_vma_access(vma, offset, buf, len, write);
+	} else {
+		ret = -EINVAL;
+	}
+
+out:
+	up_write(&vm->lock);
+
+	return ret;
+}
+
+struct vm_file {
+	struct xe_eudebug *debugger;
+	struct xe_vm *vm;
+	u64 flags;
+	u64 vm_handle;
+	unsigned int timeout_us;
+};
+
+static ssize_t __vm_read_write(struct xe_vm *vm,
+			       void *bb,
+			       char __user *r_buffer,
+			       const char __user *w_buffer,
+			       unsigned long offset,
+			       unsigned long len,
+			       const bool write)
+{
+	ssize_t ret;
+
+	if (!len)
+		return 0;
+
+	if (write) {
+		ret = copy_from_user(bb, w_buffer, len);
+		if (ret)
+			return -EFAULT;
+
+		ret = xe_eudebug_vm_access(vm, offset, bb, len, true);
+		if (ret <= 0)
+			return ret;
+
+		len = ret;
+	} else {
+		ret = xe_eudebug_vm_access(vm, offset, bb, len, false);
+		if (ret <= 0)
+			return ret;
+
+		len = ret;
+
+		ret = copy_to_user(r_buffer, bb, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	return len;
+}
+
+static ssize_t __xe_eudebug_vm_access(struct file *file,
+				      char __user *r_buffer,
+				      const char __user *w_buffer,
+				      size_t count, loff_t *__pos)
+{
+	struct vm_file *vmf = file->private_data;
+	struct xe_eudebug * const d = vmf->debugger;
+	struct xe_device * const xe = d->xe;
+	const bool write = !!w_buffer;
+	struct xe_vm *vm;
+	ssize_t copied = 0;
+	ssize_t bytes_left = count;
+	ssize_t ret;
+	unsigned long alloc_len;
+	loff_t pos = *__pos;
+	void *k_buffer;
+
+	if (XE_IOCTL_DBG(xe, write && r_buffer))
+		return -EINVAL;
+
+	vm = xe_eudebug_vm_get(d, vmf->vm_handle);
+	if (XE_IOCTL_DBG(xe, !vm))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, vm != vmf->vm)) {
+		eu_warn(d, "vm_access(%s): vm handle mismatch vm_handle=%llu, flags=0x%llx, pos=%llu, count=%zu\n",
+			write ? "write" : "read",
+			vmf->vm_handle, vmf->flags, pos, count);
+		xe_vm_put(vm);
+		return -EINVAL;
+	}
+
+	if (!count) {
+		xe_vm_put(vm);
+		return 0;
+	}
+
+	alloc_len = min_t(unsigned long, ALIGN(count, PAGE_SIZE), 64 * SZ_1M);
+	do  {
+		k_buffer = vmalloc(alloc_len);
+		if (k_buffer)
+			break;
+
+		alloc_len >>= 1;
+	} while (alloc_len > PAGE_SIZE);
+
+	if (XE_IOCTL_DBG(xe, !k_buffer)) {
+		xe_vm_put(vm);
+		return -ENOMEM;
+	}
+
+	do {
+		const ssize_t len = min_t(ssize_t, bytes_left, alloc_len);
+
+		ret = __vm_read_write(vm, k_buffer,
+				      write ? NULL : r_buffer + copied,
+				      write ? w_buffer + copied : NULL,
+				      pos + copied,
+				      len,
+				      write);
+		if (ret <= 0)
+			break;
+
+		bytes_left -= ret;
+		copied += ret;
+	} while (bytes_left > 0);
+
+	vfree(k_buffer);
+	xe_vm_put(vm);
+
+	if (XE_WARN_ON(copied < 0))
+		copied = 0;
+
+	*__pos += copied;
+
+	return copied ?: ret;
+}
+
+static ssize_t xe_eudebug_vm_read(struct file *file,
+				  char __user *buffer,
+				  size_t count, loff_t *pos)
+{
+	return __xe_eudebug_vm_access(file, buffer, NULL, count, pos);
+}
+
+static ssize_t xe_eudebug_vm_write(struct file *file,
+				   const char __user *buffer,
+				   size_t count, loff_t *pos)
+{
+	return __xe_eudebug_vm_access(file, NULL, buffer, count, pos);
+}
+
+static int engine_rcu_flush(struct xe_eudebug *d,
+			    struct xe_hw_engine *hwe,
+			    unsigned int timeout_us)
+{
+	const struct xe_reg psmi_addr = RING_PSMI_CTL(hwe->mmio_base);
+	struct xe_gt *gt = hwe->gt;
+	unsigned int fw_ref;
+	u32 mask = RCU_ASYNC_FLUSH_AND_INVALIDATE_ALL;
+	u32 psmi_ctrl;
+	u32 id;
+	int ret;
+
+	if (hwe->class == XE_ENGINE_CLASS_RENDER)
+		id = 0;
+	else if (hwe->class == XE_ENGINE_CLASS_COMPUTE)
+		id = hwe->instance + 1;
+	else
+		return -EINVAL;
+
+	if (id < 8)
+		mask |= id << RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT;
+	else
+		mask |= (id - 8) << RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT |
+			RCU_ASYNC_FLUSH_ENGINE_ID_DECODE1;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(gt), hwe->domain);
+	if (!fw_ref)
+		return -ETIMEDOUT;
+
+	/* Prevent concurrent flushes */
+	mutex_lock(&d->hw.lock);
+	psmi_ctrl = xe_mmio_read32(&gt->mmio, psmi_addr);
+	if (!(psmi_ctrl & IDLE_MSG_DISABLE))
+		xe_mmio_write32(&gt->mmio, psmi_addr, _MASKED_BIT_ENABLE(IDLE_MSG_DISABLE));
+
+	/* XXX: Timeout is per operation but in here we flush previous */
+	ret = xe_mmio_wait32(&gt->mmio, RCU_ASYNC_FLUSH,
+			     RCU_ASYNC_FLUSH_IN_PROGRESS, 0,
+			     timeout_us, NULL, false);
+	if (ret)
+		goto out;
+
+	xe_mmio_write32(&gt->mmio, RCU_ASYNC_FLUSH, mask);
+
+	ret = xe_mmio_wait32(&gt->mmio, RCU_ASYNC_FLUSH,
+			     RCU_ASYNC_FLUSH_IN_PROGRESS, 0,
+			     timeout_us, NULL, false);
+out:
+	if (!(psmi_ctrl & IDLE_MSG_DISABLE))
+		xe_mmio_write32(&gt->mmio, psmi_addr, _MASKED_BIT_DISABLE(IDLE_MSG_DISABLE));
+
+	mutex_unlock(&d->hw.lock);
+	xe_force_wake_put(gt_to_fw(gt), fw_ref);
+
+	return ret;
+}
+
+static int xe_eudebug_vm_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+	struct vm_file *vmf = file->private_data;
+	struct xe_eudebug *d = vmf->debugger;
+	struct xe_gt *gt;
+	int gt_id;
+	int ret = -EINVAL;
+
+	eu_dbg(d, "vm_fsync: vm_handle=%llu, flags=0x%llx, start=%llu, end=%llu datasync=%d\n",
+	       vmf->vm_handle, vmf->flags, start, end, datasync);
+
+	for_each_gt(gt, d->xe, gt_id) {
+		struct xe_hw_engine *hwe;
+		enum xe_hw_engine_id id;
+
+		/* XXX: vm open per engine? */
+		xe_eudebug_for_each_hw_engine(hwe, gt, id) {
+			ret = engine_rcu_flush(d, hwe, vmf->timeout_us);
+			if (ret)
+				break;
+		}
+	}
+
+	return ret;
+}
+
+static int xe_eudebug_vm_release(struct inode *inode, struct file *file)
+{
+	struct vm_file *vmf = file->private_data;
+	struct xe_eudebug *d = vmf->debugger;
+
+	eu_dbg(d, "vm_release: vm_handle=%llu, flags=0x%llx",
+	       vmf->vm_handle, vmf->flags);
+
+	xe_vm_put(vmf->vm);
+	drm_dev_put(&d->xe->drm);
+	xe_eudebug_put(d);
+
+	kfree(vmf);
+
+	return 0;
+}
+
+static const struct file_operations vm_fops = {
+	.owner   = THIS_MODULE,
+	.llseek  = generic_file_llseek,
+	.read    = xe_eudebug_vm_read,
+	.write   = xe_eudebug_vm_write,
+	.fsync   = xe_eudebug_vm_fsync,
+	.mmap    = NULL,
+	.release = xe_eudebug_vm_release,
+};
+
+long xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg)
+{
+	struct drm_xe_eudebug_vm_open param;
+	struct xe_device * const xe = d->xe;
+	struct vm_file *vmf = NULL;
+	struct xe_vm *vm;
+	struct file *file;
+	long ret = 0;
+	int fd;
+
+	if (XE_IOCTL_DBG(xe, _IOC_SIZE(DRM_XE_EUDEBUG_IOCTL_VM_OPEN) != sizeof(param)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !(_IOC_DIR(DRM_XE_EUDEBUG_IOCTL_VM_OPEN) & _IOC_WRITE)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, copy_from_user(&param, (void __user *)arg, sizeof(param))))
+		return -EFAULT;
+
+	if (XE_IOCTL_DBG(xe, param.flags))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, xe_eudebug_detached(d)))
+		return -ENOTCONN;
+
+	vm = xe_eudebug_vm_get(d, param.vm_handle);
+	if (XE_IOCTL_DBG(xe, !vm))
+		return -EINVAL;
+
+	vmf = kzalloc_obj(*vmf, GFP_KERNEL);
+	if (XE_IOCTL_DBG(xe, !vmf)) {
+		ret = -ENOMEM;
+		goto out_vm_put;
+	}
+
+	fd = get_unused_fd_flags(O_CLOEXEC);
+	if (XE_IOCTL_DBG(xe, fd < 0)) {
+		ret = fd;
+		goto out_free;
+	}
+
+	kref_get(&d->ref);
+	vmf->debugger = d;
+	vmf->vm = vm;
+	vmf->flags = param.flags;
+	vmf->vm_handle = param.vm_handle;
+	vmf->timeout_us = div64_u64(param.timeout_ns, 1000ull);
+
+	file = anon_inode_getfile("[xe_eudebug.vm]", &vm_fops, vmf, O_RDWR);
+	if (IS_ERR(file)) {
+		ret = PTR_ERR(file);
+		XE_IOCTL_DBG(xe, ret);
+		file = NULL;
+		goto out_fd_put;
+	}
+
+	drm_dev_get(&xe->drm);
+
+	file->f_mode |= FMODE_PREAD | FMODE_PWRITE |
+		FMODE_READ | FMODE_WRITE | FMODE_LSEEK;
+
+	fd_install(fd, file);
+
+	eu_dbg(d, "vm_open: handle=%llu, flags=0x%llx, fd=%d",
+	       vmf->vm_handle, vmf->flags, fd);
+
+	XE_WARN_ON(ret);
+
+	return fd;
+
+out_fd_put:
+	put_unused_fd(fd);
+	xe_eudebug_put(d);
+out_free:
+	kfree(vmf);
+out_vm_put:
+	xe_vm_put(vm);
+
+	XE_WARN_ON(ret >= 0);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug_vm.h b/drivers/gpu/drm/xe/xe_eudebug_vm.h
new file mode 100644
index 000000000000..b3dc5618a5e6
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_vm.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+struct xe_eudebug;
+
+long xe_eudebug_vm_open_ioctl(struct xe_eudebug *d, unsigned long arg);
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index fb53174869ef..029a51340777 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -12,6 +12,7 @@ extern "C" {
 
 #define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
 #define DRM_XE_EUDEBUG_IOCTL_ACK_EVENT		_IOW('j', 0x1, struct drm_xe_eudebug_ack)
+#define DRM_XE_EUDEBUG_IOCTL_VM_OPEN		_IOW('j', 0x2, struct drm_xe_eudebug_vm_open)
 
 /**
  * struct drm_xe_eudebug_event - Base type of event delivered by xe_eudebug.
@@ -242,6 +243,34 @@ struct drm_xe_eudebug_ack {
 	__u64 seqno;
 };
 
+/**
+ * struct drm_xe_eudebug_vm_open - Open a target vm
+ *
+ * Open target VM for reading and writing with DRM_XE_EUDEBUG_IOCTL_VM_OPEN.
+ *
+ * File descriptor is returned which can be used with pread and pwrite
+ * to inspect and modify the target VM.
+ *
+ * Multiple operations can be synced with calling fsync(fd). If
+ * timeout_ns was specified, the fsync will timeout if the
+ * VM can't be guaranteed to be in sync. Caller should re-read the
+ * state with pread again.
+ *
+ */
+struct drm_xe_eudebug_vm_open {
+	/** @extensions: Pointer to the first extension struct, if any */
+	__u64 extensions;
+
+	/** @vm_handle: handle of vm to be accessed */
+	__u64 vm_handle;
+
+	/** @flags: flags, must be zero */
+	__u64 flags;
+
+	/** @timeout_ns: Timeout value in nanoseconds */
+	__u64 timeout_ns;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 12/22] drm/xe/eudebug: userptr vm pread/pwrite
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (10 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 11/22] drm/xe/eudebug: vm open/pread/pwrite Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 13/22] drm/xe/eudebug: hw enablement for eudebug Mika Kuoppala
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala, Simona Vetter, Dominik Grzegorzek

Implement debugger vm access for userptrs.

When bind is done, take ref to current task so that
we know from which vm the address was bound. Then during
debugger pread/pwrite we use this target task as
parameter to access the debuggee vm with access_process_vm().

This is based on suggestions from Simona, Thomas and Joonas.

v2: need to add offset into vma (Dominik)
v3: move code into xe_userptr.c (Mika)

Cc: Simona Vetter <simona@ffwll.ch>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Andrzej Hajda <andrzej.hajda@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Cc: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/xe_eudebug_vm.c | 16 +++++++++++++++
 drivers/gpu/drm/xe/xe_userptr.c    |  4 ++++
 drivers/gpu/drm/xe/xe_userptr.h    | 32 ++++++++++++++++++++++++++++++
 3 files changed, 52 insertions(+)

diff --git a/drivers/gpu/drm/xe/xe_eudebug_vm.c b/drivers/gpu/drm/xe/xe_eudebug_vm.c
index 4cf6d2e79658..074443a792be 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_vm.c
+++ b/drivers/gpu/drm/xe/xe_eudebug_vm.c
@@ -51,6 +51,22 @@ static int xe_eudebug_vma_access(struct xe_vma *vma,
 		xe_bo_put(bo);
 
 		return ret;
+	} else if (xe_vma_is_userptr(vma)) {
+		struct xe_userptr *userptr = &to_userptr_vma(vma)->userptr;
+
+		if (XE_WARN_ON(!userptr->eudebug.task))
+			return -EINVAL;
+
+		/*
+		 * access_remote_vm() would fit as userptr notifier has
+		 * mm ref so we would not need to carry task ref at all.
+		 * But access_remote_vm is not exported. access_process_vm()
+		 * is exported so use it instead.
+		 */
+		return access_process_vm(userptr->eudebug.task,
+					 xe_vma_userptr(vma) + offset_in_vma,
+					 buf, bytes,
+					 write ? FOLL_WRITE : 0);
 	}
 
 	return -EINVAL;
diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
index e120323c43bc..5932f9f0cce3 100644
--- a/drivers/gpu/drm/xe/xe_userptr.c
+++ b/drivers/gpu/drm/xe/xe_userptr.c
@@ -292,6 +292,8 @@ int xe_userptr_setup(struct xe_userptr_vma *uvma, unsigned long start,
 
 	userptr->pages.notifier_seq = LONG_MAX;
 
+	xe_eudebug_track_userptr_task(userptr);
+
 	return 0;
 }
 
@@ -300,6 +302,8 @@ void xe_userptr_remove(struct xe_userptr_vma *uvma)
 	struct xe_vm *vm = xe_vma_vm(&uvma->vma);
 	struct xe_userptr *userptr = &uvma->userptr;
 
+	xe_eudebug_untrack_userptr_task(userptr);
+
 	drm_gpusvm_free_pages(&vm->svm.gpusvm, &uvma->userptr.pages,
 			      xe_vma_size(&uvma->vma) >> PAGE_SHIFT);
 
diff --git a/drivers/gpu/drm/xe/xe_userptr.h b/drivers/gpu/drm/xe/xe_userptr.h
index ef801234991e..4af2569f1fa1 100644
--- a/drivers/gpu/drm/xe/xe_userptr.h
+++ b/drivers/gpu/drm/xe/xe_userptr.h
@@ -66,6 +66,12 @@ struct xe_userptr {
 #if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT)
 	u32 divisor;
 #endif
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+	struct {
+		struct task_struct *task;
+	} eudebug;
+#endif
 };
 
 #if IS_ENABLED(CONFIG_DRM_GPUSVM)
@@ -104,4 +110,30 @@ static inline void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma)
 {
 }
 #endif
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+static inline void xe_eudebug_track_userptr_task(struct xe_userptr *userptr)
+{
+	/*
+	 * We could use the mm which is on notifier. But
+	 * the access_remote_vm() is not exported. Thus
+	 * we get reference to task for access_process_vm()
+	 */
+	userptr->eudebug.task = get_task_struct(current);
+}
+
+static inline void xe_eudebug_untrack_userptr_task(struct xe_userptr *userptr)
+{
+	put_task_struct(userptr->eudebug.task);
+}
+#else
+static inline void xe_eudebug_track_userptr_task(struct xe_userptr *userptr)
+{
+}
+
+static inline void xe_eudebug_untrack_userptr_task(struct xe_userptr *userptr)
+{
+}
+#endif /* CONFIG_DRM_XE_EUDEBUG */
+
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 13/22] drm/xe/eudebug: hw enablement for eudebug
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (11 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 12/22] drm/xe/eudebug: userptr vm pread/pwrite Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 14/22] drm/xe/eudebug: Introduce EU control interface Mika Kuoppala
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Dominik Grzegorzek, Mika Kuoppala

From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>

In order to turn on debug capabilities, (i.e. breakpoints), TD_CTL
and some other registers needs to be programmed. Implement eudebug
mode enabling including eudebug related workarounds.

v2: Move workarounds to xe_wa_oob. Use reg_sr directly instead of
xe_rtp as it suits better for dynamic manipulation of those register we
do later in the series.
v3: get rid of undefining XE_MCR_REG (Mika)

Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/Makefile              |   2 +-
 drivers/gpu/drm/xe/regs/xe_engine_regs.h |   4 +
 drivers/gpu/drm/xe/regs/xe_gt_regs.h     |  19 +++
 drivers/gpu/drm/xe/xe_eudebug_hw.c       |  72 +++++++++
 drivers/gpu/drm/xe/xe_eudebug_hw.h       |  25 ++++
 drivers/gpu/drm/xe/xe_gt_debug.c         | 179 +++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_gt_debug.h         |  32 ++++
 drivers/gpu/drm/xe/xe_reg_sr.c           |  21 ++-
 drivers/gpu/drm/xe/xe_reg_sr.h           |   4 +-
 drivers/gpu/drm/xe/xe_reg_whitelist.c    |   2 +-
 drivers/gpu/drm/xe/xe_rtp.c              |   2 +-
 drivers/gpu/drm/xe/xe_wa_oob.rules       |   2 +
 12 files changed, 354 insertions(+), 10 deletions(-)
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_hw.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_hw.h
 create mode 100644 drivers/gpu/drm/xe/xe_gt_debug.c
 create mode 100644 drivers/gpu/drm/xe/xe_gt_debug.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 32a83300e1f2..f3e113f801d0 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -152,7 +152,7 @@ xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
 xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
 
 # debugging shaders with gdb (eudebug) support
-xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o
+xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_gt_debug.o
 
 # graphics hardware monitoring (HWMON) support
 xe-$(CONFIG_HWMON) += xe_hwmon.o
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index dc5a4fafa70c..2e6c08c577ed 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -129,6 +129,10 @@
 
 #define INDIRECT_RING_STATE(base)		XE_REG((base) + 0x108)
 
+#define CS_DEBUG_MODE2(base)			XE_REG((base) + 0xd8, XE_REG_OPTION_MASKED)
+#define   INST_STATE_CACHE_INVALIDATE		REG_BIT(6)
+#define   GLOBAL_DEBUG_ENABLE			REG_BIT(5)
+
 #define RING_BBADDR(base)			XE_REG((base) + 0x140)
 #define RING_BBADDR_UDW(base)			XE_REG((base) + 0x168)
 
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 8e2e901a08bb..879c6335700e 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -506,10 +506,20 @@
 #define   DG2_DISABLE_ROUND_ENABLE_ALLOW_FOR_SSLA	REG_BIT(15)
 #define   CLEAR_OPTIMIZATION_DISABLE			REG_BIT(6)
 
+#define TD_CTL					XE_REG_MCR(0xe400)
+#define   TD_CTL_FEH_AND_FEE_ENABLE		REG_BIT(7) /* forced halt and exception */
+#define   TD_CTL_FORCE_EXTERNAL_HALT		REG_BIT(6)
+#define   TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE	REG_BIT(4)
+#define   TD_CTL_FORCE_EXCEPTION		REG_BIT(3)
+#define   TD_CTL_BREAKPOINT_ENABLE		REG_BIT(2)
+#define   TD_CTL_GLOBAL_DEBUG_ENABLE		REG_BIT(0) /* XeHP */
+
 #define CACHE_MODE_SS				XE_REG_MCR(0xe420, XE_REG_OPTION_MASKED)
 #define   DISABLE_ECC				REG_BIT(5)
 #define   ENABLE_PREFETCH_INTO_IC		REG_BIT(3)
 
+#define EU_ATT(reg, row)			XE_REG_MCR((reg ? 0xe478 : 0xe470) + (row) * 4)
+
 #define ROW_CHICKEN4				XE_REG_MCR(0xe48c, XE_REG_OPTION_MASKED)
 #define   DISABLE_GRF_CLEAR			REG_BIT(13)
 #define   XEHP_DIS_BBL_SYSPIPE			REG_BIT(11)
@@ -519,6 +529,8 @@
 #define   THREAD_EX_ARB_MODE			REG_GENMASK(3, 2)
 #define   THREAD_EX_ARB_MODE_RR_AFTER_DEP	REG_FIELD_PREP(THREAD_EX_ARB_MODE, 0x2)
 
+#define EU_ATT_CLR(reg, row)			XE_REG_MCR((reg ? 0xe698 : 0xe490) + (row) * 4)
+
 #define ROW_CHICKEN3				XE_REG_MCR(0xe49c, XE_REG_OPTION_MASKED)
 #define   XE2_EUPEND_CHK_FLUSH_DIS		REG_BIT(14)
 #define   DIS_FIX_EOT1_FLUSH			REG_BIT(9)
@@ -533,11 +545,13 @@
 #define   MDQ_ARBITRATION_MODE			REG_BIT(12)
 #define   STALL_DOP_GATING_DISABLE		REG_BIT(5)
 #define   EARLY_EOT_DIS				REG_BIT(1)
+#define   STALL_DOP_GATING_DISABLE		REG_BIT(5)
 
 #define ROW_CHICKEN2				XE_REG_MCR(0xe4f4, XE_REG_OPTION_MASKED)
 #define   DISABLE_READ_SUPPRESSION		REG_BIT(15)
 #define   DISABLE_EARLY_READ			REG_BIT(14)
 #define   ENABLE_LARGE_GRF_MODE			REG_BIT(12)
+#define   XEHPC_DISABLE_BTB			REG_BIT(11)
 #define   PUSH_CONST_DEREF_HOLD_DIS		REG_BIT(8)
 #define   DISABLE_TDL_SVHS_GATING		REG_BIT(1)
 #define   DISABLE_DOP_GATING			REG_BIT(0)
@@ -601,6 +615,11 @@
 #define   CCS_MODE_CSLICE(cslice, ccs) \
 	((ccs) << ((cslice) * CCS_MODE_CSLICE_WIDTH))
 
+#define RCU_DEBUG_1				XE_REG(0x14a00)
+#define   RCU_DEBUG_1_ENGINE_STATUS		REG_GENMASK(2, 0)
+#define   RCU_DEBUG_1_RUNALONE_ACTIVE		REG_BIT(2)
+#define   RCU_DEBUG_1_CONTEXT_ACTIVE		REG_BIT(0)
+
 #define RCU_ASYNC_FLUSH				XE_REG(0x149fc)
 #define   RCU_ASYNC_FLUSH_IN_PROGRESS	REG_BIT(31)
 #define   RCU_ASYNC_FLUSH_ENGINE_ID_SHIFT	28
diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
new file mode 100644
index 000000000000..aa31b4c91713
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#include "xe_eudebug_hw.h"
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <generated/xe_wa_oob.h>
+
+#include "regs/xe_gt_regs.h"
+#include "regs/xe_engine_regs.h"
+
+#include "xe_eudebug.h"
+#include "xe_eudebug_types.h"
+#include "xe_exec_queue.h"
+#include "xe_exec_queue_types.h"
+#include "xe_force_wake.h"
+#include "xe_gt.h"
+#include "xe_gt_debug.h"
+#include "xe_gt_mcr.h"
+#include "xe_hw_engine.h"
+#include "xe_lrc.h"
+#include "xe_macros.h"
+#include "xe_mmio.h"
+#include "xe_reg_sr.h"
+#include "xe_rtp.h"
+#include "xe_wa.h"
+
+static void add_sr_entry(struct xe_hw_engine *hwe,
+			 struct xe_reg_mcr mcr_reg,
+			 u32 mask, bool enable)
+{
+	const struct xe_reg_sr_entry sr_entry = {
+		.reg = mcr_reg.__reg,
+		.clr_bits = mask,
+		.set_bits = enable ? mask : 0,
+		.read_mask = mask,
+	};
+
+	xe_reg_sr_add(&hwe->reg_sr, &sr_entry, hwe->gt, true);
+}
+
+void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe, bool enable)
+{
+	struct xe_gt *gt = hwe->gt;
+	struct xe_device *xe = gt_to_xe(gt);
+
+	if (!xe_rtp_match_first_render_or_compute(xe, gt, hwe))
+		return;
+
+	if (XE_GT_WA(gt, 18022722726))
+		add_sr_entry(hwe, ROW_CHICKEN,
+			     STALL_DOP_GATING_DISABLE, enable);
+
+	if (XE_GT_WA(gt, 14015474168))
+		add_sr_entry(hwe, ROW_CHICKEN2,
+			     XEHPC_DISABLE_BTB,
+			     enable);
+
+	if (xe->info.graphics_verx100 >= 1200)
+		add_sr_entry(hwe, TD_CTL,
+			     TD_CTL_BREAKPOINT_ENABLE |
+			     TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE |
+			     TD_CTL_FEH_AND_FEE_ENABLE,
+			     enable);
+
+	if (xe->info.graphics_verx100 >= 1250)
+		add_sr_entry(hwe, TD_CTL,
+			     TD_CTL_GLOBAL_DEBUG_ENABLE, enable);
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.h b/drivers/gpu/drm/xe/xe_eudebug_hw.h
new file mode 100644
index 000000000000..7362ed9bde68
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_hw.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#include <linux/types.h>
+
+#ifndef _XE_EUDEBUG_HW_H_
+#define _XE_EUDEBUG_HW_H_
+
+#include <linux/types.h>
+
+struct xe_eudebug;
+struct xe_hw_engine;
+struct xe_gt;
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+
+void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe, bool enable);
+
+#else /* CONFIG_DRM_XE_EUDEBUG */
+
+#endif /* CONFIG_DRM_XE_EUDEBUG */
+
+#endif /* _XE_EUDEBUG_HW_H_ */
diff --git a/drivers/gpu/drm/xe/xe_gt_debug.c b/drivers/gpu/drm/xe/xe_gt_debug.c
new file mode 100644
index 000000000000..314eef6734c3
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_debug.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#include "regs/xe_gt_regs.h"
+#include "xe_device.h"
+#include "xe_force_wake.h"
+#include "xe_gt.h"
+#include "xe_gt_topology.h"
+#include "xe_gt_debug.h"
+#include "xe_gt_mcr.h"
+#include "xe_pm.h"
+#include "xe_macros.h"
+
+unsigned int xe_gt_eu_att_regs(struct xe_gt *gt)
+{
+	return (GRAPHICS_VERx100(gt_to_xe(gt)) >= 3000) ? 2u : 1u;
+}
+
+int xe_gt_foreach_dss_group_instance(struct xe_gt *gt,
+				     int (*fn)(struct xe_gt *gt,
+					       void *data,
+					       u16 group,
+					       u16 instance,
+					       bool present),
+				     void *data)
+{
+	const enum xe_force_wake_domains fw_domains = XE_FW_GT;
+	xe_dss_mask_t dss_mask;
+	unsigned int dss, fw_ref;
+	u16 group, instance;
+	int ret = 0;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(gt), fw_domains);
+	if (!fw_ref)
+		return -ETIMEDOUT;
+
+	bitmap_or(dss_mask, gt->fuse_topo.g_dss_mask, gt->fuse_topo.c_dss_mask,
+		  XE_MAX_DSS_FUSE_BITS);
+
+	/*
+	 * Note: This removes terminating zeros when last dss is fused out!
+	 * In order bitmask to be exactly the same as on with i915 we would
+	 * need to figure out max dss for given platform, most probably by
+	 * querying hwconfig
+	 */
+
+	for (dss = 0;
+	     dss <= find_last_bit(dss_mask, XE_MAX_DSS_FUSE_BITS);
+	     dss++) {
+		xe_gt_mcr_get_dss_steering(gt, dss, &group, &instance);
+
+		ret = fn(gt, data, group, instance, test_bit(dss, dss_mask));
+		if (ret)
+			break;
+	}
+
+	xe_force_wake_put(gt_to_fw(gt), fw_ref);
+
+	return ret;
+}
+
+static int read_first_attention_mcr(struct xe_gt *gt, void *data,
+				    u16 group, u16 instance, bool present)
+{
+	unsigned int reg, row;
+
+	if (!present)
+		return 0;
+
+	for (reg = 0; reg < xe_gt_eu_att_regs(gt); reg++) {
+		for (row = 0; row < XE_GT_EU_ATT_ROWS; row++) {
+			u32 val;
+
+			val = xe_gt_mcr_unicast_read(gt, EU_ATT(reg, row), group, instance);
+
+			if (val)
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+#define MAX_EUS_PER_ROW 4u
+#define MAX_THREADS 8u
+
+/**
+ * xe_gt_eu_attention_bitmap_size - query size of the attention bitmask
+ *
+ * @gt: pointer to struct xe_gt
+ *
+ * Return: size in bytes.
+ */
+int xe_gt_eu_attention_bitmap_size(struct xe_gt *gt)
+{
+	xe_dss_mask_t dss_mask;
+
+	bitmap_or(dss_mask, gt->fuse_topo.c_dss_mask,
+		  gt->fuse_topo.g_dss_mask, XE_MAX_DSS_FUSE_BITS);
+
+	return (find_last_bit(dss_mask, XE_MAX_DSS_FUSE_BITS) + 1) *
+		XE_GT_EU_ATT_ROWS * xe_gt_eu_att_regs(gt) * MAX_THREADS *
+		MAX_EUS_PER_ROW / 8;
+}
+
+struct attn_read_iter {
+	struct xe_gt *gt;
+	unsigned int i;
+	unsigned int size;
+	u8 *bits;
+};
+
+static int read_eu_attentions_mcr(struct xe_gt *gt, void *data,
+				  u16 group, u16 instance, bool present)
+{
+	struct attn_read_iter * const iter = data;
+	unsigned int reg, row;
+
+	for (reg = 0; reg < xe_gt_eu_att_regs(gt); reg++) {
+		for (row = 0; row < XE_GT_EU_ATT_ROWS; row++) {
+			u32 val;
+
+			if (iter->i >= iter->size)
+				return 0;
+
+			XE_WARN_ON(iter->i + sizeof(val) > xe_gt_eu_attention_bitmap_size(gt));
+
+			if (present)
+				val = xe_gt_mcr_unicast_read(gt, EU_ATT(reg, row), group, instance);
+			else
+				val = 0;
+
+			memcpy(&iter->bits[iter->i], &val, sizeof(val));
+			iter->i += sizeof(val);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * xe_gt_eu_attention_bitmap - query host attention
+ *
+ * @gt: pointer to struct xe_gt
+ *
+ * Return: 0 on success, negative otherwise.
+ */
+int xe_gt_eu_attention_bitmap(struct xe_gt *gt, u8 *bits,
+			      unsigned int bitmap_size)
+{
+	struct attn_read_iter iter = {
+		.gt = gt,
+		.i = 0,
+		.size = bitmap_size,
+		.bits = bits
+	};
+
+	return xe_gt_foreach_dss_group_instance(gt, read_eu_attentions_mcr, &iter);
+}
+
+/**
+ * xe_gt_eu_threads_needing_attention - Query host attention
+ *
+ * @gt: pointer to struct xe_gt
+ *
+ * Return: 1 if threads waiting host attention, 0 otherwise.
+ */
+int xe_gt_eu_threads_needing_attention(struct xe_gt *gt)
+{
+	int err;
+
+	err = xe_gt_foreach_dss_group_instance(gt, read_first_attention_mcr, NULL);
+
+	XE_WARN_ON(err < 0);
+
+	return err < 0 ? 0 : err;
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_debug.h b/drivers/gpu/drm/xe/xe_gt_debug.h
new file mode 100644
index 000000000000..9dabe9cc1d25
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_debug.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023 Intel Corporation
+ */
+
+#ifndef __XE_GT_DEBUG_
+#define __XE_GT_DEBUG_
+
+#include <linux/bits.h>
+#include <linux/math.h>
+
+struct xe_gt;
+
+#define XE_GT_ATTENTION_TIMEOUT_MS 100
+#define XE_GT_EU_ATT_ROWS 2u
+
+unsigned int xe_gt_eu_att_regs(struct xe_gt *gt);
+
+int xe_gt_eu_threads_needing_attention(struct xe_gt *gt);
+int xe_gt_foreach_dss_group_instance(struct xe_gt *gt,
+				     int (*fn)(struct xe_gt *gt,
+					       void *data,
+					       u16 group,
+					       u16 instance,
+					       bool present),
+				     void *data);
+
+int xe_gt_eu_attention_bitmap_size(struct xe_gt *gt);
+int xe_gt_eu_attention_bitmap(struct xe_gt *gt, u8 *bits,
+			      unsigned int bitmap_size);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_reg_sr.c b/drivers/gpu/drm/xe/xe_reg_sr.c
index 7d60bf7ce3dc..8114b5588121 100644
--- a/drivers/gpu/drm/xe/xe_reg_sr.c
+++ b/drivers/gpu/drm/xe/xe_reg_sr.c
@@ -72,22 +72,31 @@ static void reg_sr_inc_error(struct xe_reg_sr *sr)
 
 int xe_reg_sr_add(struct xe_reg_sr *sr,
 		  const struct xe_reg_sr_entry *e,
-		  struct xe_gt *gt)
+		  struct xe_gt *gt,
+		  bool overwrite)
 {
 	unsigned long idx = e->reg.addr;
 	struct xe_reg_sr_entry *pentry = xa_load(&sr->xa, idx);
 	int ret;
 
 	if (pentry) {
-		if (!compatible_entries(pentry, e)) {
+		if (overwrite && e->set_bits) {
+			pentry->clr_bits |= e->clr_bits;
+			pentry->set_bits |= e->set_bits;
+			pentry->read_mask |= e->read_mask;
+		} else if (overwrite && !e->set_bits) {
+			pentry->clr_bits |= e->clr_bits;
+			pentry->set_bits &= ~e->clr_bits;
+			pentry->read_mask |= e->read_mask;
+		} else if (!compatible_entries(pentry, e)) {
 			ret = -EINVAL;
 			goto fail;
+		} else {
+			pentry->clr_bits |= e->clr_bits;
+			pentry->set_bits |= e->set_bits;
+			pentry->read_mask |= e->read_mask;
 		}
 
-		pentry->clr_bits |= e->clr_bits;
-		pentry->set_bits |= e->set_bits;
-		pentry->read_mask |= e->read_mask;
-
 		return 0;
 	}
 
diff --git a/drivers/gpu/drm/xe/xe_reg_sr.h b/drivers/gpu/drm/xe/xe_reg_sr.h
index 1ec6e8ecf278..3b88ffee3563 100644
--- a/drivers/gpu/drm/xe/xe_reg_sr.h
+++ b/drivers/gpu/drm/xe/xe_reg_sr.h
@@ -6,6 +6,8 @@
 #ifndef _XE_REG_SR_
 #define _XE_REG_SR_
 
+#include <linux/types.h>
+
 /*
  * Reg save/restore bookkeeping
  */
@@ -28,7 +30,7 @@ void xe_reg_sr_lrc_check(struct xe_reg_sr *sr,
 			 struct drm_printer *p);
 
 int xe_reg_sr_add(struct xe_reg_sr *sr, const struct xe_reg_sr_entry *e,
-		  struct xe_gt *gt);
+		  struct xe_gt *gt, bool overwrite);
 void xe_reg_sr_apply_mmio(struct xe_reg_sr *sr, struct xe_gt *gt);
 void xe_reg_sr_apply_whitelist(struct xe_hw_engine *hwe);
 
diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c
index 80577e4b7437..6631d0335240 100644
--- a/drivers/gpu/drm/xe/xe_reg_whitelist.c
+++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c
@@ -170,7 +170,7 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe)
 		}
 
 		xe_reg_whitelist_print_entry(&p, 0, reg, entry);
-		xe_reg_sr_add(&hwe->reg_sr, &hwe_entry, hwe->gt);
+		xe_reg_sr_add(&hwe->reg_sr, &hwe_entry, hwe->gt, false);
 
 		slot++;
 	}
diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c
index 7bfdc6795ce6..8c9df8ff6ad3 100644
--- a/drivers/gpu/drm/xe/xe_rtp.c
+++ b/drivers/gpu/drm/xe/xe_rtp.c
@@ -177,7 +177,7 @@ static void rtp_add_sr_entry(const struct xe_rtp_action *action,
 	};
 
 	sr_entry.reg.addr += mmio_base;
-	xe_reg_sr_add(sr, &sr_entry, gt);
+	xe_reg_sr_add(sr, &sr_entry, gt, false);
 }
 
 static bool rtp_process_one_sr(const struct xe_rtp_entry_sr *entry,
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index 5527c2cf4f81..c2c8f48e63c6 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -77,3 +77,5 @@
 		GRAPHICS_VERSION_RANGE(2004, 3005)
 14022766366	GRAPHICS_VERSION_RANGE(2001, 2004)
 		GRAPHICS_VERSION_RANGE(3000, 3005)
+18022722726	GRAPHICS_VERSION_RANGE(1250, 1274)
+14015474168	PLATFORM(PVC)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 14/22] drm/xe/eudebug: Introduce EU control interface
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (12 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 13/22] drm/xe/eudebug: hw enablement for eudebug Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 15/22] drm/xe/eudebug: Introduce per device attention scan worker Mika Kuoppala
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Dominik Grzegorzek, Mika Kuoppala

From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>

Introduce EU control functionality, which allows EU debugger
to interrupt, resume, and inform about the current state of
EU threads during execution. Provide an abstraction layer,
so in the future guc will only need to provide appropriate callbacks.

Based on implementation created by authors and other folks within
i915 driver.

v2: - checkpatch (Maciej)
    - lrc index off by one fix (Mika)
    - checkpatch (Tilak)
    - 32bit fixes (Andrzej, Mika)
    - find_resource_get for client (Mika)

v3: - fw ref (Mika)
    - attention register naming

v4: - fused off handling (Dominik)
    - squash xe3 parts and ptl attentions (Mika)
v5: - s/ioctl_lock/exec_queue.lock to avoid wrong lock order (Mika)
v6: - require seqno MBZ (Mika)

Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>

dont use ioctl lock when finding active lrc

require seqno zero on eu control
---
 Documentation/gpu/xe/xe_eudebug.rst      |   6 +
 drivers/gpu/drm/xe/regs/xe_engine_regs.h |   1 +
 drivers/gpu/drm/xe/xe_eudebug.c          |  52 ++
 drivers/gpu/drm/xe/xe_eudebug.h          |   2 +
 drivers/gpu/drm/xe/xe_eudebug_hw.c       | 661 +++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug_hw.h       |   7 +
 drivers/gpu/drm/xe/xe_eudebug_types.h    |  25 +
 include/uapi/drm/xe_drm_eudebug.h        |  58 ++
 8 files changed, 812 insertions(+)

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index 466d366c1e83..76f255c7da73 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -72,3 +72,9 @@ VM Access
 
 .. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
    :identifiers: drm_xe_eudebug_vm_open
+
+EU/HW Control
+=============
+
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_eu_control
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index 2e6c08c577ed..144b0028a9e3 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -152,6 +152,7 @@
 #define   IDLE_DELAY				REG_GENMASK(20, 0)
 
 #define RING_CURRENT_LRCA(base)			XE_REG((base) + 0x240)
+#define   CURRENT_LRCA_VALID			REG_BIT(0)
 
 #define RING_CONTEXT_CONTROL(base)		XE_REG((base) + 0x244, XE_REG_OPTION_MASKED)
 #define	  CTX_CTRL_PXP_ENABLE			REG_BIT(10)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index bbf01fefdc63..da28baa007c8 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -15,11 +15,14 @@
 #include "xe_debug_data_types.h"
 #include "xe_device.h"
 #include "xe_eudebug.h"
+#include "xe_eudebug_hw.h"
 #include "xe_eudebug_types.h"
 #include "xe_eudebug_vm.h"
 #include "xe_exec_queue.h"
+#include "xe_gt.h"
 #include "xe_hw_engine.h"
 #include "xe_macros.h"
+#include "xe_pm.h"
 #include "xe_sync.h"
 #include "xe_vm.h"
 
@@ -767,6 +770,29 @@ struct xe_vm *xe_eudebug_vm_get(struct xe_eudebug *d, u32 id)
 	return vm;
 }
 
+struct xe_exec_queue *xe_eudebug_exec_queue_get(struct xe_eudebug *d, u32 id)
+{
+	struct xe_exec_queue *q;
+
+	mutex_lock(&d->target.lock);
+	q = find_resource__unlocked(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, id);
+	if (q)
+		xe_exec_queue_get(q);
+	mutex_unlock(&d->target.lock);
+
+	return q;
+}
+
+struct xe_lrc *xe_eudebug_find_lrc(struct xe_eudebug *d, u32 id)
+{
+	struct xe_lrc *lrc;
+
+	mutex_lock(&d->target.lock);
+	lrc = find_resource__unlocked(d, XE_EUDEBUG_RES_TYPE_LRC, id);
+	mutex_unlock(&d->target.lock);
+
+	return lrc;
+}
 
 static struct drm_xe_eudebug_event *
 xe_eudebug_create_event(struct xe_eudebug *d, u16 type, u64 seqno, u16 flags,
@@ -1754,6 +1780,10 @@ static long xe_eudebug_ioctl(struct file *file,
 		ret = xe_eudebug_vm_open_ioctl(d, arg);
 		eu_dbg(d, "ioctl cmd=VM_OPEN ret=%ld\n", ret);
 		break;
+	case DRM_XE_EUDEBUG_IOCTL_EU_CONTROL:
+		ret = xe_eudebug_eu_control(d, arg);
+		eu_dbg(d, "ioctl cmd=EU_CONTROL ret=%ld\n", ret);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -1834,6 +1864,8 @@ xe_eudebug_connect(struct xe_device *xe,
 		goto err_detach;
 	}
 
+	xe_eudebug_hw_init(d);
+
 	kref_get(&d->ref);
 	queue_work(xe->eudebug.wq, &d->discovery_work);
 
@@ -1863,6 +1895,10 @@ bool xe_eudebug_is_enabled(struct xe_device *xe)
 
 int xe_eudebug_enable(struct xe_device *xe, bool enable)
 {
+	struct xe_gt *gt;
+	int i;
+	u8 id;
+
 	mutex_lock(&xe->eudebug.lock);
 
 	if (xe->eudebug.state == XE_EUDEBUG_NOT_SUPPORTED) {
@@ -1880,6 +1916,22 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
 		return 0;
 	}
 
+	xe_pm_runtime_get(xe);
+
+	for_each_gt(gt, xe, id) {
+		for (i = 0; i < ARRAY_SIZE(gt->hw_engines); i++) {
+			if (!(gt->info.engine_mask & BIT(i)))
+				continue;
+
+			xe_eudebug_init_hw_engine(&gt->hw_engines[i], enable);
+		}
+
+		xe_gt_reset_async(gt);
+		flush_work(&gt->reset.worker);
+	}
+
+	xe_pm_runtime_put(xe);
+
 	xe->eudebug.state = enable ?
 		XE_EUDEBUG_ENABLED : XE_EUDEBUG_DISABLED;
 	mutex_unlock(&xe->eudebug.lock);
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index 74171cc81fe1..bd9fd7bf454f 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -63,6 +63,8 @@ struct xe_vm *xe_eudebug_vm_get(struct xe_eudebug *d, u32 vm_id);
 
 void xe_eudebug_exec_queue_create(struct xe_file *xef, struct xe_exec_queue *q);
 void xe_eudebug_exec_queue_destroy(struct xe_file *xef, struct xe_exec_queue *q);
+struct xe_exec_queue *xe_eudebug_exec_queue_get(struct xe_eudebug *d, u32 id);
+struct xe_lrc *xe_eudebug_find_lrc(struct xe_eudebug *d, u32 id);
 
 void xe_eudebug_vm_bind_execute(struct xe_vm *vm, struct xe_vma_ops *ops);
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
index aa31b4c91713..3e2e3ab5aa45 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_hw.c
+++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
@@ -70,3 +70,664 @@ void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe, bool enable)
 		add_sr_entry(hwe, TD_CTL,
 			     TD_CTL_GLOBAL_DEBUG_ENABLE, enable);
 }
+
+static int __current_lrca(struct xe_hw_engine *hwe, u32 *lrc_hw)
+{
+	u32 lrc_reg;
+
+	lrc_reg = xe_hw_engine_mmio_read32(hwe, RING_CURRENT_LRCA(0));
+
+	if (!(lrc_reg & CURRENT_LRCA_VALID))
+		return -ENOENT;
+
+	*lrc_hw = lrc_reg & GENMASK(31, 12);
+
+	return 0;
+}
+
+static int current_lrca(struct xe_hw_engine *hwe, u32 *lrc_hw)
+{
+	unsigned int fw_ref;
+	int ret;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(hwe->gt), hwe->domain);
+	if (!fw_ref)
+		return -ETIMEDOUT;
+
+	ret = __current_lrca(hwe, lrc_hw);
+
+	xe_force_wake_put(gt_to_fw(hwe->gt), fw_ref);
+
+	return ret;
+}
+
+static bool lrca_equals(u32 a, u32 b)
+{
+	return (a & GENMASK(31, 12)) == (b & GENMASK(31, 12));
+}
+
+static int match_exec_queue_lrca(struct xe_exec_queue *q, u32 lrc_hw)
+{
+	int i;
+
+	for (i = 0; i < q->width; i++)
+		if (lrca_equals(lower_32_bits(xe_lrc_descriptor(q->lrc[i])), lrc_hw))
+			return i;
+
+	return -1;
+}
+
+static int rcu_debug1_engine_index(const struct xe_hw_engine * const hwe)
+{
+	if (hwe->class == XE_ENGINE_CLASS_RENDER) {
+		XE_WARN_ON(hwe->instance);
+		return 0;
+	}
+
+	XE_WARN_ON(hwe->instance > 3);
+
+	return hwe->instance + 1;
+}
+
+static u32 engine_status_xe1(const struct xe_hw_engine * const hwe,
+			     u32 rcu_debug1)
+{
+	const unsigned int first = 7;
+	const unsigned int incr = 3;
+	const unsigned int i = rcu_debug1_engine_index(hwe);
+	const unsigned int shift = first + (i * incr);
+
+	return (rcu_debug1 >> shift) & RCU_DEBUG_1_ENGINE_STATUS;
+}
+
+static u32 engine_status_xe2(const struct xe_hw_engine * const hwe,
+			     u32 rcu_debug1)
+{
+	const unsigned int first = 7;
+	const unsigned int incr = 4;
+	const unsigned int i = rcu_debug1_engine_index(hwe);
+	const unsigned int shift = first + (i * incr);
+
+	return (rcu_debug1 >> shift) & RCU_DEBUG_1_ENGINE_STATUS;
+}
+
+static u32 engine_status_xe3(const struct xe_hw_engine * const hwe,
+			     u32 rcu_debug1)
+{
+	const unsigned int first = 6;
+	const unsigned int incr = 4;
+	const unsigned int i = rcu_debug1_engine_index(hwe);
+	const unsigned int shift = first + (i * incr);
+
+	return (rcu_debug1 >> shift) & RCU_DEBUG_1_ENGINE_STATUS;
+}
+
+static u32 engine_status(const struct xe_hw_engine * const hwe,
+			 u32 rcu_debug1)
+{
+	u32 status = 0;
+
+	if (GRAPHICS_VER(gt_to_xe(hwe->gt)) < 20)
+		status = engine_status_xe1(hwe, rcu_debug1);
+	else if (GRAPHICS_VER(gt_to_xe(hwe->gt)) < 30)
+		status = engine_status_xe2(hwe, rcu_debug1);
+	else if (GRAPHICS_VER(gt_to_xe(hwe->gt)) < 35)
+		status = engine_status_xe3(hwe, rcu_debug1);
+	else
+		XE_WARN_ON(GRAPHICS_VER(gt_to_xe(hwe->gt)));
+
+	return status;
+}
+
+static bool engine_has_runalone_set(const struct xe_hw_engine * const hwe,
+				   u32 rcu_debug1)
+{
+	return engine_status(hwe, rcu_debug1) & RCU_DEBUG_1_RUNALONE_ACTIVE;
+}
+
+static bool engine_has_context_set(const struct xe_hw_engine * const hwe,
+				  u32 rcu_debug1)
+{
+	return engine_status(hwe, rcu_debug1) & RCU_DEBUG_1_CONTEXT_ACTIVE;
+}
+
+static struct xe_hw_engine *get_runalone_active_hw_engine(struct xe_gt *gt)
+{
+	struct xe_hw_engine *hwe, *first = NULL;
+	unsigned int num_active, id, fw_ref;
+	u32 val;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+	if (!fw_ref) {
+		drm_dbg(&gt_to_xe(gt)->drm, "eudbg: runalone failed to get force wake\n");
+		return NULL;
+	}
+
+	val = xe_mmio_read32(&gt->mmio, RCU_DEBUG_1);
+	xe_force_wake_put(gt_to_fw(gt), fw_ref);
+
+	drm_dbg(&gt_to_xe(gt)->drm, "eudbg: runalone RCU_DEBUG_1 = 0x%08x\n", val);
+
+	num_active = 0;
+	xe_eudebug_for_each_hw_engine(hwe, gt, id) {
+		bool runalone, ctx;
+
+		runalone = engine_has_runalone_set(hwe, val);
+		ctx = engine_has_context_set(hwe, val);
+
+		drm_dbg(&gt_to_xe(gt)->drm, "eudbg: engine %s: runalone=%s, context=%s",
+			hwe->name, runalone ? "active" : "inactive",
+			ctx ? "active" : "inactive");
+
+		/*
+		 * On earlier gen12 the context status seems to be idle when
+		 * it has raised attention. We have to omit the active bit.
+		 */
+		if (IS_DGFX(gt_to_xe(gt)))
+			ctx = true;
+
+		if (runalone && ctx) {
+			num_active++;
+
+			drm_dbg(&gt_to_xe(gt)->drm, "eudbg: runalone engine %s %s",
+				hwe->name, first ? "selected" : "found");
+			if (!first)
+				first = hwe;
+		}
+	}
+
+	if (num_active > 1)
+		drm_err(&gt_to_xe(gt)->drm, "eudbg: %d runalone engines active!",
+			num_active);
+
+	return first;
+}
+
+static struct xe_exec_queue *active_hwe_to_exec_queue(struct xe_hw_engine *hwe,
+						      int *lrc_idx)
+{
+	struct xe_device *xe = gt_to_xe(hwe->gt);
+	struct xe_gt *gt = hwe->gt;
+	struct xe_exec_queue *q, *found = NULL;
+	struct xe_file *xef;
+	unsigned long i;
+	int idx, err;
+	u32 lrc_hw;
+
+	err = current_lrca(hwe, &lrc_hw);
+	if (err)
+		return ERR_PTR(err);
+
+	mutex_lock(&xe->eudebug.lock);
+	list_for_each_entry(xef, &xe->eudebug.targets, eudebug.target_link) {
+		mutex_lock(&xef->exec_queue.lock);
+		xa_for_each(&xef->exec_queue.xa, i, q) {
+			if (q->gt != gt)
+				continue;
+
+			if (q->class != hwe->class)
+				continue;
+
+			if (xe_exec_queue_is_idle(q))
+				continue;
+
+			idx = match_exec_queue_lrca(q, lrc_hw);
+			if (idx < 0)
+				continue;
+
+			found = xe_exec_queue_get(q);
+
+			if (lrc_idx)
+				*lrc_idx = idx;
+
+			break;
+		}
+		mutex_unlock(&xef->exec_queue.lock);
+
+		if (found)
+			break;
+	}
+	mutex_unlock(&xe->eudebug.lock);
+
+	if (!found)
+		return ERR_PTR(-ENOENT);
+
+	if (XE_WARN_ON(current_lrca(hwe, &lrc_hw)) &&
+	    XE_WARN_ON(match_exec_queue_lrca(found, lrc_hw) < 0)) {
+		xe_exec_queue_put(found);
+		return ERR_PTR(-ENOENT);
+	}
+
+	return found;
+}
+
+static struct xe_exec_queue *runalone_active_queue_get(struct xe_gt *gt, int *lrc_idx)
+{
+	struct xe_hw_engine *active;
+
+	active = get_runalone_active_hw_engine(gt);
+	if (!active) {
+		drm_dbg(&gt_to_xe(gt)->drm, "Runalone engine not found!");
+		return ERR_PTR(-ENOENT);
+	}
+
+	return active_hwe_to_exec_queue(active, lrc_idx);
+}
+
+static int do_eu_control(struct xe_eudebug *d,
+			 const struct drm_xe_eudebug_eu_control * const arg,
+			 struct drm_xe_eudebug_eu_control __user * const user_ptr)
+{
+	void __user * const bitmask_ptr = u64_to_user_ptr(arg->bitmask_ptr);
+	struct xe_device *xe = d->xe;
+	u8 *bits = NULL;
+	unsigned int hw_attn_size, attn_size;
+	struct xe_exec_queue *q;
+	struct xe_lrc *lrc;
+	u64 seqno;
+	int ret;
+
+	if (xe_eudebug_detached(d))
+		return -ENOTCONN;
+
+	/* Accept only hardware reg granularity mask */
+	if (XE_IOCTL_DBG(xe, !IS_ALIGNED(arg->bitmask_size, sizeof(u32))))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, arg->seqno))
+		return -EINVAL;
+
+	q = xe_eudebug_exec_queue_get(d, arg->exec_queue_handle);
+	if (XE_IOCTL_DBG(xe, !q))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_debuggable(q))) {
+		ret = -EINVAL;
+		goto queue_put;
+	}
+
+	lrc = xe_eudebug_find_lrc(d, arg->lrc_handle);
+	if (XE_IOCTL_DBG(xe, !lrc)) {
+		ret = -EINVAL;
+		goto queue_put;
+	}
+
+	hw_attn_size = xe_gt_eu_attention_bitmap_size(q->gt);
+	attn_size = arg->bitmask_size;
+
+	if (attn_size > hw_attn_size)
+		attn_size = hw_attn_size;
+
+	if (attn_size > 0) {
+		bits = kmalloc(attn_size, GFP_KERNEL);
+		if (!bits) {
+			ret =  -ENOMEM;
+			goto queue_put;
+		}
+
+		if (copy_from_user(bits, bitmask_ptr, attn_size)) {
+			ret = -EFAULT;
+			goto out_free;
+		}
+	}
+
+	if (!pm_runtime_active(xe->drm.dev)) {
+		ret = -EIO;
+		goto out_free;
+	}
+
+	ret = -EINVAL;
+	mutex_lock(&d->hw.lock);
+
+	switch (arg->cmd) {
+	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL:
+		/* Make sure we dont promise anything but interrupting all */
+		if (!attn_size)
+			ret = d->ops->interrupt_all(d, q, lrc);
+		break;
+	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_STOPPED:
+		ret = d->ops->stopped(d, q, lrc, bits, attn_size);
+		break;
+	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_RESUME:
+		ret = d->ops->resume(d, q, lrc, bits, attn_size);
+		break;
+	default:
+		break;
+	}
+
+	if (ret == 0)
+		seqno = atomic_long_inc_return(&d->events.seqno);
+
+	mutex_unlock(&d->hw.lock);
+
+	if (ret)
+		goto out_free;
+
+	if (put_user(seqno, &user_ptr->seqno)) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+
+	if (copy_to_user(bitmask_ptr, bits, attn_size)) {
+		ret = -EFAULT;
+		goto out_free;
+	}
+
+	if (hw_attn_size != arg->bitmask_size)
+		if (put_user(hw_attn_size, &user_ptr->bitmask_size))
+			ret = -EFAULT;
+
+out_free:
+	kfree(bits);
+queue_put:
+	xe_exec_queue_put(q);
+
+	return ret;
+}
+
+static int xe_eu_control_interrupt_all(struct xe_eudebug *d,
+				       struct xe_exec_queue *q,
+				       struct xe_lrc *lrc)
+{
+	struct xe_gt *gt = q->hwe->gt;
+	struct xe_device *xe = d->xe;
+	struct xe_exec_queue *active;
+	struct xe_hw_engine *hwe;
+	unsigned int fw_ref;
+	int lrc_idx, ret;
+	u32 lrc_hw;
+	u32 td_ctl;
+
+	hwe = get_runalone_active_hw_engine(gt);
+	if (XE_IOCTL_DBG(xe, !hwe)) {
+		drm_dbg(&gt_to_xe(gt)->drm, "Runalone engine not found!");
+		return -EINVAL;
+	}
+
+	active = active_hwe_to_exec_queue(hwe, &lrc_idx);
+	if (XE_IOCTL_DBG(xe, IS_ERR(active)))
+		return PTR_ERR(active);
+
+	if (XE_IOCTL_DBG(xe, q != active)) {
+		xe_exec_queue_put(active);
+		return -EINVAL;
+	}
+	xe_exec_queue_put(active);
+
+	if (XE_IOCTL_DBG(xe, lrc_idx >= q->width || q->lrc[lrc_idx] != lrc))
+		return -EINVAL;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(gt), hwe->domain);
+	if (!fw_ref)
+		return -ETIMEDOUT;
+
+	/* Additional check just before issuing MMIO writes */
+	ret = __current_lrca(hwe, &lrc_hw);
+	if (ret)
+		goto put_fw;
+
+	if (!lrca_equals(lower_32_bits(xe_lrc_descriptor(lrc)), lrc_hw)) {
+		ret = -EBUSY;
+		goto put_fw;
+	}
+
+	td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
+
+	/* Halt on next thread dispatch */
+	if (!(td_ctl & TD_CTL_FORCE_EXTERNAL_HALT))
+		xe_gt_mcr_multicast_write(gt, TD_CTL,
+					  td_ctl | TD_CTL_FORCE_EXTERNAL_HALT);
+	else
+		eu_warn(d, "TD_CTL force external halt bit already set!\n");
+
+	/*
+	 * The sleep is needed because some interrupts are ignored
+	 * by the HW, hence we allow the HW some time to acknowledge
+	 * that.
+	 */
+	usleep_range(100, 110);
+
+	/* Halt regardless of thread dependencies */
+	if (!(td_ctl & TD_CTL_FORCE_EXCEPTION))
+		xe_gt_mcr_multicast_write(gt, TD_CTL,
+					  td_ctl | TD_CTL_FORCE_EXCEPTION);
+	else
+		eu_warn(d, "TD_CTL force exception bit already set!\n");
+
+	usleep_range(100, 110);
+
+	xe_gt_mcr_multicast_write(gt, TD_CTL, td_ctl &
+				  ~(TD_CTL_FORCE_EXTERNAL_HALT | TD_CTL_FORCE_EXCEPTION));
+
+	/*
+	 * In case of stopping wrong ctx emit warning.
+	 * Nothing else we can do for now.
+	 */
+	ret = __current_lrca(hwe, &lrc_hw);
+	if (ret || !lrca_equals(lower_32_bits(xe_lrc_descriptor(lrc)), lrc_hw))
+		eu_warn(d, "xe_eudebug: interrupted wrong context.");
+
+put_fw:
+	xe_force_wake_put(gt_to_fw(gt), fw_ref);
+
+	return ret;
+}
+
+struct ss_iter {
+	struct xe_eudebug *debugger;
+	unsigned int i;
+
+	unsigned int size;
+	u8 *bits;
+};
+
+static int check_attn_mcr(struct xe_gt *gt, void *data,
+			  u16 group, u16 instance, bool present)
+{
+	struct ss_iter *iter = data;
+	struct xe_eudebug *d = iter->debugger;
+	unsigned int reg, row;
+
+	for (reg = 0; reg < xe_gt_eu_att_regs(gt); reg++) {
+		for (row = 0; row < XE_GT_EU_ATT_ROWS; row++) {
+			u32 val, cur = 0;
+
+			if (iter->i >= iter->size)
+				return 0;
+
+			if (XE_WARN_ON((iter->i + sizeof(val)) >
+					(xe_gt_eu_attention_bitmap_size(gt))))
+				return -EIO;
+
+			memcpy(&val, &iter->bits[iter->i], sizeof(val));
+			iter->i += sizeof(val);
+
+			if (present)
+				cur = xe_gt_mcr_unicast_read(gt, EU_ATT(reg, row), group, instance);
+
+			if ((val | cur) != cur) {
+				eu_dbg(d,
+				       "WRONG CLEAR (%u:%u:%u:%u) EU_ATT_CLR: 0x%08x; EU_ATT: 0x%08x\n",
+				       group, instance, reg, row, val, cur);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int clear_attn_mcr(struct xe_gt *gt, void *data,
+			  u16 group, u16 instance, bool present)
+{
+	struct ss_iter *iter = data;
+	struct xe_eudebug *d = iter->debugger;
+	unsigned int reg, row;
+
+	for (reg = 0; reg < xe_gt_eu_att_regs(gt); reg++) {
+		for (row = 0; row < XE_GT_EU_ATT_ROWS; row++) {
+			u32 val;
+
+			if (iter->i >= iter->size)
+				return 0;
+
+			if (XE_WARN_ON((iter->i + sizeof(val)) >
+					(xe_gt_eu_attention_bitmap_size(gt))))
+				return -EIO;
+
+			memcpy(&val, &iter->bits[iter->i], sizeof(val));
+			iter->i += sizeof(val);
+
+			if (!val)
+				continue;
+
+			if (present) {
+				xe_gt_mcr_unicast_write(gt, EU_ATT_CLR(reg, row), val,
+							group, instance);
+
+				eu_dbg(d,
+				       "EU_ATT_CLR: (%u:%u:%u:%u): 0x%08x\n",
+				       group, instance, reg, row, val);
+			} else {
+				eu_warn(d,
+					"EU_ATT_CLR: (%u:%u:%u:%u): 0x%08x to fused off dss\n",
+					group, instance, reg, row, val);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int xe_eu_control_resume(struct xe_eudebug *d,
+				struct xe_exec_queue *q,
+				struct xe_lrc *lrc,
+				u8 *bits, unsigned int bitmask_size)
+{
+	struct xe_device *xe = d->xe;
+	struct ss_iter iter = {
+		.debugger = d,
+		.i = 0,
+		.size = bitmask_size,
+		.bits = bits
+	};
+	int ret = 0;
+	struct xe_exec_queue *active;
+	int lrc_idx;
+
+	active = runalone_active_queue_get(q->gt, &lrc_idx);
+	if (IS_ERR(active))
+		return PTR_ERR(active);
+
+	if (XE_IOCTL_DBG(xe, q != active)) {
+		xe_exec_queue_put(active);
+		return -EBUSY;
+	}
+	xe_exec_queue_put(active);
+
+	if (XE_IOCTL_DBG(xe, lrc_idx >= q->width || q->lrc[lrc_idx] != lrc))
+		return -EBUSY;
+
+	/*
+	 * hsdes: 18021122357
+	 * We need to avoid clearing attention bits that are not set
+	 * in order to avoid the EOT hang on PVC.
+	 */
+	if (GRAPHICS_VERx100(d->xe) == 1260) {
+		ret = xe_gt_foreach_dss_group_instance(q->gt, check_attn_mcr, &iter);
+		if (ret)
+			return ret;
+
+		iter.i = 0;
+	}
+
+	xe_gt_foreach_dss_group_instance(q->gt, clear_attn_mcr, &iter);
+	return 0;
+}
+
+static int xe_eu_control_stopped(struct xe_eudebug *d,
+				 struct xe_exec_queue *q,
+				 struct xe_lrc *lrc,
+				 u8 *bits, unsigned int bitmask_size)
+{
+	struct xe_device *xe = d->xe;
+	struct xe_exec_queue *active;
+	int lrc_idx;
+
+	if (XE_WARN_ON(!q) || XE_WARN_ON(!q->gt))
+		return -EINVAL;
+
+	active = runalone_active_queue_get(q->gt, &lrc_idx);
+	if (IS_ERR(active))
+		return PTR_ERR(active);
+
+	if (active) {
+		if (XE_IOCTL_DBG(xe, q != active)) {
+			xe_exec_queue_put(active);
+			return -EBUSY;
+		}
+
+		if (XE_IOCTL_DBG(xe, lrc_idx >= q->width || q->lrc[lrc_idx] != lrc)) {
+			xe_exec_queue_put(active);
+			return -EBUSY;
+		}
+	}
+
+	xe_exec_queue_put(active);
+
+	return xe_gt_eu_attention_bitmap(q->gt, bits, bitmask_size);
+}
+
+static struct xe_eudebug_eu_control_ops eu_control = {
+	.interrupt_all = xe_eu_control_interrupt_all,
+	.stopped = xe_eu_control_stopped,
+	.resume = xe_eu_control_resume,
+};
+
+void xe_eudebug_hw_init(struct xe_eudebug *d)
+{
+	d->ops = &eu_control;
+}
+
+long xe_eudebug_eu_control(struct xe_eudebug *d, const u64 arg)
+{
+	struct drm_xe_eudebug_eu_control __user * const user_ptr =
+		u64_to_user_ptr(arg);
+	struct drm_xe_eudebug_eu_control user_arg;
+	struct xe_device *xe = d->xe;
+	int ret;
+
+	if (XE_IOCTL_DBG(xe, !(_IOC_DIR(DRM_XE_EUDEBUG_IOCTL_EU_CONTROL) & _IOC_WRITE)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, !(_IOC_DIR(DRM_XE_EUDEBUG_IOCTL_EU_CONTROL) & _IOC_READ)))
+		return -EINVAL;
+
+	if (XE_IOCTL_DBG(xe, _IOC_SIZE(DRM_XE_EUDEBUG_IOCTL_EU_CONTROL) != sizeof(user_arg)))
+		return -EINVAL;
+
+	if (copy_from_user(&user_arg,
+			   user_ptr,
+			   sizeof(user_arg)))
+		return -EFAULT;
+
+	if (XE_IOCTL_DBG(xe, user_arg.flags))
+		return -EINVAL;
+
+	if (!access_ok(u64_to_user_ptr(user_arg.bitmask_ptr), user_arg.bitmask_size))
+		return -EFAULT;
+
+	eu_dbg(d,
+	       "eu_control: cmd=%u, flags=0x%x, exec_queue_handle=%llu, bitmask_size=%u\n",
+	       user_arg.cmd, user_arg.flags, user_arg.exec_queue_handle,
+	       user_arg.bitmask_size);
+
+	ret = do_eu_control(d, &user_arg, user_ptr);
+
+	eu_dbg(d,
+	       "eu_control: cmd=%u, flags=0x%x, exec_queue_handle=%llu, bitmask_size=%u ret=%d\n",
+	       user_arg.cmd, user_arg.flags, user_arg.exec_queue_handle,
+	       user_arg.bitmask_size, ret);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.h b/drivers/gpu/drm/xe/xe_eudebug_hw.h
index 7362ed9bde68..8f59ec574e4e 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_hw.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_hw.h
@@ -16,10 +16,17 @@ struct xe_gt;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
+void xe_eudebug_hw_init(struct xe_eudebug *d);
 void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe, bool enable);
 
+long xe_eudebug_eu_control(struct xe_eudebug *d, const u64 arg);
+
+struct xe_exec_queue *xe_gt_runalone_active_queue_get(struct xe_gt *gt, int *lrc_idx);
+
 #else /* CONFIG_DRM_XE_EUDEBUG */
 
+static inline void xe_eudebug_init_hw_engine(struct xe_hw_engine *hwe, bool enable) { }
+
 #endif /* CONFIG_DRM_XE_EUDEBUG */
 
 #endif /* _XE_EUDEBUG_HW_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 10d19a43ba6b..57bd82a02ecb 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -17,7 +17,11 @@
 
 struct xe_device;
 struct task_struct;
+struct xe_eudebug;
+struct xe_hw_engine;
 struct workqueue_struct;
+struct xe_exec_queue;
+struct xe_lrc;
 
 /**
  * enum xe_eudebug_state - eudebug capability state
@@ -65,6 +69,24 @@ struct xe_eudebug_resource {
 #define XE_EUDEBUG_RES_TYPE_LRC		2
 #define XE_EUDEBUG_RES_TYPE_COUNT	(XE_EUDEBUG_RES_TYPE_LRC + 1)
 
+/**
+ * struct xe_eudebug_eu_control_ops - interface for eu thread
+ * state control backend
+ */
+struct xe_eudebug_eu_control_ops {
+	/** @interrupt_all: interrupts workload active on given hwe */
+	int (*interrupt_all)(struct xe_eudebug *e, struct xe_exec_queue *q,
+			     struct xe_lrc *lrc);
+
+	/** @resume: resumes threads reflected by bitmask active on given hwe */
+	int (*resume)(struct xe_eudebug *e, struct xe_exec_queue *q,
+		      struct xe_lrc *lrc, u8 *bitmap, unsigned int bitmap_size);
+
+	/** @stopped: returns bitmap reflecting threads which signal attention */
+	int (*stopped)(struct xe_eudebug *e, struct xe_exec_queue *q,
+		       struct xe_lrc *lrc, u8 *bitmap, unsigned int bitmap_size);
+};
+
 /**
  * struct xe_eudebug - Top level struct for eudebug: the connection
  */
@@ -139,6 +161,9 @@ struct xe_eudebug {
 		/** @lock: guards access to hw state */
 		struct mutex lock;
 	} hw;
+
+	/** @ops: operations for eu_control */
+	struct xe_eudebug_eu_control_ops *ops;
 };
 
 #endif /* _XE_EUDEBUG_TYPES_H_ */
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 029a51340777..6d69e100c965 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -13,6 +13,7 @@ extern "C" {
 #define DRM_XE_EUDEBUG_IOCTL_READ_EVENT		_IO('j', 0x0)
 #define DRM_XE_EUDEBUG_IOCTL_ACK_EVENT		_IOW('j', 0x1, struct drm_xe_eudebug_ack)
 #define DRM_XE_EUDEBUG_IOCTL_VM_OPEN		_IOW('j', 0x2, struct drm_xe_eudebug_vm_open)
+#define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL		_IOWR('j', 0x3, struct drm_xe_eudebug_eu_control)
 
 /**
  * struct drm_xe_eudebug_event - Base type of event delivered by xe_eudebug.
@@ -271,6 +272,63 @@ struct drm_xe_eudebug_vm_open {
 	__u64 timeout_ns;
 };
 
+/**
+ * struct drm_xe_eudebug_eu_control - Control EU states
+ *
+ * Issue commands to execution units in hardware.
+ *
+ * With DRM_XE_EUDEBUG_IOCTL_EU_CONTROL debugger can
+ * interrupt all threads on execution units, query thread
+ * state and resume execution.
+ *
+ * :c:member:`drm_xe_eudebug_eu_control.seqno`
+ * will be updated to the timeline point when
+ * the command was issued.
+ *
+ * :c:member:`drm_xe_eudebug_eu_control.cmd` can
+ * be following:
+ *
+ * *DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL*
+ *  will instruct hardware to stop all threads on EUs
+ *  for exec_queue:lrc
+ *
+ * *DRM_XE_EUDEBUG_EU_CONTROL_CMD_STOPPED*
+ *  returns the bitmask for threads that are
+ *  in so called attention state.
+ *
+ * *DRM_XE_EUDEBUG_EU_CONTROL_CMD_RESUME* resumes the
+ *  threads that the bitmask is set.
+ *
+ */
+struct drm_xe_eudebug_eu_control {
+	/** @cmd: Command for execution units */
+#define DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL	0
+#define DRM_XE_EUDEBUG_EU_CONTROL_CMD_STOPPED		1
+#define DRM_XE_EUDEBUG_EU_CONTROL_CMD_RESUME		2
+	__u32 cmd;
+
+	/** @flags: Flags, must be set to zero */
+	__u32 flags;
+
+	/** @seqno: Seqno, must be set to zero */
+	__u64 seqno;
+
+	/** @exec_queue_handle: Exec queue handle for the command */
+	__u64 exec_queue_handle;
+
+	/** @lrc_handle: LRC handle for the command */
+	__u64 lrc_handle;
+
+	/** @reserved: Reserved field, must be set to zero */
+	__u32 reserved;
+
+	/** @bitmask_size: Bitmask size in bytes */
+	__u32 bitmask_size;
+
+	/** @bitmask_ptr: Bitmask pointer, each bit is one thread */
+	__u64 bitmask_ptr;
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 15/22] drm/xe/eudebug: Introduce per device attention scan worker
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (13 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 14/22] drm/xe/eudebug: Introduce EU control interface Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 16/22] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test Mika Kuoppala
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Dominik Grzegorzek, Mika Kuoppala

From: Dominik Grzegorzek <dominik.grzegorzek@intel.com>

Scan for EU debugging attention bits periodically to detect if some EU
thread has entered the system routine (SIP) due to EU thread exception.

Make the scanning interval 10 times slower when there is no debugger
connection open. Send attention event whenever we see attention with
debugger presence. If there is no debugger connection active - reset.

Based on work by authors and other folks who were part of attentions in
i915.

v2: - use xa_array for files
    - null ptr deref fix for non-debugged context (Dominik)
    - checkpatch (Tilak)
    - use discovery_lock during list traversal

v3: - engine status per gen improvements, force_wake ref
    - __counted_by (Mika)

v4: - attention register naming (Dominik)

Signed-off-by: Dominik Grzegorzek <dominik.grzegorzek@intel.com>
Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 Documentation/gpu/xe/xe_eudebug.rst   |   3 +
 drivers/gpu/drm/xe/xe_device_types.h  |   3 +
 drivers/gpu/drm/xe/xe_eudebug.c       | 170 ++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug_hw.c    |   6 +-
 drivers/gpu/drm/xe/xe_eudebug_types.h |   3 +-
 include/uapi/drm/xe_drm_eudebug.h     |  29 +++++
 6 files changed, 209 insertions(+), 5 deletions(-)

diff --git a/Documentation/gpu/xe/xe_eudebug.rst b/Documentation/gpu/xe/xe_eudebug.rst
index 76f255c7da73..29f70b023326 100644
--- a/Documentation/gpu/xe/xe_eudebug.rst
+++ b/Documentation/gpu/xe/xe_eudebug.rst
@@ -67,6 +67,9 @@ Resource Event Types
 .. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
    :identifiers: drm_xe_eudebug_event_vm_bind_ufence
 
+.. kernel-doc:: include/uapi/drm/xe_drm_eudebug.h
+   :identifiers: drm_xe_eudebug_event_eu_attention
+
 VM Access
 =========
 
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 21e749c6a635..985c294e94de 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -585,6 +585,9 @@ struct xe_device {
 
 		/** @wq: used for client discovery */
 		struct workqueue_struct *wq;
+
+		/** @attention_poll: attention poll work */
+		struct delayed_work attention_dwork;
 	} eudebug;
 #endif
 };
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index da28baa007c8..0c67db86f009 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -21,6 +21,8 @@
 #include "xe_exec_queue.h"
 #include "xe_gt.h"
 #include "xe_hw_engine.h"
+#include "xe_gt.h"
+#include "xe_gt_debug.h"
 #include "xe_macros.h"
 #include "xe_pm.h"
 #include "xe_sync.h"
@@ -1799,6 +1801,154 @@ static const struct file_operations fops = {
 	.unlocked_ioctl	= xe_eudebug_ioctl,
 };
 
+static int send_attention_event(struct xe_eudebug *d, struct xe_exec_queue *q, int lrc_idx)
+{
+	struct drm_xe_eudebug_event_eu_attention *e;
+	struct drm_xe_eudebug_event *event;
+	const u32 size = xe_gt_eu_attention_bitmap_size(q->gt);
+	const u32 sz = struct_size(e, bitmask, size);
+	int h_queue, h_lrc;
+	int ret;
+
+	XE_WARN_ON(lrc_idx < 0 || lrc_idx >= q->width);
+
+	XE_WARN_ON(!xe_exec_queue_is_debuggable(q));
+
+	h_queue = find_handle(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, q);
+	if (h_queue < 0)
+		return h_queue;
+
+	h_lrc = find_handle(d, XE_EUDEBUG_RES_TYPE_LRC, q->lrc[lrc_idx]);
+	if (h_lrc < 0)
+		return h_lrc;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_EU_ATTENTION, 0,
+					DRM_XE_EUDEBUG_EVENT_STATE_CHANGE, sz);
+
+	if (!event)
+		return -ENOSPC;
+
+	e = cast_event(e, event);
+	e->exec_queue_handle = h_queue;
+	e->lrc_handle = h_lrc;
+	e->bitmask_size = size;
+
+	mutex_lock(&d->hw.lock);
+	event->seqno = atomic_long_inc_return(&d->events.seqno);
+	ret = xe_gt_eu_attention_bitmap(q->gt, &e->bitmask[0], e->bitmask_size);
+	mutex_unlock(&d->hw.lock);
+
+	if (ret)
+		return ret;
+
+	return xe_eudebug_queue_event(d, event);
+}
+
+static int xe_send_gt_attention(struct xe_gt *gt)
+{
+	struct xe_eudebug *d;
+	struct xe_exec_queue *q;
+	int ret, lrc_idx;
+
+	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
+	if (IS_ERR(q))
+		return PTR_ERR(q);
+
+	if (!xe_exec_queue_is_debuggable(q)) {
+		ret = -EPERM;
+		goto err_exec_queue_put;
+	}
+
+	d = xe_eudebug_get_nolock(q->vm->xef);
+	if (!d) {
+		ret = -ENOTCONN;
+		goto err_exec_queue_put;
+	}
+
+	if (!completion_done(&d->discovery)) {
+		eu_dbg(d, "discovery not yet done\n");
+		ret = -EBUSY;
+		goto err_eudebug_put;
+	}
+
+	ret = send_attention_event(d, q, lrc_idx);
+	if (ret)
+		xe_eudebug_disconnect(d, ret);
+
+err_eudebug_put:
+	xe_eudebug_put(d);
+err_exec_queue_put:
+	xe_exec_queue_put(q);
+
+	return ret;
+}
+
+static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
+{
+	int ret;
+
+	ret = xe_gt_eu_threads_needing_attention(gt);
+	if (ret <= 0)
+		return ret;
+
+	ret = xe_send_gt_attention(gt);
+
+	/* Discovery in progress, fake it */
+	if (ret == -EBUSY)
+		return 0;
+
+	return ret;
+}
+
+static void attention_poll_work(struct work_struct *work)
+{
+	struct xe_device *xe = container_of(work, typeof(*xe),
+					    eudebug.attention_dwork.work);
+	const unsigned int poll_interval_ms = 100;
+	long delay = msecs_to_jiffies(poll_interval_ms);
+	struct xe_gt *gt;
+	u8 gt_id;
+
+	if (list_empty(&xe->eudebug.targets))
+		delay *= 11;
+
+	if (delay >= HZ)
+		delay = round_jiffies_up_relative(delay);
+
+	if (xe_pm_runtime_get_if_active(xe)) {
+		for_each_gt(gt, xe, gt_id) {
+			int ret;
+
+			if (gt->info.type != XE_GT_TYPE_MAIN)
+				continue;
+
+			ret = xe_eudebug_handle_gt_attention(gt);
+			if (ret) {
+				/* TODO: error capture */
+				drm_info(&gt_to_xe(gt)->drm,
+					 "gt:%d unable to handle eu attention ret=%d\n",
+					 gt_id, ret);
+
+				xe_gt_reset_async(gt);
+			}
+		}
+
+		xe_pm_runtime_put(xe);
+	}
+
+	schedule_delayed_work(&xe->eudebug.attention_dwork, delay);
+}
+
+static void attention_poll_stop(struct xe_device *xe)
+{
+	cancel_delayed_work_sync(&xe->eudebug.attention_dwork);
+}
+
+static void attention_poll_start(struct xe_device *xe)
+{
+	mod_delayed_work(system_wq, &xe->eudebug.attention_dwork, 0);
+}
+
 static int
 xe_eudebug_connect(struct xe_device *xe,
 		   struct drm_file *file,
@@ -1868,6 +2018,7 @@ xe_eudebug_connect(struct xe_device *xe,
 
 	kref_get(&d->ref);
 	queue_work(xe->eudebug.wq, &d->discovery_work);
+	attention_poll_start(xe);
 
 	eu_dbg(d, "connected session %lld", d->session);
 
@@ -1936,6 +2087,11 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
 		XE_EUDEBUG_ENABLED : XE_EUDEBUG_DISABLED;
 	mutex_unlock(&xe->eudebug.lock);
 
+	if (enable)
+		attention_poll_start(xe);
+	else
+		attention_poll_stop(xe);
+
 	return 0;
 }
 
@@ -1977,6 +2133,15 @@ static void xe_eudebug_sysfs_fini(void *arg)
 			  &dev_attr_enable_eudebug.attr);
 }
 
+static void xe_eudebug_fini(struct drm_device *dev, void *__unused)
+{
+	struct xe_device *xe = to_xe_device(dev);
+
+	xe_assert(xe, list_empty(&xe->eudebug.targets));
+
+	attention_poll_stop(xe);
+}
+
 void xe_eudebug_init(struct xe_device *xe)
 {
 	struct drm_device *dev = &xe->drm;
@@ -1984,6 +2149,7 @@ void xe_eudebug_init(struct xe_device *xe)
 	int err;
 
 	INIT_LIST_HEAD(&xe->eudebug.targets);
+	INIT_DELAYED_WORK(&xe->eudebug.attention_dwork, attention_poll_work);
 
 	xe->eudebug.state = XE_EUDEBUG_NOT_SUPPORTED;
 
@@ -1998,6 +2164,10 @@ void xe_eudebug_init(struct xe_device *xe)
 	}
 	xe->eudebug.wq = wq;
 
+	err = drmm_add_action_or_reset(&xe->drm, xe_eudebug_fini, NULL);
+	if (err)
+		goto out_err;
+
 	err = sysfs_create_file(&dev->dev->kobj,
 				&dev_attr_enable_eudebug.attr);
 	if (err)
diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
index 3e2e3ab5aa45..5365265a67b3 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_hw.c
+++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
@@ -301,7 +301,7 @@ static struct xe_exec_queue *active_hwe_to_exec_queue(struct xe_hw_engine *hwe,
 	return found;
 }
 
-static struct xe_exec_queue *runalone_active_queue_get(struct xe_gt *gt, int *lrc_idx)
+struct xe_exec_queue *xe_gt_runalone_active_queue_get(struct xe_gt *gt, int *lrc_idx)
 {
 	struct xe_hw_engine *active;
 
@@ -615,7 +615,7 @@ static int xe_eu_control_resume(struct xe_eudebug *d,
 	struct xe_exec_queue *active;
 	int lrc_idx;
 
-	active = runalone_active_queue_get(q->gt, &lrc_idx);
+	active = xe_gt_runalone_active_queue_get(q->gt, &lrc_idx);
 	if (IS_ERR(active))
 		return PTR_ERR(active);
 
@@ -657,7 +657,7 @@ static int xe_eu_control_stopped(struct xe_eudebug *d,
 	if (XE_WARN_ON(!q) || XE_WARN_ON(!q->gt))
 		return -EINVAL;
 
-	active = runalone_active_queue_get(q->gt, &lrc_idx);
+	active = xe_gt_runalone_active_queue_get(q->gt, &lrc_idx);
 	if (IS_ERR(active))
 		return PTR_ERR(active);
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 57bd82a02ecb..386b5c78ecff 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -37,7 +37,7 @@ enum xe_eudebug_state {
 };
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
-#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE
+#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EU_ATTENTION
 
 /**
  * struct xe_eudebug_handle - eudebug resource handle
@@ -167,4 +167,3 @@ struct xe_eudebug {
 };
 
 #endif /* _XE_EUDEBUG_TYPES_H_ */
-
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 6d69e100c965..54394a7e12ab 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -52,6 +52,7 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND		4
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	6
+#define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	7
 
 	/** @flags: Flags */
 	__u16 flags;
@@ -329,6 +330,34 @@ struct drm_xe_eudebug_eu_control {
 	__u64 bitmask_ptr;
 };
 
+/**
+ * struct drm_xe_eudebug_event_eu_attention - EU Attention Event
+ *
+ * Whenever there is any thread in halted/attentions state, this
+ * event will be delivered. The event will be delivered periodically
+ * until there are no attentions detected.
+ *
+ */
+struct drm_xe_eudebug_event_eu_attention {
+	/** @base: base event */
+	struct drm_xe_eudebug_event base;
+
+	/** @exec_queue_handle: Exec queue handle for the attentions */
+	__u64 exec_queue_handle;
+
+	/** @lrc_handle: LRC handle for the attentions */
+	__u64 lrc_handle;
+
+	/** @flags: Flags */
+	__u32 flags;
+
+	/** @bitmask_size: Bitmask size in bytes for bitmask[] */
+	__u32 bitmask_size;
+
+	/** @bitmask: Attention bits, one per thread */
+	__u8 bitmask[];
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 16/22] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (14 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 15/22] drm/xe/eudebug: Introduce per device attention scan worker Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 17/22] drm/xe: Implement SR-IOV and eudebug exclusivity Mika Kuoppala
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

From: Christoph Manszewski <christoph.manszewski@intel.com>

Introduce kunit test for eudebug. For now it checks the dynamic
application of WAs.

v2: adapt to removal of call_for_each_device (Mika)
v3: s/FW_RENDER/FORCEWAKE_ALL (Mika)

Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/tests/xe_eudebug.c       | 183 ++++++++++++++++++++
 drivers/gpu/drm/xe/tests/xe_live_test_mod.c |   5 +
 drivers/gpu/drm/xe/xe_eudebug.c             |   4 +
 3 files changed, 192 insertions(+)
 create mode 100644 drivers/gpu/drm/xe/tests/xe_eudebug.c

diff --git a/drivers/gpu/drm/xe/tests/xe_eudebug.c b/drivers/gpu/drm/xe/tests/xe_eudebug.c
new file mode 100644
index 000000000000..f839fb292b9b
--- /dev/null
+++ b/drivers/gpu/drm/xe/tests/xe_eudebug.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0 AND MIT
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+#include <kunit/visibility.h>
+
+#include "regs/xe_gt_regs.h"
+#include "regs/xe_engine_regs.h"
+
+#include "xe_force_wake.h"
+#include "xe_gt_mcr.h"
+#include "xe_mmio.h"
+
+#include "tests/xe_kunit_helpers.h"
+#include "tests/xe_pci_test.h"
+#include "tests/xe_test.h"
+
+#undef XE_REG_MCR
+#define XE_REG_MCR(r_, ...)	((const struct xe_reg_mcr){					\
+				 .__reg = XE_REG_INITIALIZER(r_,  ##__VA_ARGS__, .mcr = 1)	\
+				 })
+
+static const char *reg_to_str(struct xe_reg reg)
+{
+	if (reg.raw == TD_CTL.__reg.raw)
+		return "TD_CTL";
+	else if (reg.raw == CS_DEBUG_MODE2(RENDER_RING_BASE).raw)
+		return "CS_DEBUG_MODE2";
+	else if (reg.raw == ROW_CHICKEN.__reg.raw)
+		return "ROW_CHICKEN";
+	else if (reg.raw == ROW_CHICKEN2.__reg.raw)
+		return "ROW_CHICKEN2";
+	else if (reg.raw == ROW_CHICKEN3.__reg.raw)
+		return "ROW_CHICKEN3";
+	else
+		return "UNKNOWN REG";
+}
+
+static u32 get_reg_mask(struct xe_device *xe, struct xe_reg reg)
+{
+	struct kunit *test = kunit_get_current_test();
+	u32 val = 0;
+
+	if (reg.raw == TD_CTL.__reg.raw) {
+		val = TD_CTL_BREAKPOINT_ENABLE |
+		      TD_CTL_FORCE_THREAD_BREAKPOINT_ENABLE |
+		      TD_CTL_FEH_AND_FEE_ENABLE;
+
+		if (GRAPHICS_VERx100(xe) >= 1250)
+			val |= TD_CTL_GLOBAL_DEBUG_ENABLE;
+
+	} else if (reg.raw == CS_DEBUG_MODE2(RENDER_RING_BASE).raw) {
+		val = GLOBAL_DEBUG_ENABLE;
+	} else if (reg.raw == ROW_CHICKEN.__reg.raw) {
+		val = STALL_DOP_GATING_DISABLE;
+	} else if (reg.raw == ROW_CHICKEN2.__reg.raw) {
+		val = XEHPC_DISABLE_BTB;
+	} else if (reg.raw == ROW_CHICKEN3.__reg.raw) {
+		val = XE2_EUPEND_CHK_FLUSH_DIS;
+	} else {
+		kunit_warn(test, "Invalid register selection: %u\n", reg.raw);
+	}
+
+	return val;
+}
+
+static u32 get_reg_expected(struct xe_device *xe, struct xe_reg reg, bool enable_eudebug)
+{
+	u32 reg_mask = get_reg_mask(xe, reg);
+	u32 reg_bits = 0;
+
+	if (enable_eudebug || reg.raw == ROW_CHICKEN3.__reg.raw)
+		reg_bits = reg_mask;
+	else
+		reg_bits = 0;
+
+	return reg_bits;
+}
+
+static void check_reg(struct xe_gt *gt, bool enable_eudebug, struct xe_reg reg)
+{
+	struct kunit *test = kunit_get_current_test();
+	struct xe_device *xe = gt_to_xe(gt);
+	u32 reg_bits_expected = get_reg_expected(xe, reg, enable_eudebug);
+	u32 reg_mask = get_reg_mask(xe, reg);
+	u32 reg_bits = 0;
+
+	if (reg.mcr)
+		reg_bits = xe_gt_mcr_unicast_read_any(gt, (struct xe_reg_mcr){.__reg = reg});
+	else
+		reg_bits = xe_mmio_read32(&gt->mmio, reg);
+
+	reg_bits &= reg_mask;
+
+	kunit_printk(KERN_DEBUG, test, "%s bits: expected == 0x%x; actual == 0x%x\n",
+		     reg_to_str(reg), reg_bits_expected, reg_bits);
+	KUNIT_EXPECT_EQ_MSG(test, reg_bits_expected, reg_bits,
+			    "Invalid bits set for %s\n", reg_to_str(reg));
+}
+
+static void __check_regs(struct xe_gt *gt, bool enable_eudebug)
+{
+	struct xe_device *xe = gt_to_xe(gt);
+
+	if (GRAPHICS_VERx100(xe) >= 1200)
+		check_reg(gt, enable_eudebug, TD_CTL.__reg);
+
+	if (GRAPHICS_VERx100(xe) >= 1250 && GRAPHICS_VERx100(xe) <= 1274)
+		check_reg(gt, enable_eudebug, ROW_CHICKEN.__reg);
+
+	if (xe->info.platform == XE_PVC)
+		check_reg(gt, enable_eudebug, ROW_CHICKEN2.__reg);
+
+	if (GRAPHICS_VERx100(xe) >= 2000 && GRAPHICS_VERx100(xe) <= 2004)
+		check_reg(gt, enable_eudebug, ROW_CHICKEN3.__reg);
+}
+
+static void check_regs(struct xe_device *xe, bool enable_eudebug)
+{
+	struct kunit *test = kunit_get_current_test();
+	struct xe_gt *gt;
+	unsigned int fw_ref;
+	u8 id;
+
+	kunit_printk(KERN_DEBUG, test, "Check regs for eudebug %s\n",
+		     enable_eudebug ? "enabled" : "disabled");
+
+	xe_pm_runtime_get(xe);
+	for_each_gt(gt, xe, id) {
+		if (xe_gt_is_media_type(gt))
+			continue;
+
+		/* XXX: Figure out per platform proper domain */
+		fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+		KUNIT_ASSERT_TRUE_MSG(test, fw_ref, "Forcewake failed.\n");
+
+		__check_regs(gt, enable_eudebug);
+
+		xe_force_wake_put(gt_to_fw(gt), fw_ref);
+	}
+	xe_pm_runtime_put(xe);
+}
+
+static int toggle_reg_value(struct xe_device *xe)
+{
+	struct kunit *test = kunit_get_current_test();
+	bool enable_eudebug = xe_eudebug_is_enabled(xe);
+
+	kunit_printk(KERN_DEBUG, test, "Test eudebug WAs for graphics version: %u\n",
+		     GRAPHICS_VERx100(xe));
+
+	check_regs(xe, enable_eudebug);
+
+	xe_eudebug_enable(xe, !enable_eudebug);
+	check_regs(xe, !enable_eudebug);
+
+	xe_eudebug_enable(xe, enable_eudebug);
+	check_regs(xe, enable_eudebug);
+
+	return 0;
+}
+
+static void xe_eudebug_toggle_reg_kunit(struct kunit *test)
+{
+	struct xe_device *xe = test->priv;
+
+	toggle_reg_value(xe);
+}
+
+static struct kunit_case xe_eudebug_tests[] = {
+	KUNIT_CASE_PARAM(xe_eudebug_toggle_reg_kunit,
+			 xe_pci_live_device_gen_param),
+	{}
+};
+
+VISIBLE_IF_KUNIT
+struct kunit_suite xe_eudebug_test_suite = {
+	.name = "xe_eudebug",
+	.test_cases = xe_eudebug_tests,
+	.init = xe_kunit_helper_xe_device_live_test_init,
+};
+EXPORT_SYMBOL_IF_KUNIT(xe_eudebug_test_suite);
diff --git a/drivers/gpu/drm/xe/tests/xe_live_test_mod.c b/drivers/gpu/drm/xe/tests/xe_live_test_mod.c
index c55e46f1ae92..dc83bb6a892d 100644
--- a/drivers/gpu/drm/xe/tests/xe_live_test_mod.c
+++ b/drivers/gpu/drm/xe/tests/xe_live_test_mod.c
@@ -19,6 +19,11 @@ kunit_test_suite(xe_migrate_test_suite);
 kunit_test_suite(xe_mocs_test_suite);
 kunit_test_suite(xe_guc_g2g_test_suite);
 
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+extern struct kunit_suite xe_eudebug_test_suite;
+kunit_test_suite(xe_eudebug_test_suite);
+#endif
+
 MODULE_AUTHOR("Intel Corporation");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("xe live kunit tests");
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index 0c67db86f009..bd1f186f496c 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -2205,3 +2205,7 @@ int xe_eudebug_connect_ioctl(struct drm_device *dev,
 
 	return xe_eudebug_connect(xe, file, param);
 }
+
+#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
+#include "tests/xe_eudebug.c"
+#endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 17/22] drm/xe: Implement SR-IOV and eudebug exclusivity
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (15 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 16/22] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 18/22] drm/xe: Add xe_client_debugfs and introduce debug_data file Mika Kuoppala
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

From: Christoph Manszewski <christoph.manszewski@intel.com>

EU debug functionality relies on access to specific mmio registers.
Since VFs don't have access to those registers and in order to avoid
interference with VFs, make SR-IOV and eudebug functionality exclusive.
I.e. don't allow to enable eudebug in VF mode and don't allow to enable
eudebug when any VFs are provisioned. Likewise, don't allow to provision
VFs when eudebug is enabled.

Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
Signed-off-by: Maciej Patelczyk <maciej.patelczyk@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/tests/xe_eudebug.c | 10 ++++++++++
 drivers/gpu/drm/xe/xe_eudebug.c       | 23 +++++++++++++++++++++--
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/xe/tests/xe_eudebug.c b/drivers/gpu/drm/xe/tests/xe_eudebug.c
index f839fb292b9b..601725c642bd 100644
--- a/drivers/gpu/drm/xe/tests/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/tests/xe_eudebug.c
@@ -8,9 +8,13 @@
 #include "regs/xe_gt_regs.h"
 #include "regs/xe_engine_regs.h"
 
+#include "xe_device.h"
+#include "xe_eudebug.h"
 #include "xe_force_wake.h"
+#include "xe_gt.h"
 #include "xe_gt_mcr.h"
 #include "xe_mmio.h"
+#include "xe_pm.h"
 
 #include "tests/xe_kunit_helpers.h"
 #include "tests/xe_pci_test.h"
@@ -147,6 +151,12 @@ static int toggle_reg_value(struct xe_device *xe)
 	struct kunit *test = kunit_get_current_test();
 	bool enable_eudebug = xe_eudebug_is_enabled(xe);
 
+	if (IS_SRIOV_VF(xe))
+		kunit_skip(test, "eudebug not available in SR-IOV VF mode\n");
+
+	if (xe->eudebug.state == XE_EUDEBUG_NOT_SUPPORTED)
+		kunit_skip(test, "eudebug not supported\n");
+
 	kunit_printk(KERN_DEBUG, test, "Test eudebug WAs for graphics version: %u\n",
 		     GRAPHICS_VERx100(xe));
 
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index bd1f186f496c..eae93c5f5e86 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -25,6 +25,7 @@
 #include "xe_gt_debug.h"
 #include "xe_macros.h"
 #include "xe_pm.h"
+#include "xe_sriov_pf.h"
 #include "xe_sync.h"
 #include "xe_vm.h"
 
@@ -2047,6 +2048,7 @@ bool xe_eudebug_is_enabled(struct xe_device *xe)
 int xe_eudebug_enable(struct xe_device *xe, bool enable)
 {
 	struct xe_gt *gt;
+	int ret;
 	int i;
 	u8 id;
 
@@ -2067,6 +2069,14 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
 		return 0;
 	}
 
+	if (enable && IS_SRIOV_PF(xe)) {
+		ret = xe_sriov_pf_lockdown(xe);
+		if (ret) {
+			mutex_unlock(&xe->eudebug.lock);
+			return ret;
+		}
+	}
+
 	xe_pm_runtime_get(xe);
 
 	for_each_gt(gt, xe, id) {
@@ -2087,11 +2097,15 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
 		XE_EUDEBUG_ENABLED : XE_EUDEBUG_DISABLED;
 	mutex_unlock(&xe->eudebug.lock);
 
-	if (enable)
+	if (enable) {
 		attention_poll_start(xe);
-	else
+	} else {
 		attention_poll_stop(xe);
 
+		if (IS_SRIOV_PF(xe))
+			xe_sriov_pf_end_lockdown(xe);
+	}
+
 	return 0;
 }
 
@@ -2153,6 +2167,11 @@ void xe_eudebug_init(struct xe_device *xe)
 
 	xe->eudebug.state = XE_EUDEBUG_NOT_SUPPORTED;
 
+	if (IS_SRIOV_VF(xe)) {
+		drm_info(&xe->drm, "eudebug not available in SR-IOV VF mode\n");
+		return;
+	}
+
 	err = drmm_mutex_init(dev, &xe->eudebug.lock);
 	if (err)
 		goto out_err;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 18/22] drm/xe: Add xe_client_debugfs and introduce debug_data file
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (16 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 17/22] drm/xe: Implement SR-IOV and eudebug exclusivity Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 19/22] drm/xe/eudebug: Add read/count/compare helper for eu attention Mika Kuoppala
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Sunil Khatri, Mika Kuoppala

From: Christoph Manszewski <christoph.manszewski@intel.com>

Create a debug_data file for each xe file/client which is supposed to
list all mapped debug data and attempts to mimic '/proc/pid/maps'.
Each line represents a single mapping and has the following format:

  <vm id> <begin>-<end> <flags> <offset> <pathname>

Cc: Sunil Khatri <sunil.khatri@amd.com>
Signed-off-by: Christoph Manszewski <christoph.manszewski@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/Makefile            |   3 +-
 drivers/gpu/drm/xe/xe_client_debugfs.c | 118 +++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_client_debugfs.h |  19 ++++
 drivers/gpu/drm/xe/xe_device.c         |   3 +
 4 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/xe/xe_client_debugfs.c
 create mode 100644 drivers/gpu/drm/xe/xe_client_debugfs.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index f3e113f801d0..34db797ef8fc 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -343,7 +343,8 @@ ifeq ($(CONFIG_DRM_FBDEV_EMULATION),y)
 endif
 
 ifeq ($(CONFIG_DEBUG_FS),y)
-	xe-y += xe_debugfs.o \
+	xe-y += xe_client_debugfs.o \
+		xe_debugfs.o \
 		xe_gt_debugfs.o \
 		xe_gt_sriov_vf_debugfs.o \
 		xe_gt_stats.o \
diff --git a/drivers/gpu/drm/xe/xe_client_debugfs.c b/drivers/gpu/drm/xe/xe_client_debugfs.c
new file mode 100644
index 000000000000..0b952038e698
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_client_debugfs.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_client_debugfs.h"
+
+#include <linux/debugfs.h>
+
+#include "xe_debug_data.h"
+#include "xe_debug_data_types.h"
+#include "xe_device_types.h"
+#include "xe_vm_types.h"
+
+#define MAX_LINE_LEN (64 + PATH_MAX)
+
+static ssize_t debug_data_read(struct file *file, char __user *buf, size_t count,
+			       loff_t *ppos)
+{
+	struct xe_debug_data *dd;
+	unsigned long vm_index;
+	const char *path;
+	char *kbuf;
+	struct xe_vm *vm;
+
+	struct xe_file *xef = file->private_data;
+	ssize_t total = 0;
+	loff_t pos = 0;
+
+	if (!xef || !buf)
+		return -EINVAL;
+
+	kbuf = kmalloc(MAX_LINE_LEN, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	mutex_lock(&xef->vm.lock);
+
+	xa_for_each(&xef->vm.xa, vm_index, vm) {
+		mutex_lock(&vm->debug_data.lock);
+		list_for_each_entry(dd, &vm->debug_data.list, link) {
+			int len;
+
+			path = dd->flags & DRM_XE_VM_BIND_DEBUG_DATA_FLAG_PSEUDO ?
+				xe_debug_data_pseudo_path_to_string(dd->pseudopath) :
+				dd->pathname;
+
+			/* Format: <vm id> <begin>-<end> <flags> <offset> <pathname> */
+			len = snprintf(kbuf, MAX_LINE_LEN, "%lu 0x%llx-0x%llx 0x%llx 0x%x\t%s\n",
+				vm_index,
+				dd->addr,
+				dd->addr + dd->range,
+				dd->flags,
+				dd->offset,
+				path);
+
+			if (pos + len <= *ppos) {
+				pos += len;
+				continue;
+			}
+
+			if (pos < *ppos) {
+				const int skip = *ppos - pos;
+
+				len -= skip;
+				memmove(kbuf, kbuf + skip, len);
+				pos = *ppos;
+			}
+
+			if (total + len > count)
+				len = count - total;
+
+			if (copy_to_user(buf + total, kbuf, len)) {
+				mutex_unlock(&vm->debug_data.lock);
+				mutex_unlock(&xef->vm.lock);
+				kfree(kbuf);
+				return -EFAULT;
+			}
+
+			total += len;
+			pos += len;
+
+			if (total >= count) {
+				mutex_unlock(&vm->debug_data.lock);
+				mutex_unlock(&xef->vm.lock);
+				kfree(kbuf);
+				*ppos = pos;
+				return total;
+			}
+		}
+		mutex_unlock(&vm->debug_data.lock);
+	}
+
+	mutex_unlock(&xef->vm.lock);
+	kfree(kbuf);
+	*ppos = pos;
+	return total;
+}
+
+static int debug_data_open(struct inode *inode, struct file *file)
+{
+	struct xe_file *xef = inode->i_private;
+
+	file->private_data = xef;
+	return 0;
+}
+
+static const struct file_operations maps_fops = {
+	.owner = THIS_MODULE,
+	.open = debug_data_open,
+	.read = debug_data_read,
+	.llseek = default_llseek,
+};
+
+void xe_client_debugfs_register(struct xe_file *xef)
+{
+	debugfs_create_file("debug_data", 0444, xef->drm->debugfs_client, xef, &maps_fops);
+}
diff --git a/drivers/gpu/drm/xe/xe_client_debugfs.h b/drivers/gpu/drm/xe/xe_client_debugfs.h
new file mode 100644
index 000000000000..9eace15c0a49
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_client_debugfs.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_CLIENT_DEBUGFS_H_
+#define _XE_CLIENT_DEBUGFS_H_
+
+#include <linux/debugfs.h>
+
+struct xe_file;
+
+#ifdef CONFIG_DEBUG_FS
+void xe_client_debugfs_register(struct xe_file *xef);
+#else
+static inline void xe_client_debugfs_register(struct xe_file *xef) { }
+#endif
+
+#endif // _XE_CLIENT_DEBUGFS_H_
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 39a70aaaa253..b4480e07e0f4 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -25,6 +25,7 @@
 #include "regs/xe_regs.h"
 #include "xe_bo.h"
 #include "xe_bo_evict.h"
+#include "xe_client_debugfs.h"
 #include "xe_debugfs.h"
 #include "xe_defaults.h"
 #include "xe_devcoredump.h"
@@ -124,6 +125,8 @@ static int xe_file_open(struct drm_device *dev, struct drm_file *file)
 		put_task_struct(task);
 	}
 
+	xe_client_debugfs_register(xef);
+
 	return 0;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 19/22] drm/xe/eudebug: Add read/count/compare helper for eu attention
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (17 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 18/22] drm/xe: Add xe_client_debugfs and introduce debug_data file Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 20/22] drm/xe/vm: Support for adding null page VMA to VM on request Mika Kuoppala
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>

Add xe_eu_attentions structure to capture and store eu attention bits.
Add a function to count the number of eu threads that have turned on from
eu attentions, and add a function to count the number of eu threads that
have changed on a state between eu attentions.

v2: fix array size calculation (Christoph)

Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/xe_gt_debug.c       | 65 ++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_gt_debug.h       |  7 +++
 drivers/gpu/drm/xe/xe_gt_debug_types.h | 23 +++++++++
 3 files changed, 95 insertions(+)
 create mode 100644 drivers/gpu/drm/xe/xe_gt_debug_types.h

diff --git a/drivers/gpu/drm/xe/xe_gt_debug.c b/drivers/gpu/drm/xe/xe_gt_debug.c
index 314eef6734c3..bf2ca95c7389 100644
--- a/drivers/gpu/drm/xe/xe_gt_debug.c
+++ b/drivers/gpu/drm/xe/xe_gt_debug.c
@@ -3,12 +3,14 @@
  * Copyright © 2023 Intel Corporation
  */
 
+#include <linux/delay.h>
 #include "regs/xe_gt_regs.h"
 #include "xe_device.h"
 #include "xe_force_wake.h"
 #include "xe_gt.h"
 #include "xe_gt_topology.h"
 #include "xe_gt_debug.h"
+#include "xe_gt_debug_types.h"
 #include "xe_gt_mcr.h"
 #include "xe_pm.h"
 #include "xe_macros.h"
@@ -177,3 +179,66 @@ int xe_gt_eu_threads_needing_attention(struct xe_gt *gt)
 
 	return err < 0 ? 0 : err;
 }
+
+static inline unsigned int
+xe_eu_attentions_count(const struct xe_eu_attentions *a)
+{
+	return bitmap_weight((void *)a->att, a->size * BITS_PER_BYTE);
+}
+
+void xe_gt_eu_attentions_read(struct xe_gt *gt,
+			      struct xe_eu_attentions *a,
+			      const unsigned int settle_time_ms)
+{
+	unsigned int prev = 0;
+	ktime_t end, now;
+
+	now = ktime_get_raw();
+	end = ktime_add_ms(now, settle_time_ms);
+
+	a->ts = 0;
+	a->size = min_t(int,
+			xe_gt_eu_attention_bitmap_size(gt),
+			sizeof(a->att));
+
+	do {
+		unsigned int attn;
+
+		xe_gt_eu_attention_bitmap(gt, a->att, a->size);
+		attn = xe_eu_attentions_count(a);
+
+		now = ktime_get_raw();
+
+		if (a->ts == 0)
+			a->ts = now;
+		else if (attn && attn != prev)
+			a->ts = now;
+
+		prev = attn;
+
+		if (settle_time_ms)
+			udelay(5);
+
+		/*
+		 * XXX We are gathering data for production SIP to find
+		 * the upper limit of settle time. For now, we wait full
+		 * timeout value regardless.
+		 */
+	} while (ktime_before(now, end));
+}
+
+unsigned int xe_eu_attentions_xor_count(const struct xe_eu_attentions *a,
+					const struct xe_eu_attentions *b)
+{
+	unsigned int count = 0;
+	unsigned int i;
+
+	if (XE_WARN_ON(a->size != b->size))
+		return -EINVAL;
+
+	for (i = 0; i < a->size; i++)
+		if (a->att[i] ^ b->att[i])
+			count++;
+
+	return count;
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_debug.h b/drivers/gpu/drm/xe/xe_gt_debug.h
index 9dabe9cc1d25..0d03565195b4 100644
--- a/drivers/gpu/drm/xe/xe_gt_debug.h
+++ b/drivers/gpu/drm/xe/xe_gt_debug.h
@@ -9,6 +9,7 @@
 #include <linux/bits.h>
 #include <linux/math.h>
 
+struct xe_eu_attentions;
 struct xe_gt;
 
 #define XE_GT_ATTENTION_TIMEOUT_MS 100
@@ -29,4 +30,10 @@ int xe_gt_eu_attention_bitmap_size(struct xe_gt *gt);
 int xe_gt_eu_attention_bitmap(struct xe_gt *gt, u8 *bits,
 			      unsigned int bitmap_size);
 
+void xe_gt_eu_attentions_read(struct xe_gt *gt,
+			      struct xe_eu_attentions *a,
+			      const unsigned int settle_time_ms);
+
+unsigned int xe_eu_attentions_xor_count(const struct xe_eu_attentions *a,
+					const struct xe_eu_attentions *b);
 #endif
diff --git a/drivers/gpu/drm/xe/xe_gt_debug_types.h b/drivers/gpu/drm/xe/xe_gt_debug_types.h
new file mode 100644
index 000000000000..35a0e822f20a
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_gt_debug_types.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __XE_GT_DEBUG_TYPES_
+#define __XE_GT_DEBUG_TYPES_
+
+#include <linux/types.h>
+
+#define XE_GT_EU_ATT_ROWS 2u
+#define XE_GT_EU_ATT_MAX_THREADS 16
+#define XE_GT_EU_MAX_NUM  1024
+
+struct xe_eu_attentions {
+	u8 att[XE_GT_EU_MAX_NUM *
+	       XE_GT_EU_ATT_ROWS *
+	       XE_GT_EU_ATT_MAX_THREADS/8];
+	unsigned int size;
+	ktime_t ts;
+};
+
+#endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 20/22] drm/xe/vm: Support for adding null page VMA to VM on request
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (18 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 19/22] drm/xe/eudebug: Add read/count/compare helper for eu attention Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 14:03 ` [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface Mika Kuoppala
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Oak Zeng, Niranjana Vishwanathapura, Stuart Summers, Bruce Chang,
	Mika Kuoppala

From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>

The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
access will halt the corresponding EUs. So, in order to activate the
debugger, kmd needs to install the temporal page to unhalt the EUs.
Plan to be used for pagefault handling when the EU debugger is running.
The idea is to install a null page vma if the pagefault is from an invalid
access. After installing null page pte, the user debugger can continue to
run/inspect without causing a fatal failure or reset and stop.
Based on Bruce's implementation [1].

[1] https://lore.kernel.org/intel-xe/20230829231648.4438-1-yu.bruce.chang@intel.com/

v2: s/NULL_VMA/DRM_GPUVA_SPARSE (Mika)
v3: use ERR_CAST as we dont return null (Mika)

Cc: Oak Zeng <oak.zeng@intel.com>
Cc: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
Cc: Stuart Summers <stuart.summers@intel.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Co-developed-by: Bruce Chang <yu.bruce.chang@intel.com>
Signed-off-by: Bruce Chang <yu.bruce.chang@intel.com>
Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/xe_vm.c | 33 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_vm.h |  3 +++
 2 files changed, 36 insertions(+)

diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index e09ebd2e357c..9ee0631cb6cb 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -4796,3 +4796,36 @@ void xe_vm_remove_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q)
 	}
 	up_write(&vm->exec_queues.lock);
 }
+
+struct xe_vma *xe_vm_create_null_vma(struct xe_vm *vm, u64 addr)
+{
+	struct xe_vma_mem_attr default_attr = {
+		.preferred_loc = {
+			.devmem_fd = DRM_XE_PREFERRED_LOC_DEFAULT_DEVICE,
+			.migration_policy = DRM_XE_MIGRATE_ALL_PAGES,
+		},
+		.atomic_access = DRM_XE_ATOMIC_UNDEFINED,
+		.default_pat_index = vm->xe->pat.idx[XE_CACHE_NONE],
+		.pat_index = vm->xe->pat.idx[XE_CACHE_NONE],
+	};
+	struct xe_vma *vma;
+	u32 page_size;
+	int err;
+
+	if (xe_vm_is_closed_or_banned(vm))
+		return ERR_PTR(-ENOENT);
+
+	page_size = vm->flags & XE_VM_FLAG_64K ? SZ_64K : SZ_4K;
+	vma = xe_vma_create(vm, NULL, 0, addr, addr + page_size - 1,
+			    &default_attr, DRM_GPUVA_SPARSE);
+	if (IS_ERR(vma))
+		return ERR_CAST(vma);
+
+	err = xe_vm_insert_vma(vm, vma);
+	if (err) {
+		xe_vma_destroy_late(vma);
+		return ERR_PTR(err);
+	}
+
+	return vma;
+}
diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h
index 288115c7844a..5d932b29d047 100644
--- a/drivers/gpu/drm/xe/xe_vm.h
+++ b/drivers/gpu/drm/xe/xe_vm.h
@@ -418,4 +418,7 @@ static inline struct drm_exec *xe_vm_validation_exec(struct xe_vm *vm)
 	((READ_ONCE(tile_present) & ~READ_ONCE(tile_invalidated)) & BIT((tile)->id))
 
 void xe_vma_mem_attr_copy(struct xe_vma_mem_attr *to, struct xe_vma_mem_attr *from);
+
+struct xe_vma *xe_vm_create_null_vma(struct xe_vm *vm, u64 addr);
+
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (19 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 20/22] drm/xe/vm: Support for adding null page VMA to VM on request Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 19:08   ` Matthew Brost
  2026-02-23 14:03 ` [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling Mika Kuoppala
                   ` (5 subsequent siblings)
  26 siblings, 1 reply; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Jan Maślak, Mika Kuoppala

From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>

The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
access will halt the corresponding EUs. To solve this problem, introduce
EU pagefault handling functionality, which allows to unhalt pagefaulted
eu threads and to EU debugger to get inform about the eu attentions state
of EU threads during execution.

If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
after handling the pagefault. The pagefault eudebug event follows
the newly added drm_xe_eudebug_event_pagefault type.
When a pagefault occurs, it prevents to send the
DRM_XE_EUDEBUG_EVENT_EU_ATTENTION event to the client during pagefault
handling.

The page fault event delivery follows the below policy.
(1) If EU Debugger discovery has completed and pagefaulted eu threads turn
    on attention bit then pagefault handler delivers pagefault event
    directly.
(2) If a pagefault occurs during eu debugger discovery process, pagefault
    handler queues a pagefault event and sends the queued event when
    discovery has completed and pagefaulted eu threads turn on attention
    bit.
(3) If the pagefaulted eu thread struggles to turn on the attention bit
    within the specified time, the attention scan worker sends a pagefault
    event when it detects that the attention bit is turned on.

If multiple eu threads are running and a pagefault occurs due to accessing
the same invalid address, send a single pagefault event
(DRM_XE_EUDEBUG_EVENT_PAGEFAULT type) to the user debugger instead of a
pagefault event for each of the multiple eu threads.
If eu threads (other than the one that caused the page fault before) access
the new invalid addresses, send a new pagefault event.

As the attention scan worker send the eu attention event whenever the
attention bit is turned on, user debugger receives attenion event
immediately after pagefault event.
In this case, the page-fault event always precedes the attention event.

When the user debugger receives an attention event after a pagefault event,
it can detect whether additional breakpoints or interrupts occur in
addition to the existing pagefault by comparing the eu threads where the
pagefault occurred with the eu threads where the attention bit is newly
enabled.

v2: use only force exception (Joonas, Mika)
v3: rebased on v4 (Mika)
v4: streamline uapi, cleanups (Mika)
v5: struct member documentation (Mika)
v6: fault to fault_type (Mika)

Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Signed-off-by: Jan Maślak <jan.maslak@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/Makefile               |   2 +-
 drivers/gpu/drm/xe/xe_eudebug.c           | 100 ++++-
 drivers/gpu/drm/xe/xe_eudebug.h           |   9 +
 drivers/gpu/drm/xe/xe_eudebug_hw.c        |  15 +-
 drivers/gpu/drm/xe/xe_eudebug_pagefault.c | 440 ++++++++++++++++++++++
 drivers/gpu/drm/xe/xe_eudebug_pagefault.h |  47 +++
 drivers/gpu/drm/xe/xe_eudebug_types.h     |  69 +++-
 drivers/gpu/drm/xe/xe_pagefault_types.h   |   4 +
 include/uapi/drm/xe_drm_eudebug.h         |  12 +
 9 files changed, 676 insertions(+), 22 deletions(-)
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.c
 create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index 34db797ef8fc..b49fe7ae18e7 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -152,7 +152,7 @@ xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
 xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
 
 # debugging shaders with gdb (eudebug) support
-xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_gt_debug.o
+xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_eudebug_pagefault.o xe_gt_debug.o
 
 # graphics hardware monitoring (HWMON) support
 xe-$(CONFIG_HWMON) += xe_hwmon.o
diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
index eae93c5f5e86..4b2f0dd9d234 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.c
+++ b/drivers/gpu/drm/xe/xe_eudebug.c
@@ -17,12 +17,16 @@
 #include "xe_eudebug.h"
 #include "xe_eudebug_hw.h"
 #include "xe_eudebug_types.h"
+#include "xe_eudebug_pagefault.h"
 #include "xe_eudebug_vm.h"
 #include "xe_exec_queue.h"
+#include "xe_force_wake.h"
 #include "xe_gt.h"
 #include "xe_hw_engine.h"
 #include "xe_gt.h"
 #include "xe_gt_debug.h"
+#include "xe_gt_mcr.h"
+#include "regs/xe_gt_regs.h"
 #include "xe_macros.h"
 #include "xe_pm.h"
 #include "xe_sriov_pf.h"
@@ -263,6 +267,7 @@ static void xe_eudebug_free(struct kref *ref)
 	while (kfifo_get(&d->events.fifo, &event))
 		kfree(event);
 
+	xe_eudebug_pagefault_fini(d);
 	xe_eudebug_resources_destroy(d);
 	mutex_destroy(&d->target.lock);
 	XE_WARN_ON(d->target.xef);
@@ -461,7 +466,7 @@ static int _xe_eudebug_disconnect(struct xe_eudebug *d,
 	} \
 })
 
-static struct xe_eudebug *
+struct xe_eudebug *
 xe_eudebug_get_nolock(struct xe_file *xef)
 {
 	struct xe_eudebug *d;
@@ -1888,10 +1893,6 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
 {
 	int ret;
 
-	ret = xe_gt_eu_threads_needing_attention(gt);
-	if (ret <= 0)
-		return ret;
-
 	ret = xe_send_gt_attention(gt);
 
 	/* Discovery in progress, fake it */
@@ -1901,6 +1902,65 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
 	return ret;
 }
 
+int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
+				    struct xe_eudebug_pagefault *pf)
+{
+	struct drm_xe_eudebug_event_pagefault *ep;
+	struct drm_xe_eudebug_event *event;
+	int h_queue, h_lrc;
+	u32 size = xe_gt_eu_attention_bitmap_size(pf->q->gt) * 3;
+	u32 sz = struct_size(ep, bitmask, size);
+	int ret;
+
+	XE_WARN_ON(pf->lrc_idx < 0 || pf->lrc_idx >= pf->q->width);
+
+	XE_WARN_ON(!xe_exec_queue_is_debuggable(pf->q));
+
+	h_queue = find_handle(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, pf->q);
+	if (h_queue < 0)
+		return h_queue;
+
+	h_lrc = find_handle(d, XE_EUDEBUG_RES_TYPE_LRC, pf->q->lrc[pf->lrc_idx]);
+	if (h_lrc < 0)
+		return h_lrc;
+
+	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_PAGEFAULT, 0,
+					DRM_XE_EUDEBUG_EVENT_STATE_CHANGE, sz);
+
+	if (!event)
+		return -ENOSPC;
+
+	ep = cast_event(ep, event);
+	ep->exec_queue_handle = h_queue;
+	ep->lrc_handle = h_lrc;
+	ep->bitmask_size = size;
+	ep->pagefault_address = pf->fault.addr;
+
+	memcpy(ep->bitmask, pf->attentions.before.att, pf->attentions.before.size);
+	memcpy(ep->bitmask + pf->attentions.before.size,
+	       pf->attentions.after.att, pf->attentions.after.size);
+	memcpy(ep->bitmask + pf->attentions.before.size + pf->attentions.after.size,
+	       pf->attentions.resolved.att, pf->attentions.resolved.size);
+
+	event->seqno = atomic_long_inc_return(&d->events.seqno);
+
+	ret = xe_eudebug_queue_event(d, event);
+	if (ret)
+		xe_eudebug_disconnect(d, ret);
+
+	return ret;
+}
+
+static void handle_attention_fail(struct xe_gt *gt, int gt_id, int ret)
+{
+	/* TODO: error capture */
+	drm_info(&gt_to_xe(gt)->drm,
+		 "gt:%d unable to handle eu attention ret = %d\n",
+		 gt_id, ret);
+
+	xe_gt_reset_async(gt);
+}
+
 static void attention_poll_work(struct work_struct *work)
 {
 	struct xe_device *xe = container_of(work, typeof(*xe),
@@ -1923,15 +1983,15 @@ static void attention_poll_work(struct work_struct *work)
 			if (gt->info.type != XE_GT_TYPE_MAIN)
 				continue;
 
-			ret = xe_eudebug_handle_gt_attention(gt);
-			if (ret) {
-				/* TODO: error capture */
-				drm_info(&gt_to_xe(gt)->drm,
-					 "gt:%d unable to handle eu attention ret=%d\n",
-					 gt_id, ret);
+			if (!xe_gt_eu_threads_needing_attention(gt))
+				continue;
+
+			ret = xe_eudebug_handle_pagefaults(gt);
+			if (!ret)
+				ret = xe_eudebug_handle_gt_attention(gt);
 
-				xe_gt_reset_async(gt);
-			}
+			if (ret)
+				handle_attention_fail(gt, gt_id, ret);
 		}
 
 		xe_pm_runtime_put(xe);
@@ -1940,12 +2000,12 @@ static void attention_poll_work(struct work_struct *work)
 	schedule_delayed_work(&xe->eudebug.attention_dwork, delay);
 }
 
-static void attention_poll_stop(struct xe_device *xe)
+void xe_eudebug_attention_poll_stop(struct xe_device *xe)
 {
 	cancel_delayed_work_sync(&xe->eudebug.attention_dwork);
 }
 
-static void attention_poll_start(struct xe_device *xe)
+void xe_eudebug_attention_poll_start(struct xe_device *xe)
 {
 	mod_delayed_work(system_wq, &xe->eudebug.attention_dwork, 0);
 }
@@ -1988,6 +2048,8 @@ xe_eudebug_connect(struct xe_device *xe,
 
 	kref_init(&d->ref);
 	mutex_init(&d->target.lock);
+	mutex_init(&d->pf_lock);
+	INIT_LIST_HEAD(&d->pagefaults);
 	init_waitqueue_head(&d->events.write_done);
 	init_waitqueue_head(&d->events.read_done);
 	init_completion(&d->discovery);
@@ -2019,7 +2081,7 @@ xe_eudebug_connect(struct xe_device *xe,
 
 	kref_get(&d->ref);
 	queue_work(xe->eudebug.wq, &d->discovery_work);
-	attention_poll_start(xe);
+	xe_eudebug_attention_poll_start(xe);
 
 	eu_dbg(d, "connected session %lld", d->session);
 
@@ -2098,9 +2160,9 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
 	mutex_unlock(&xe->eudebug.lock);
 
 	if (enable) {
-		attention_poll_start(xe);
+		xe_eudebug_attention_poll_start(xe);
 	} else {
-		attention_poll_stop(xe);
+		xe_eudebug_attention_poll_stop(xe);
 
 		if (IS_SRIOV_PF(xe))
 			xe_sriov_pf_end_lockdown(xe);
@@ -2153,7 +2215,7 @@ static void xe_eudebug_fini(struct drm_device *dev, void *__unused)
 
 	xe_assert(xe, list_empty(&xe->eudebug.targets));
 
-	attention_poll_stop(xe);
+	xe_eudebug_attention_poll_stop(xe);
 }
 
 void xe_eudebug_init(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
index bd9fd7bf454f..34938e87be13 100644
--- a/drivers/gpu/drm/xe/xe_eudebug.h
+++ b/drivers/gpu/drm/xe/xe_eudebug.h
@@ -13,12 +13,14 @@ struct drm_file;
 struct xe_debug_data;
 struct xe_device;
 struct xe_file;
+struct xe_gt;
 struct xe_vm;
 struct xe_vma;
 struct xe_vma_ops;
 struct xe_exec_queue;
 struct xe_user_fence;
 struct xe_eudebug;
+struct xe_eudebug_pagefault;
 
 #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
 
@@ -72,8 +74,15 @@ void xe_eudebug_ufence_init(struct xe_user_fence *ufence);
 void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
 bool xe_eudebug_ufence_track(struct xe_user_fence *ufence);
 
+struct xe_eudebug *xe_eudebug_get_nolock(struct xe_file *xef);
 void xe_eudebug_put(struct xe_eudebug *d);
 
+int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
+				    struct xe_eudebug_pagefault *pf);
+
+void xe_eudebug_attention_poll_stop(struct xe_device *xe);
+void xe_eudebug_attention_poll_start(struct xe_device *xe);
+
 #else
 
 static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
index 5365265a67b3..270f7abc82e9 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_hw.c
+++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
@@ -322,6 +322,7 @@ static int do_eu_control(struct xe_eudebug *d,
 	struct xe_device *xe = d->xe;
 	u8 *bits = NULL;
 	unsigned int hw_attn_size, attn_size;
+	struct dma_fence *pf_fence;
 	struct xe_exec_queue *q;
 	struct xe_lrc *lrc;
 	u64 seqno;
@@ -376,8 +377,20 @@ static int do_eu_control(struct xe_eudebug *d,
 		goto out_free;
 	}
 
-	ret = -EINVAL;
 	mutex_lock(&d->hw.lock);
+	do {
+		pf_fence = dma_fence_get(d->pf_fence);
+		if (pf_fence) {
+			mutex_unlock(&d->hw.lock);
+			ret = dma_fence_wait(pf_fence, true);
+			dma_fence_put(pf_fence);
+			if (ret)
+				goto out_free;
+			mutex_lock(&d->hw.lock);
+		}
+	} while (pf_fence);
+
+	ret = -EINVAL;
 
 	switch (arg->cmd) {
 	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL:
diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.c b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
new file mode 100644
index 000000000000..edd368a7f6ae
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
@@ -0,0 +1,440 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#include "xe_eudebug_pagefault.h"
+
+#include <linux/delay.h>
+
+#include "xe_exec_queue.h"
+#include "xe_eudebug.h"
+#include "xe_eudebug_hw.h"
+#include "xe_force_wake.h"
+#include "xe_gt_debug.h"
+#include "xe_gt_mcr.h"
+#include "regs/xe_gt_regs.h"
+#include "xe_vm.h"
+
+static struct xe_gt *
+pf_to_gt(struct xe_eudebug_pagefault *pf)
+{
+	return pf->q->gt;
+}
+
+static void destroy_pagefault(struct xe_eudebug_pagefault *pf)
+{
+	xe_exec_queue_put(pf->q);
+	kfree(pf);
+}
+
+static int queue_pagefault(struct xe_eudebug_pagefault *pf)
+{
+	struct xe_eudebug *d;
+
+	d = xe_eudebug_get_nolock(pf->q->vm->xef);
+	if (!d)
+		return -EINVAL;
+
+	mutex_lock(&d->pf_lock);
+	list_add_tail(&pf->link, &d->pagefaults);
+	mutex_unlock(&d->pf_lock);
+
+	xe_eudebug_put(d);
+
+	return 0;
+}
+
+static int send_pagefault(struct xe_eudebug_pagefault *pf,
+			  bool from_attention_scan)
+{
+	struct xe_gt *gt = pf_to_gt(pf);
+	struct xe_eudebug *d;
+	struct xe_exec_queue *q;
+	int ret, lrc_idx;
+
+	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
+	if (IS_ERR(q))
+		return PTR_ERR(q);
+
+	if (!xe_exec_queue_is_debuggable(q)) {
+		ret = -EPERM;
+		goto out_exec_queue_put;
+	}
+
+	d = xe_eudebug_get_nolock(q->vm->xef);
+	if (!d) {
+		ret = -ENOTCONN;
+		goto out_exec_queue_put;
+	}
+
+	if (pf->deferred_resolved) {
+		xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
+					 XE_GT_ATTENTION_TIMEOUT_MS);
+
+		if (!xe_eu_attentions_xor_count(&pf->attentions.after,
+						&pf->attentions.resolved) &&
+		    !from_attention_scan) {
+			eu_dbg(d, "xe attentions not yet updated\n");
+			ret = -EBUSY;
+			goto out_eudebug_put;
+		}
+	}
+
+	ret = xe_eudebug_send_pagefault_event(d, pf);
+
+out_eudebug_put:
+	xe_eudebug_put(d);
+out_exec_queue_put:
+	xe_exec_queue_put(q);
+
+	return ret;
+}
+
+static const char *
+pagefault_get_driver_name(struct dma_fence *dma_fence)
+{
+	return "xe";
+}
+
+static const char *
+pagefault_fence_get_timeline_name(struct dma_fence *dma_fence)
+{
+	return "eudebug_pagefault_fence";
+}
+
+static const struct dma_fence_ops pagefault_fence_ops = {
+	.get_driver_name = pagefault_get_driver_name,
+	.get_timeline_name = pagefault_fence_get_timeline_name,
+};
+
+struct pagefault_fence {
+	struct dma_fence base;
+	spinlock_t lock;
+};
+
+static struct pagefault_fence *pagefault_fence_create(void)
+{
+	struct pagefault_fence *fence;
+
+	fence = kzalloc_obj(*fence, GFP_KERNEL);
+	if (fence == NULL)
+		return NULL;
+
+	spin_lock_init(&fence->lock);
+	dma_fence_init(&fence->base, &pagefault_fence_ops, &fence->lock,
+		       dma_fence_context_alloc(1), 1);
+
+	return fence;
+}
+
+void
+xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
+{
+	struct pagefault_fence *pf_fence;
+	struct xe_eudebug_pagefault *epf;
+	struct xe_vma *vma;
+	struct xe_gt *gt = pf->gt;
+	struct xe_exec_queue *q;
+	struct dma_fence *fence;
+	struct xe_eudebug *d;
+	unsigned int fw_ref;
+	int lrc_idx;
+	u32 td_ctl;
+
+	pf->consumer.epf = NULL;
+
+	down_read(&vm->lock);
+	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
+	up_read(&vm->lock);
+
+	if (vma)
+		return;
+
+	d = xe_eudebug_get_nolock(vm->xef);
+	if (!d)
+		return;
+
+	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
+	if (IS_ERR(q))
+		goto err_put_eudebug;
+
+	if (XE_WARN_ON(q->vm != vm))
+		goto err_put_exec_queue;
+
+	if (!xe_exec_queue_is_debuggable(q))
+		goto err_put_exec_queue;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(gt), q->hwe->domain);
+	if (!fw_ref)
+		goto err_put_exec_queue;
+
+	/*
+	 * If there is no debug functionality (TD_CTL_GLOBAL_DEBUG_ENABLE, etc.),
+	 * don't proceed pagefault routine for eu debugger.
+	 */
+	td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
+	if (!td_ctl)
+		goto err_put_fw;
+
+	epf = kzalloc_obj(*epf, GFP_KERNEL);
+	if (!epf)
+		goto err_put_fw;
+
+	xe_eudebug_attention_poll_stop(gt_to_xe(gt));
+
+	mutex_lock(&d->hw.lock);
+	fence = dma_fence_get(d->pf_fence);
+
+	if (fence) {
+		/*
+		 * TODO: If the new incoming pagefaulted address is different
+		 * from the pagefaulted address it is currently handling on the
+		 * same ASID, it needs a routine to wait here and then do the
+		 * following pagefault.
+		 */
+		dma_fence_put(fence);
+		goto err_unlock_hw_lock;
+	}
+
+	pf_fence = pagefault_fence_create();
+	if (!pf_fence)
+		goto err_unlock_hw_lock;
+
+	d->pf_fence = &pf_fence->base;
+
+	INIT_LIST_HEAD(&epf->link);
+
+	xe_gt_eu_attentions_read(gt, &epf->attentions.before, 0);
+
+	if (td_ctl & TD_CTL_FORCE_EXCEPTION)
+		eu_warn(d, "force exception already set!");
+
+	/* Halt regardless of thread dependencies */
+	while (!(td_ctl & TD_CTL_FORCE_EXCEPTION)) {
+		xe_gt_mcr_multicast_write(gt, TD_CTL,
+					  td_ctl | TD_CTL_FORCE_EXCEPTION);
+		udelay(200);
+		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
+	}
+
+	xe_gt_eu_attentions_read(gt, &epf->attentions.after,
+				 XE_GT_ATTENTION_TIMEOUT_MS);
+
+	mutex_unlock(&d->hw.lock);
+
+	/*
+	 * xe_exec_queue_put() will be called from xe_eudebug_pagefault_destroy()
+	 * or handle_pagefault()
+	 */
+	epf->q = q;
+	epf->lrc_idx = lrc_idx;
+	epf->fault.addr = pf->consumer.page_addr;
+	epf->fault.type_level = pf->consumer.fault_type_level;
+	epf->fault.access_type = pf->consumer.access_type;
+
+	pf->consumer.epf = epf;
+
+	xe_force_wake_put(gt_to_fw(gt), fw_ref);
+	xe_eudebug_put(d);
+
+	return;
+
+err_unlock_hw_lock:
+	mutex_unlock(&d->hw.lock);
+	xe_eudebug_attention_poll_start(gt_to_xe(gt));
+	kfree(epf);
+err_put_fw:
+	xe_force_wake_put(gt_to_fw(gt), fw_ref);
+err_put_exec_queue:
+	xe_exec_queue_put(q);
+err_put_eudebug:
+	xe_eudebug_put(d);
+}
+
+struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
+{
+	struct xe_vma *vma = NULL;
+
+	if (!pf->consumer.epf)
+		return NULL;
+
+	vma = xe_vm_create_null_vma(vm, pf->consumer.page_addr);
+	if (IS_ERR(vma))
+		return vma;
+
+	pf->consumer.epf->is_null = true;
+
+	return vma;
+}
+
+static void
+xe_eudebug_pagefault_process(struct xe_eudebug_pagefault *pf)
+{
+	struct xe_gt *gt = pf->q->gt;
+
+	xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
+				 XE_GT_ATTENTION_TIMEOUT_MS);
+
+	if (!xe_eu_attentions_xor_count(&pf->attentions.after,
+					&pf->attentions.resolved))
+		pf->deferred_resolved = true;
+}
+
+static void
+_xe_eudebug_pagefault_destroy(struct xe_eudebug_pagefault *pf)
+{
+	struct xe_gt *gt = pf->q->gt;
+	struct xe_vm *vm = pf->q->vm;
+	struct xe_eudebug *d;
+	unsigned int fw_ref;
+	u32 td_ctl;
+	bool queued, try_send;
+	int ret;
+
+	fw_ref = xe_force_wake_get(gt_to_fw(gt), pf->q->hwe->domain);
+	if (!fw_ref) {
+		struct xe_device *xe = gt_to_xe(gt);
+
+		drm_warn(&xe->drm, "Forcewake fail: Can not recover TD_CTL");
+	} else {
+		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
+		xe_gt_mcr_multicast_write(gt, TD_CTL, td_ctl &
+					  ~(TD_CTL_FORCE_EXCEPTION));
+		xe_force_wake_put(gt_to_fw(gt), fw_ref);
+	}
+
+	queued = false;
+	try_send = pf->is_null;
+	if (try_send) {
+		ret = send_pagefault(pf, false);
+
+		/*
+		 * if debugger discovery is not completed or resolved attentions are not
+		 * updated, then queue pagefault
+		 */
+		if (ret == -EBUSY) {
+			ret = queue_pagefault(pf);
+			if (!ret)
+				queued = true;
+		}
+	}
+
+	d = xe_eudebug_get_nolock(vm->xef);
+	if (d) {
+		struct dma_fence *f;
+
+		mutex_lock(&d->hw.lock);
+		f = d->pf_fence;
+		d->pf_fence = NULL;
+		mutex_unlock(&d->hw.lock);
+
+		if (f) {
+			if (!queued)
+				dma_fence_signal(f);
+
+			dma_fence_put(f);
+		}
+
+		xe_eudebug_put(d);
+	}
+
+	if (!queued)
+		destroy_pagefault(pf);
+
+	xe_eudebug_attention_poll_start(gt_to_xe(gt));
+}
+
+static int send_queued_pagefaults(struct xe_eudebug *d)
+{
+	struct xe_eudebug_pagefault *pf, *pf_temp;
+	int ret = 0;
+
+	mutex_lock(&d->pf_lock);
+	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
+		ret = send_pagefault(pf, true);
+
+		/* if resolved attentions are not updated */
+		if (ret == -EBUSY)
+			break;
+
+		list_del(&pf->link);
+
+		destroy_pagefault(pf);
+
+		if (ret)
+			break;
+	}
+	mutex_unlock(&d->pf_lock);
+
+	return ret;
+}
+
+int xe_eudebug_handle_pagefaults(struct xe_gt *gt)
+{
+	struct xe_exec_queue *q;
+	struct xe_eudebug *d;
+	int ret, lrc_idx;
+
+	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
+	if (IS_ERR(q))
+		return PTR_ERR(q);
+
+	if (!xe_exec_queue_is_debuggable(q)) {
+		ret = -EPERM;
+		goto out_exec_queue_put;
+	}
+
+	d = xe_eudebug_get_nolock(q->vm->xef);
+	if (!d) {
+		ret = -ENOTCONN;
+		goto out_exec_queue_put;
+	}
+
+	ret = send_queued_pagefaults(d);
+
+	xe_eudebug_put(d);
+
+out_exec_queue_put:
+	xe_exec_queue_put(q);
+
+	return ret;
+}
+
+void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
+{
+	struct xe_eudebug_pagefault *f = pf->consumer.epf;
+
+	if (!f)
+		return;
+
+	if (f->is_null)
+		xe_eudebug_pagefault_process(f);
+}
+
+void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
+{
+	struct xe_eudebug_pagefault *f = pf->consumer.epf;
+
+	if (!f)
+		return;
+
+	if (err)
+		f->is_null = false;
+
+	_xe_eudebug_pagefault_destroy(f);
+}
+
+void xe_eudebug_pagefault_fini(struct xe_eudebug *d)
+{
+	struct xe_eudebug_pagefault *pf, *pf_temp;
+
+	/* Since it's the last reference no race here */
+
+	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
+		list_del(&pf->link);
+		destroy_pagefault(pf);
+	}
+
+	XE_WARN_ON(d->pf_fence);
+}
diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.h b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
new file mode 100644
index 000000000000..1ba20beac3cf
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2023-2025 Intel Corporation
+ */
+
+#ifndef _XE_EUDEBUG_PAGEFAULT_H_
+#define _XE_EUDEBUG_PAGEFAULT_H_
+
+#include <linux/types.h>
+
+struct xe_eudebug;
+struct xe_gt;
+struct xe_pagefault;
+struct xe_eudebug_pagefault;
+struct xe_vm;
+
+void xe_eudebug_pagefault_fini(struct xe_eudebug *d);
+int xe_eudebug_handle_pagefaults(struct xe_gt *gt);
+
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+void xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf);
+struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf);
+void xe_eudebug_pagefault_service(struct xe_pagefault *pf);
+void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err);
+#else
+
+static inline void
+xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
+{
+}
+
+static inline struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
+{
+	return NULL;
+}
+
+static inline void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
+{
+}
+
+static inline void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
+{
+}
+
+#endif
+
+#endif /* _XE_EUDEBUG_PAGEFAULT_H_ */
diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
index 386b5c78ecff..09bfae8b94ab 100644
--- a/drivers/gpu/drm/xe/xe_eudebug_types.h
+++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
@@ -15,6 +15,8 @@
 #include <linux/wait.h>
 #include <linux/xarray.h>
 
+#include "xe_gt_debug_types.h"
+
 struct xe_device;
 struct task_struct;
 struct xe_eudebug;
@@ -37,7 +39,7 @@ enum xe_eudebug_state {
 };
 
 #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
-#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EU_ATTENTION
+#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_PAGEFAULT
 
 /**
  * struct xe_eudebug_handle - eudebug resource handle
@@ -164,6 +166,71 @@ struct xe_eudebug {
 
 	/** @ops: operations for eu_control */
 	struct xe_eudebug_eu_control_ops *ops;
+
+	/** @pf_lock: guards access to pagefaults list*/
+	struct mutex pf_lock;
+	/** @pagefaults: xe_eudebug_pagefault list for pagefault event queuing */
+	struct list_head pagefaults;
+	/**
+	 * @pf_fence: fence on operations of eus (eu thread control and attention)
+	 * when page faults are being handled, protected by @eu_lock.
+	 */
+	struct dma_fence *pf_fence;
+};
+
+/**
+ * struct xe_eudebug_pagefault - eudebug structure for queuing pagefault
+ */
+struct xe_eudebug_pagefault {
+	/** @link: link into the xe_eudebug.pagefaults */
+	struct list_head link;
+	/** @q: exec_queue which raised pagefault */
+	struct xe_exec_queue *q;
+	/** @lrc_idx: lrc index of the workload which raised pagefault */
+	int lrc_idx;
+
+	/** @fault: pagefault raw partial data passed from guc */
+	struct {
+		/** @addr: ppgtt address where the pagefault occurred */
+		u64 addr;
+		u8 type_level;
+		u8 access_type;
+	} fault;
+
+	/** @attentions: attention states in different phases of fault */
+	struct {
+		/** @before: state of attention bits before page fault WA processing*/
+		struct xe_eu_attentions before;
+		/**
+		 * @after: status of attention bits during page fault WA processing.
+		 * It includes eu threads where attention bits are turned on for
+		 * reasons other than page fault WA (breakpoint, interrupt, etc.).
+		 */
+		struct xe_eu_attentions after;
+		/**
+		 * @resolved: state of the attention bits after page fault WA.
+		 * It includes the eu thread that caused the page fault.
+		 * To determine the eu thread that caused the page fault,
+		 * do XOR attentions.after and attentions.resolved.
+		 */
+		struct xe_eu_attentions resolved;
+	} attentions;
+
+	/**
+	 * @deferred_resolved: to update attentions.resolved again when attention
+	 * bits are ready if the eu thread fails to turn on attention bits within
+	 * a certain time after page fault WA processing.
+	 */
+	bool deferred_resolved;
+
+	/**
+	 * @is_null: marks if this vma is null or not. The lookup for the
+	 * vma is done in two phases and eudebug pagefault struct needs
+	 * to be allocated apriori to resolving if we need null vma or not.
+	 * So we keep the state here so that processing and teardown
+	 * know which type of fault resulted in creation of this eudebug pf.
+	 */
+	bool is_null;
 };
 
 #endif /* _XE_EUDEBUG_TYPES_H_ */
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
index 0e378f41ede6..2bee858da597 100644
--- a/drivers/gpu/drm/xe/xe_pagefault_types.h
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -10,6 +10,7 @@
 
 struct xe_gt;
 struct xe_pagefault;
+struct xe_eudebug_pagefault;
 
 /** enum xe_pagefault_access_type - Xe page fault access type */
 enum xe_pagefault_access_type {
@@ -84,6 +85,9 @@ struct xe_pagefault {
 		u8 engine_class;
 		/** @consumer.engine_instance: engine instance */
 		u8 engine_instance;
+#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
+		struct xe_eudebug_pagefault *epf;
+#endif
 		/** consumer.reserved: reserved bits for future expansion */
 		u64 reserved;
 	} consumer;
diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
index 54394a7e12ab..f7d035532be2 100644
--- a/include/uapi/drm/xe_drm_eudebug.h
+++ b/include/uapi/drm/xe_drm_eudebug.h
@@ -53,6 +53,7 @@ struct drm_xe_eudebug_event {
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
 #define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	6
 #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	7
+#define DRM_XE_EUDEBUG_EVENT_PAGEFAULT		8
 
 	/** @flags: Flags */
 	__u16 flags;
@@ -358,6 +359,17 @@ struct drm_xe_eudebug_event_eu_attention {
 	__u8 bitmask[];
 };
 
+struct drm_xe_eudebug_event_pagefault {
+	struct drm_xe_eudebug_event base;
+
+	__u64 exec_queue_handle;
+	__u64 lrc_handle;
+	__u32 flags;
+	__u32 bitmask_size;
+	__u64 pagefault_address;
+	__u8 bitmask[];
+};
+
 #if defined(__cplusplus)
 }
 #endif
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (20 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface Mika Kuoppala
@ 2026-02-23 14:03 ` Mika Kuoppala
  2026-02-23 18:41   ` Matthew Brost
  2026-02-27 23:11   ` Gustavo Sousa
  2026-02-23 15:14 ` ✗ CI.checkpatch: warning for Intel Xe GPU Debug Support (eudebug) v7 Patchwork
                   ` (4 subsequent siblings)
  26 siblings, 2 replies; 35+ messages in thread
From: Mika Kuoppala @ 2026-02-23 14:03 UTC (permalink / raw)
  To: intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>

The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
access will halt the corresponding EUs. To solve this problem, enable
EU pagefault handling functionality, which allows to unhalt pagefaulted
eu threads and to EU debugger to get inform about the eu attentions state
of EU threads during execution.

If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
after handling the pagefault.

The pagefault handling is a mechanism that allows a stalled EU thread to
enter SIP mode by installing a temporal null page to the page table entry
where the pagefault happened.

A brief description of the page fault handling mechanism flow between KMD
and the eu thread is as follows

(1) eu thread accesses unallocated address
(2) pagefault happens and eu thread stalls
(3) XE kmd set an force eu thread exception to allow the running eu thread
    to enter SIP mode (kmd set ForceException / ForceExternalHalt bit of
    TD_CTL register)
    Not stalled (none-pagefaulted) eu threads enter SIP mode
(4) XE kmd installs temporal null page to the pagetable entry of the
    address where pagefault happened.
(5) XE kmd replies pagefault successful message to GUC
(6) stalled eu thread resumes as per pagefault condition has resolved
(7) resumed eu thread enters SIP mode due to force exception set by (3)
(8) adapted to consumer/produced pagefaults

As designed this feature to only work when eudbug is enabled, it should
have no impact to regular recoverable pagefault code path.

v2: - pf->q holds the vm ref so drop it (Mika)
    - streamline uapi (Mika)
    - cleanup the pagefault through producer if (Mika)

Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/xe/xe_guc_pagefault.c   |  8 +++++++
 drivers/gpu/drm/xe/xe_pagefault.c       | 31 ++++++++++++++++++++++++-
 drivers/gpu/drm/xe/xe_pagefault_types.h |  9 +++++++
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
index d48f6ed103bb..6adf3bf73b1c 100644
--- a/drivers/gpu/drm/xe/xe_guc_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
@@ -8,6 +8,7 @@
 #include "xe_guc_ct.h"
 #include "xe_guc_pagefault.h"
 #include "xe_pagefault.h"
+#include "xe_eudebug_pagefault.h"
 
 static void guc_ack_fault(struct xe_pagefault *pf, int err)
 {
@@ -37,8 +38,15 @@ static void guc_ack_fault(struct xe_pagefault *pf, int err)
 	xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
 }
 
+static void guc_cleanup_fault(struct xe_pagefault *pf, int err)
+{
+	xe_eudebug_pagefault_service(pf);
+	xe_eudebug_pagefault_destroy(pf, 0);
+}
+
 static const struct xe_pagefault_ops guc_pagefault_ops = {
 	.ack_fault = guc_ack_fault,
+	.cleanup_fault = guc_cleanup_fault,
 };
 
 /**
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
index 72f589fd2b64..9dcd854e99f9 100644
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -10,6 +10,7 @@
 
 #include "xe_bo.h"
 #include "xe_device.h"
+#include "xe_eudebug_pagefault.h"
 #include "xe_gt_printk.h"
 #include "xe_gt_types.h"
 #include "xe_gt_stats.h"
@@ -171,6 +172,8 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
 	if (IS_ERR(vm))
 		return PTR_ERR(vm);
 
+	xe_eudebug_pagefault_create(vm, pf);
+
 	/*
 	 * TODO: Change to read lock? Using write lock for simplicity.
 	 */
@@ -184,9 +187,28 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
 	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
 	if (!vma) {
 		err = -EINVAL;
-		goto unlock_vm;
+		vma = xe_eudebug_create_vma(vm, pf);
+		if (IS_ERR(vma)) {
+			err = PTR_ERR(vma);
+			vma = NULL;
+		}
 	}
 
+	if (vma) {
+		/*
+		 * When creating an instance of eudebug_pagefault, there was
+		 * no vma containing the ppgtt address where the pagefault occurred,
+		 * but when reacquiring vm->lock, there is.
+		 * During not aquiring the vm->lock from this context,
+		 * but vma corresponding to the address where the pagefault occurred
+		 * in another context has allocated.
+		 */
+		err = 0;
+	}
+
+	if (err)
+		goto unlock_vm;
+
 	atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
 
 	if (xe_vma_is_cpu_addr_mirror(vma))
@@ -198,6 +220,10 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
 unlock_vm:
 	if (!err)
 		vm->usm.last_fault_vma = vma;
+
+	if (err)
+		xe_eudebug_pagefault_destroy(pf, err);
+
 	up_write(&vm->lock);
 	xe_vm_put(vm);
 
@@ -268,6 +294,9 @@ static void xe_pagefault_queue_work(struct work_struct *w)
 
 		pf.producer.ops->ack_fault(&pf, err);
 
+		if (pf.producer.ops->cleanup_fault)
+			pf.producer.ops->cleanup_fault(&pf, err);
+
 		if (time_after(jiffies, threshold)) {
 			queue_work(gt_to_xe(pf.gt)->usm.pf_wq, w);
 			break;
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
index 2bee858da597..9d2d29d35a4b 100644
--- a/drivers/gpu/drm/xe/xe_pagefault_types.h
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -43,6 +43,15 @@ struct xe_pagefault_ops {
 	 * sends the result to the HW/FW interface.
 	 */
 	void (*ack_fault)(struct xe_pagefault *pf, int err);
+
+	/**
+	 * @cleanup_fault: Cleanup for producer, if any
+	 * @pf: Page fault
+	 * @err: Error state of fault
+	 *
+	 * Page fault producer received cleanup request from consumer
+	 */
+	void (*cleanup_fault)(struct xe_pagefault *pf, int err);
 };
 
 /**
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* ✗ CI.checkpatch: warning for Intel Xe GPU Debug Support (eudebug) v7
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (21 preceding siblings ...)
  2026-02-23 14:03 ` [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling Mika Kuoppala
@ 2026-02-23 15:14 ` Patchwork
  2026-02-23 15:16 ` ✓ CI.KUnit: success " Patchwork
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Patchwork @ 2026-02-23 15:14 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

== Series Details ==

Series: Intel Xe GPU Debug Support (eudebug) v7
URL   : https://patchwork.freedesktop.org/series/161979/
State : warning

== Summary ==

+ KERNEL=/kernel
+ git clone https://gitlab.freedesktop.org/drm/maintainer-tools mt
Cloning into 'mt'...
warning: redirecting to https://gitlab.freedesktop.org/drm/maintainer-tools.git/
+ git -C mt rev-list -n1 origin/master
1f57ba1afceae32108bd24770069f764d940a0e4
+ cd /kernel
+ git config --global --add safe.directory /kernel
+ git log -n1
commit 4356dbe33a5eb3ae829a1911e21aa2712b055693
Author: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Date:   Mon Feb 23 16:03:17 2026 +0200

    drm/xe/eudebug: Enable EU pagefault handling
    
    The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
    access will halt the corresponding EUs. To solve this problem, enable
    EU pagefault handling functionality, which allows to unhalt pagefaulted
    eu threads and to EU debugger to get inform about the eu attentions state
    of EU threads during execution.
    
    If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
    after handling the pagefault.
    
    The pagefault handling is a mechanism that allows a stalled EU thread to
    enter SIP mode by installing a temporal null page to the page table entry
    where the pagefault happened.
    
    A brief description of the page fault handling mechanism flow between KMD
    and the eu thread is as follows
    
    (1) eu thread accesses unallocated address
    (2) pagefault happens and eu thread stalls
    (3) XE kmd set an force eu thread exception to allow the running eu thread
        to enter SIP mode (kmd set ForceException / ForceExternalHalt bit of
        TD_CTL register)
        Not stalled (none-pagefaulted) eu threads enter SIP mode
    (4) XE kmd installs temporal null page to the pagetable entry of the
        address where pagefault happened.
    (5) XE kmd replies pagefault successful message to GUC
    (6) stalled eu thread resumes as per pagefault condition has resolved
    (7) resumed eu thread enters SIP mode due to force exception set by (3)
    (8) adapted to consumer/produced pagefaults
    
    As designed this feature to only work when eudbug is enabled, it should
    have no impact to regular recoverable pagefault code path.
    
    v2: - pf->q holds the vm ref so drop it (Mika)
        - streamline uapi (Mika)
        - cleanup the pagefault through producer if (Mika)
    
    Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
    Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
+ /mt/dim checkpatch 45a3045fc0dc46a893cb8bbe304afafd4120c904 drm-intel
c1065702b0ff drm/xe/eudebug: Introduce eudebug interface
-:226: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#226: 
new file mode 100644

-:284: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!READ_ONCE"
#284: FILE: drivers/gpu/drm/xe/xe_eudebug.c:54:
+	return READ_ONCE(d->target.xef) == NULL;

-:467: CHECK:MACRO_ARG_REUSE: Macro argument reuse '_d' - possible side-effects?
#467: FILE: drivers/gpu/drm/xe/xe_eudebug.c:237:
+#define xe_eudebug_disconnect(_d, _err) ({ \
+	if (_xe_eudebug_disconnect((_d), (_err))) { \
+		if ((_err) == 0 || (_err) == -ETIMEDOUT) \
+			eu_dbg((_d), "Session closed (%d)", (_err));	\
+		else \
+			eu_err((_d), "Session disconnected, err = %d (%s:%d)", \
+			       (_err), __func__, __LINE__); \
+	} \
+})

-:467: CHECK:MACRO_ARG_REUSE: Macro argument reuse '_err' - possible side-effects?
#467: FILE: drivers/gpu/drm/xe/xe_eudebug.c:237:
+#define xe_eudebug_disconnect(_d, _err) ({ \
+	if (_xe_eudebug_disconnect((_d), (_err))) { \
+		if ((_err) == 0 || (_err) == -ETIMEDOUT) \
+			eu_dbg((_d), "Session closed (%d)", (_err));	\
+		else \
+			eu_err((_d), "Session disconnected, err = %d (%s:%d)", \
+			       (_err), __func__, __LINE__); \
+	} \
+})

-:819: CHECK:MACRO_ARG_REUSE: Macro argument reuse '_d' - possible side-effects?
#819: FILE: drivers/gpu/drm/xe/xe_eudebug.c:589:
+#define xe_eudebug_event_put(_d, _err) ({ \
+	if ((_err)) \
+		xe_eudebug_disconnect((_d), (_err)); \
+	xe_eudebug_put((_d)); \
+	})

-:819: CHECK:MACRO_ARG_REUSE: Macro argument reuse '_err' - possible side-effects?
#819: FILE: drivers/gpu/drm/xe/xe_eudebug.c:589:
+#define xe_eudebug_event_put(_d, _err) ({ \
+	if ((_err)) \
+		xe_eudebug_disconnect((_d), (_err)); \
+	xe_eudebug_put((_d)); \
+	})

-:1283: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#1283: FILE: drivers/gpu/drm/xe/xe_eudebug.h:20:
+#define XE_EUDEBUG_DBG_ARGS(d) (d)->session, \
+		atomic_long_read(&(d)->events.seqno), \
+		!READ_ONCE(d->target.xef) ? "disconnected" : "", \
+		current->pid, \
+		task_tgid_nr(current), \
+		READ_ONCE(d->target.xef) ? d->target.xef->pid : -1

BUT SEE:

   do {} while (0) advice is over-stated in a few situations:

   The more obvious case is macros, like MODULE_PARM_DESC, invoked at
   file-scope, where C disallows code (it must be in functions).  See
   $exceptions if you have one to add by name.

   More troublesome is declarative macros used at top of new scope,
   like DECLARE_PER_CPU.  These might just compile with a do-while-0
   wrapper, but would be incorrect.  Most of these are handled by
   detecting struct,union,etc declaration primitives in $exceptions.

   Theres also macros called inside an if (block), which "return" an
   expression.  These cannot do-while, and need a ({}) wrapper.

   Enjoy this qualification while we work to improve our heuristics.

-:1283: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'd' - possible side-effects?
#1283: FILE: drivers/gpu/drm/xe/xe_eudebug.h:20:
+#define XE_EUDEBUG_DBG_ARGS(d) (d)->session, \
+		atomic_long_read(&(d)->events.seqno), \
+		!READ_ONCE(d->target.xef) ? "disconnected" : "", \
+		current->pid, \
+		task_tgid_nr(current), \
+		READ_ONCE(d->target.xef) ? d->target.xef->pid : -1

-:1290: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'd' - possible side-effects?
#1290: FILE: drivers/gpu/drm/xe/xe_eudebug.h:27:
+#define eu_err(d, fmt, ...) drm_err(&(d)->xe->drm, XE_EUDEBUG_DBG_STR # fmt, \
+				    XE_EUDEBUG_DBG_ARGS(d), ##__VA_ARGS__)

-:1292: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'd' - possible side-effects?
#1292: FILE: drivers/gpu/drm/xe/xe_eudebug.h:29:
+#define eu_warn(d, fmt, ...) drm_warn(&(d)->xe->drm, XE_EUDEBUG_DBG_STR # fmt, \
+				      XE_EUDEBUG_DBG_ARGS(d), ##__VA_ARGS__)

-:1294: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'd' - possible side-effects?
#1294: FILE: drivers/gpu/drm/xe/xe_eudebug.h:31:
+#define eu_dbg(d, fmt, ...) drm_dbg(&(d)->xe->drm, XE_EUDEBUG_DBG_STR # fmt, \
+				    XE_EUDEBUG_DBG_ARGS(d), ##__VA_ARGS__)

-:1505: WARNING:LONG_LINE: line length of 130 exceeds 100 columns
#1505: FILE: include/uapi/drm/xe_drm.h:129:
+#define DRM_IOCTL_XE_EUDEBUG_CONNECT		DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_EUDEBUG_CONNECT, struct drm_xe_eudebug_connect)

total: 1 errors, 2 warnings, 9 checks, 1491 lines checked
b6a8216a1b6d drm/xe/eudebug: Add documentation
-:21: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#21: 
new file mode 100644

total: 0 errors, 1 warnings, 0 checks, 108 lines checked
c942cff65862 drm/xe/eudebug: Add connection establishment documentation
4614d5180fcc drm/xe/eudebug: Introduce discovery for resources
7d1a7be2ee3c drm/xe/eudebug: Introduce exec_queue events
feff6753f4d3 drm/xe: Add EUDEBUG_ENABLE exec queue property
52ec38632a8c drm/xe/eudebug: Mark guc contexts as debuggable
6adca595de2d drm/xe: Introduce ADD_DEBUG_DATA and REMOVE_DEBUG_DATA vm bind ops
-:44: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#44: 
new file mode 100644

-:617: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#617: FILE: drivers/gpu/drm/xe/xe_vm.c:3478:
+		if (XE_IOCTL_DBG(xe, operation != DRM_XE_VM_BIND_OP_ADD_DEBUG_DATA &&
+				     operation != DRM_XE_VM_BIND_OP_REMOVE_DEBUG_DATA &&

-:620: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#620: FILE: drivers/gpu/drm/xe/xe_vm.c:3481:
+		    XE_IOCTL_DBG(xe, ext.name == XE_VM_BIND_OP_EXTENSIONS_DEBUG_DATA &&
+				     ++debug_data_count > 1))

-:740: CHECK:UNCOMMENTED_DEFINITION: struct mutex definition without comment
#740: FILE: drivers/gpu/drm/xe/xe_vm_types.h:372:
+		struct mutex lock;

total: 0 errors, 1 warnings, 3 checks, 787 lines checked
63c125f233b9 drm/xe/eudebug: Introduce vm bind and vm bind debug data events
-:7: WARNING:COMMIT_LOG_LONG_LINE: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#7: 
This patch adds events to track the bind ioctl and associated debug data add

-:414: WARNING:LONG_LINE_COMMENT: line length of 102 exceeds 100 columns
#414: FILE: include/uapi/drm/xe_drm_eudebug.h:112:
+ *  │  EVENT_VM_BIND        ├──────────────────┬─┬┄┐

-:415: WARNING:LONG_LINE_COMMENT: line length of 108 exceeds 100 columns
#415: FILE: include/uapi/drm/xe_drm_eudebug.h:113:
+ *  └───────────────────────┘                  │ │ ┊

-:416: WARNING:LONG_LINE_COMMENT: line length of 130 exceeds 100 columns
#416: FILE: include/uapi/drm/xe_drm_eudebug.h:114:
+ *      ┌──────────────────────────────────┐   │ │ ┊

-:418: WARNING:LONG_LINE_COMMENT: line length of 128 exceeds 100 columns
#418: FILE: include/uapi/drm/xe_drm_eudebug.h:116:
+ *      └──────────────────────────────────┘     │ ┊

-:420: WARNING:LONG_LINE_COMMENT: line length of 128 exceeds 100 columns
#420: FILE: include/uapi/drm/xe_drm_eudebug.h:118:
+ *      ┌──────────────────────────────────┐     │ ┊

-:422: WARNING:LONG_LINE_COMMENT: line length of 126 exceeds 100 columns
#422: FILE: include/uapi/drm/xe_drm_eudebug.h:120:
+ *      └──────────────────────────────────┘       ┊

-:424: WARNING:LONG_LINE_COMMENT: line length of 126 exceeds 100 columns
#424: FILE: include/uapi/drm/xe_drm_eudebug.h:122:
+ *      ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐       ┊

-:426: WARNING:LONG_LINE_COMMENT: line length of 116 exceeds 100 columns
#426: FILE: include/uapi/drm/xe_drm_eudebug.h:124:
+ *      └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘

total: 0 errors, 9 warnings, 0 checks, 427 lines checked
ca09231b0e9e drm/xe/eudebug: Add UFENCE events with acks
-:320: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!ufence"
#320: FILE: drivers/gpu/drm/xe/xe_eudebug.c:1273:
+			xe_assert(vm->xe, ufence == NULL);

-:621: CHECK:UNCOMMENTED_DEFINITION: spinlock_t definition without comment
#621: FILE: drivers/gpu/drm/xe/xe_sync_types.h:26:
+		spinlock_t lock;

total: 0 errors, 0 warnings, 2 checks, 640 lines checked
19cb953494c2 drm/xe/eudebug: vm open/pread/pwrite
-:138: CHECK:BRACES: Blank lines aren't necessary after an open brace '{'
#138: FILE: drivers/gpu/drm/xe/xe_eudebug.c:747:
+{
+

-:161: CHECK:LINE_SPACING: Please don't use multiple blank lines
#161: FILE: drivers/gpu/drm/xe/xe_eudebug.c:770:
+
+

-:201: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parentheses
#201: FILE: drivers/gpu/drm/xe/xe_eudebug.h:42:
+#define xe_eudebug_for_each_hw_engine(__hwe, __gt, __id) \
+	for_each_hw_engine(__hwe, __gt, __id)	       \
+		if (xe_hw_engine_has_eudebug(__hwe))

BUT SEE:

   do {} while (0) advice is over-stated in a few situations:

   The more obvious case is macros, like MODULE_PARM_DESC, invoked at
   file-scope, where C disallows code (it must be in functions).  See
   $exceptions if you have one to add by name.

   More troublesome is declarative macros used at top of new scope,
   like DECLARE_PER_CPU.  These might just compile with a do-while-0
   wrapper, but would be incorrect.  Most of these are handled by
   detecting struct,union,etc declaration primitives in $exceptions.

   Theres also macros called inside an if (block), which "return" an
   expression.  These cannot do-while, and need a ({}) wrapper.

   Enjoy this qualification while we work to improve our heuristics.

-:201: CHECK:MACRO_ARG_REUSE: Macro argument reuse '__hwe' - possible side-effects?
#201: FILE: drivers/gpu/drm/xe/xe_eudebug.h:42:
+#define xe_eudebug_for_each_hw_engine(__hwe, __gt, __id) \
+	for_each_hw_engine(__hwe, __gt, __id)	       \
+		if (xe_hw_engine_has_eudebug(__hwe))

-:251: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#251: 
new file mode 100644

total: 1 errors, 1 warnings, 3 checks, 642 lines checked
43418d456fb5 drm/xe/eudebug: userptr vm pread/pwrite
bf5b27b0ca3f drm/xe/eudebug: hw enablement for eudebug
-:107: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#107: 
new file mode 100644

total: 0 errors, 1 warnings, 0 checks, 452 lines checked
fc993f381517 drm/xe/eudebug: Introduce EU control interface
-:297: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#297: FILE: drivers/gpu/drm/xe/xe_eudebug_hw.c:183:
+static bool engine_has_runalone_set(const struct xe_hw_engine * const hwe,
+				   u32 rcu_debug1)

-:303: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#303: FILE: drivers/gpu/drm/xe/xe_eudebug_hw.c:189:
+static bool engine_has_context_set(const struct xe_hw_engine * const hwe,
+				  u32 rcu_debug1)

total: 0 errors, 0 warnings, 2 checks, 912 lines checked
94daf711b082 drm/xe/eudebug: Introduce per device attention scan worker
f8c8e4cfbf22 drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test
-:16: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#16: 
new file mode 100644

total: 0 errors, 1 warnings, 0 checks, 201 lines checked
6a1fb3c4be57 drm/xe: Implement SR-IOV and eudebug exclusivity
d4023d6256f3 drm/xe: Add xe_client_debugfs and introduce debug_data file
-:31: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#31: 
new file mode 100644

-:85: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis
#85: FILE: drivers/gpu/drm/xe/xe_client_debugfs.c:50:
+			len = snprintf(kbuf, MAX_LINE_LEN, "%lu 0x%llx-0x%llx 0x%llx 0x%x\t%s\n",
+				vm_index,

total: 0 errors, 1 warnings, 1 checks, 161 lines checked
bf32a13c5225 drm/xe/eudebug: Add read/count/compare helper for eu attention
-:127: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#127: 
new file mode 100644

-:149: CHECK:SPACING: spaces preferred around that '/' (ctx:VxV)
#149: FILE: drivers/gpu/drm/xe/xe_gt_debug_types.h:18:
+	       XE_GT_EU_ATT_MAX_THREADS/8];
 	                               ^

total: 0 errors, 1 warnings, 1 checks, 120 lines checked
29924ce91f73 drm/xe/vm: Support for adding null page VMA to VM on request
-:15: WARNING:COMMIT_LOG_LONG_LINE: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#15: 
[1] https://lore.kernel.org/intel-xe/20230829231648.4438-1-yu.bruce.chang@intel.com/

total: 0 errors, 1 warnings, 0 checks, 43 lines checked
c4c56069ef09 drm/xe/eudebug: Introduce EU pagefault handling interface
-:338: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#338: 
new file mode 100644

-:455: CHECK:UNCOMMENTED_DEFINITION: spinlock_t definition without comment
#455: FILE: drivers/gpu/drm/xe/xe_eudebug_pagefault.c:113:
+	spinlock_t lock;

-:463: CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written "!fence"
#463: FILE: drivers/gpu/drm/xe/xe_eudebug_pagefault.c:121:
+	if (fence == NULL)

-:559: CHECK:USLEEP_RANGE: usleep_range is preferred over udelay; see function description of usleep_range() and udelay().
#559: FILE: drivers/gpu/drm/xe/xe_eudebug_pagefault.c:217:
+		udelay(200);

total: 0 errors, 1 warnings, 3 checks, 857 lines checked
4356dbe33a5e drm/xe/eudebug: Enable EU pagefault handling



^ permalink raw reply	[flat|nested] 35+ messages in thread

* ✓ CI.KUnit: success for Intel Xe GPU Debug Support (eudebug) v7
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (22 preceding siblings ...)
  2026-02-23 15:14 ` ✗ CI.checkpatch: warning for Intel Xe GPU Debug Support (eudebug) v7 Patchwork
@ 2026-02-23 15:16 ` Patchwork
  2026-02-23 15:31 ` ✗ CI.checksparse: warning " Patchwork
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 35+ messages in thread
From: Patchwork @ 2026-02-23 15:16 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

== Series Details ==

Series: Intel Xe GPU Debug Support (eudebug) v7
URL   : https://patchwork.freedesktop.org/series/161979/
State : success

== Summary ==

+ trap cleanup EXIT
+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/xe/.kunitconfig
[15:14:52] Configuring KUnit Kernel ...
Generating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[15:14:57] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
[15:15:27] Starting KUnit Kernel (1/1)...
[15:15:27] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[15:15:28] ================== guc_buf (11 subtests) ===================
[15:15:28] [PASSED] test_smallest
[15:15:28] [PASSED] test_largest
[15:15:28] [PASSED] test_granular
[15:15:28] [PASSED] test_unique
[15:15:28] [PASSED] test_overlap
[15:15:28] [PASSED] test_reusable
[15:15:28] [PASSED] test_too_big
[15:15:28] [PASSED] test_flush
[15:15:28] [PASSED] test_lookup
[15:15:28] [PASSED] test_data
[15:15:28] [PASSED] test_class
[15:15:28] ===================== [PASSED] guc_buf =====================
[15:15:28] =================== guc_dbm (7 subtests) ===================
[15:15:28] [PASSED] test_empty
[15:15:28] [PASSED] test_default
[15:15:28] ======================== test_size  ========================
[15:15:28] [PASSED] 4
[15:15:28] [PASSED] 8
[15:15:28] [PASSED] 32
[15:15:28] [PASSED] 256
[15:15:28] ==================== [PASSED] test_size ====================
[15:15:28] ======================= test_reuse  ========================
[15:15:28] [PASSED] 4
[15:15:28] [PASSED] 8
[15:15:28] [PASSED] 32
[15:15:28] [PASSED] 256
[15:15:28] =================== [PASSED] test_reuse ====================
[15:15:28] =================== test_range_overlap  ====================
[15:15:28] [PASSED] 4
[15:15:28] [PASSED] 8
[15:15:28] [PASSED] 32
[15:15:28] [PASSED] 256
[15:15:28] =============== [PASSED] test_range_overlap ================
[15:15:28] =================== test_range_compact  ====================
[15:15:28] [PASSED] 4
[15:15:28] [PASSED] 8
[15:15:28] [PASSED] 32
[15:15:28] [PASSED] 256
[15:15:28] =============== [PASSED] test_range_compact ================
[15:15:28] ==================== test_range_spare  =====================
[15:15:28] [PASSED] 4
[15:15:28] [PASSED] 8
[15:15:28] [PASSED] 32
[15:15:28] [PASSED] 256
[15:15:28] ================ [PASSED] test_range_spare =================
[15:15:28] ===================== [PASSED] guc_dbm =====================
[15:15:28] =================== guc_idm (6 subtests) ===================
[15:15:28] [PASSED] bad_init
[15:15:28] [PASSED] no_init
[15:15:28] [PASSED] init_fini
[15:15:28] [PASSED] check_used
[15:15:28] [PASSED] check_quota
[15:15:28] [PASSED] check_all
[15:15:28] ===================== [PASSED] guc_idm =====================
[15:15:28] ================== no_relay (3 subtests) ===================
[15:15:28] [PASSED] xe_drops_guc2pf_if_not_ready
[15:15:28] [PASSED] xe_drops_guc2vf_if_not_ready
[15:15:28] [PASSED] xe_rejects_send_if_not_ready
[15:15:28] ==================== [PASSED] no_relay =====================
[15:15:28] ================== pf_relay (14 subtests) ==================
[15:15:28] [PASSED] pf_rejects_guc2pf_too_short
[15:15:28] [PASSED] pf_rejects_guc2pf_too_long
[15:15:28] [PASSED] pf_rejects_guc2pf_no_payload
[15:15:28] [PASSED] pf_fails_no_payload
[15:15:28] [PASSED] pf_fails_bad_origin
[15:15:28] [PASSED] pf_fails_bad_type
[15:15:28] [PASSED] pf_txn_reports_error
[15:15:28] [PASSED] pf_txn_sends_pf2guc
[15:15:28] [PASSED] pf_sends_pf2guc
[15:15:28] [SKIPPED] pf_loopback_nop
[15:15:28] [SKIPPED] pf_loopback_echo
[15:15:28] [SKIPPED] pf_loopback_fail
[15:15:28] [SKIPPED] pf_loopback_busy
[15:15:28] [SKIPPED] pf_loopback_retry
[15:15:28] ==================== [PASSED] pf_relay =====================
[15:15:28] ================== vf_relay (3 subtests) ===================
[15:15:28] [PASSED] vf_rejects_guc2vf_too_short
[15:15:28] [PASSED] vf_rejects_guc2vf_too_long
[15:15:28] [PASSED] vf_rejects_guc2vf_no_payload
[15:15:28] ==================== [PASSED] vf_relay =====================
[15:15:28] ================ pf_gt_config (9 subtests) =================
[15:15:28] [PASSED] fair_contexts_1vf
[15:15:28] [PASSED] fair_doorbells_1vf
[15:15:28] [PASSED] fair_ggtt_1vf
[15:15:28] ====================== fair_vram_1vf  ======================
[15:15:28] [PASSED] 3.50 GiB
[15:15:28] [PASSED] 11.5 GiB
[15:15:28] [PASSED] 15.5 GiB
[15:15:28] [PASSED] 31.5 GiB
[15:15:28] [PASSED] 63.5 GiB
[15:15:28] [PASSED] 13.9 GiB
[15:15:28] ================== [PASSED] fair_vram_1vf ==================
[15:15:28] ================ fair_vram_1vf_admin_only  =================
[15:15:28] [PASSED] 3.50 GiB
[15:15:28] [PASSED] 11.5 GiB
[15:15:28] [PASSED] 15.5 GiB
[15:15:28] [PASSED] 31.5 GiB
[15:15:28] [PASSED] 63.5 GiB
[15:15:28] [PASSED] 13.9 GiB
[15:15:28] ============ [PASSED] fair_vram_1vf_admin_only =============
[15:15:28] ====================== fair_contexts  ======================
[15:15:28] [PASSED] 1 VF
[15:15:28] [PASSED] 2 VFs
[15:15:28] [PASSED] 3 VFs
[15:15:28] [PASSED] 4 VFs
[15:15:28] [PASSED] 5 VFs
[15:15:28] [PASSED] 6 VFs
[15:15:28] [PASSED] 7 VFs
[15:15:28] [PASSED] 8 VFs
[15:15:28] [PASSED] 9 VFs
[15:15:28] [PASSED] 10 VFs
[15:15:28] [PASSED] 11 VFs
[15:15:28] [PASSED] 12 VFs
[15:15:28] [PASSED] 13 VFs
[15:15:28] [PASSED] 14 VFs
[15:15:28] [PASSED] 15 VFs
[15:15:28] [PASSED] 16 VFs
[15:15:28] [PASSED] 17 VFs
[15:15:28] [PASSED] 18 VFs
[15:15:28] [PASSED] 19 VFs
[15:15:28] [PASSED] 20 VFs
[15:15:28] [PASSED] 21 VFs
[15:15:28] [PASSED] 22 VFs
[15:15:28] [PASSED] 23 VFs
[15:15:28] [PASSED] 24 VFs
[15:15:28] [PASSED] 25 VFs
[15:15:28] [PASSED] 26 VFs
[15:15:28] [PASSED] 27 VFs
[15:15:28] [PASSED] 28 VFs
[15:15:28] [PASSED] 29 VFs
[15:15:28] [PASSED] 30 VFs
[15:15:28] [PASSED] 31 VFs
[15:15:28] [PASSED] 32 VFs
[15:15:28] [PASSED] 33 VFs
[15:15:28] [PASSED] 34 VFs
[15:15:28] [PASSED] 35 VFs
[15:15:28] [PASSED] 36 VFs
[15:15:28] [PASSED] 37 VFs
[15:15:28] [PASSED] 38 VFs
[15:15:28] [PASSED] 39 VFs
[15:15:28] [PASSED] 40 VFs
[15:15:28] [PASSED] 41 VFs
[15:15:28] [PASSED] 42 VFs
[15:15:28] [PASSED] 43 VFs
[15:15:28] [PASSED] 44 VFs
[15:15:28] [PASSED] 45 VFs
[15:15:28] [PASSED] 46 VFs
[15:15:28] [PASSED] 47 VFs
[15:15:28] [PASSED] 48 VFs
[15:15:28] [PASSED] 49 VFs
[15:15:28] [PASSED] 50 VFs
[15:15:28] [PASSED] 51 VFs
[15:15:28] [PASSED] 52 VFs
[15:15:28] [PASSED] 53 VFs
[15:15:28] [PASSED] 54 VFs
[15:15:28] [PASSED] 55 VFs
[15:15:28] [PASSED] 56 VFs
[15:15:28] [PASSED] 57 VFs
[15:15:28] [PASSED] 58 VFs
[15:15:28] [PASSED] 59 VFs
[15:15:28] [PASSED] 60 VFs
[15:15:28] [PASSED] 61 VFs
[15:15:28] [PASSED] 62 VFs
[15:15:28] [PASSED] 63 VFs
[15:15:28] ================== [PASSED] fair_contexts ==================
[15:15:28] ===================== fair_doorbells  ======================
[15:15:28] [PASSED] 1 VF
[15:15:28] [PASSED] 2 VFs
[15:15:28] [PASSED] 3 VFs
[15:15:28] [PASSED] 4 VFs
[15:15:28] [PASSED] 5 VFs
[15:15:28] [PASSED] 6 VFs
[15:15:28] [PASSED] 7 VFs
[15:15:28] [PASSED] 8 VFs
[15:15:28] [PASSED] 9 VFs
[15:15:28] [PASSED] 10 VFs
[15:15:28] [PASSED] 11 VFs
[15:15:28] [PASSED] 12 VFs
[15:15:28] [PASSED] 13 VFs
[15:15:28] [PASSED] 14 VFs
[15:15:28] [PASSED] 15 VFs
[15:15:28] [PASSED] 16 VFs
[15:15:28] [PASSED] 17 VFs
[15:15:28] [PASSED] 18 VFs
[15:15:28] [PASSED] 19 VFs
[15:15:28] [PASSED] 20 VFs
[15:15:28] [PASSED] 21 VFs
[15:15:28] [PASSED] 22 VFs
[15:15:28] [PASSED] 23 VFs
[15:15:28] [PASSED] 24 VFs
[15:15:28] [PASSED] 25 VFs
[15:15:28] [PASSED] 26 VFs
[15:15:28] [PASSED] 27 VFs
[15:15:28] [PASSED] 28 VFs
[15:15:28] [PASSED] 29 VFs
[15:15:28] [PASSED] 30 VFs
[15:15:28] [PASSED] 31 VFs
[15:15:28] [PASSED] 32 VFs
[15:15:28] [PASSED] 33 VFs
[15:15:28] [PASSED] 34 VFs
[15:15:28] [PASSED] 35 VFs
[15:15:28] [PASSED] 36 VFs
[15:15:28] [PASSED] 37 VFs
[15:15:28] [PASSED] 38 VFs
[15:15:28] [PASSED] 39 VFs
[15:15:28] [PASSED] 40 VFs
[15:15:28] [PASSED] 41 VFs
[15:15:28] [PASSED] 42 VFs
[15:15:28] [PASSED] 43 VFs
[15:15:28] [PASSED] 44 VFs
[15:15:28] [PASSED] 45 VFs
[15:15:28] [PASSED] 46 VFs
[15:15:28] [PASSED] 47 VFs
[15:15:28] [PASSED] 48 VFs
[15:15:28] [PASSED] 49 VFs
[15:15:28] [PASSED] 50 VFs
[15:15:28] [PASSED] 51 VFs
[15:15:28] [PASSED] 52 VFs
[15:15:28] [PASSED] 53 VFs
[15:15:28] [PASSED] 54 VFs
[15:15:28] [PASSED] 55 VFs
[15:15:28] [PASSED] 56 VFs
[15:15:28] [PASSED] 57 VFs
[15:15:28] [PASSED] 58 VFs
[15:15:28] [PASSED] 59 VFs
[15:15:28] [PASSED] 60 VFs
[15:15:28] [PASSED] 61 VFs
[15:15:28] [PASSED] 62 VFs
[15:15:28] [PASSED] 63 VFs
[15:15:28] ================= [PASSED] fair_doorbells ==================
[15:15:28] ======================== fair_ggtt  ========================
[15:15:28] [PASSED] 1 VF
[15:15:28] [PASSED] 2 VFs
[15:15:28] [PASSED] 3 VFs
[15:15:28] [PASSED] 4 VFs
[15:15:28] [PASSED] 5 VFs
[15:15:28] [PASSED] 6 VFs
[15:15:28] [PASSED] 7 VFs
[15:15:28] [PASSED] 8 VFs
[15:15:28] [PASSED] 9 VFs
[15:15:28] [PASSED] 10 VFs
[15:15:28] [PASSED] 11 VFs
[15:15:28] [PASSED] 12 VFs
[15:15:28] [PASSED] 13 VFs
[15:15:28] [PASSED] 14 VFs
[15:15:28] [PASSED] 15 VFs
[15:15:28] [PASSED] 16 VFs
[15:15:28] [PASSED] 17 VFs
[15:15:28] [PASSED] 18 VFs
[15:15:28] [PASSED] 19 VFs
[15:15:28] [PASSED] 20 VFs
[15:15:28] [PASSED] 21 VFs
[15:15:28] [PASSED] 22 VFs
[15:15:28] [PASSED] 23 VFs
[15:15:28] [PASSED] 24 VFs
[15:15:28] [PASSED] 25 VFs
[15:15:28] [PASSED] 26 VFs
[15:15:28] [PASSED] 27 VFs
[15:15:28] [PASSED] 28 VFs
[15:15:28] [PASSED] 29 VFs
[15:15:28] [PASSED] 30 VFs
[15:15:28] [PASSED] 31 VFs
[15:15:28] [PASSED] 32 VFs
[15:15:28] [PASSED] 33 VFs
[15:15:28] [PASSED] 34 VFs
[15:15:28] [PASSED] 35 VFs
[15:15:28] [PASSED] 36 VFs
[15:15:28] [PASSED] 37 VFs
[15:15:28] [PASSED] 38 VFs
[15:15:28] [PASSED] 39 VFs
[15:15:28] [PASSED] 40 VFs
[15:15:28] [PASSED] 41 VFs
[15:15:28] [PASSED] 42 VFs
[15:15:28] [PASSED] 43 VFs
[15:15:28] [PASSED] 44 VFs
[15:15:28] [PASSED] 45 VFs
[15:15:28] [PASSED] 46 VFs
[15:15:28] [PASSED] 47 VFs
[15:15:28] [PASSED] 48 VFs
[15:15:28] [PASSED] 49 VFs
[15:15:28] [PASSED] 50 VFs
[15:15:28] [PASSED] 51 VFs
[15:15:28] [PASSED] 52 VFs
[15:15:28] [PASSED] 53 VFs
[15:15:28] [PASSED] 54 VFs
[15:15:28] [PASSED] 55 VFs
[15:15:28] [PASSED] 56 VFs
[15:15:28] [PASSED] 57 VFs
[15:15:28] [PASSED] 58 VFs
[15:15:28] [PASSED] 59 VFs
[15:15:28] [PASSED] 60 VFs
[15:15:28] [PASSED] 61 VFs
[15:15:28] [PASSED] 62 VFs
[15:15:28] [PASSED] 63 VFs
[15:15:28] ==================== [PASSED] fair_ggtt ====================
[15:15:28] ======================== fair_vram  ========================
[15:15:28] [PASSED] 1 VF
[15:15:28] [PASSED] 2 VFs
[15:15:28] [PASSED] 3 VFs
[15:15:28] [PASSED] 4 VFs
[15:15:28] [PASSED] 5 VFs
[15:15:28] [PASSED] 6 VFs
[15:15:28] [PASSED] 7 VFs
[15:15:28] [PASSED] 8 VFs
[15:15:28] [PASSED] 9 VFs
[15:15:28] [PASSED] 10 VFs
[15:15:28] [PASSED] 11 VFs
[15:15:28] [PASSED] 12 VFs
[15:15:28] [PASSED] 13 VFs
[15:15:28] [PASSED] 14 VFs
[15:15:28] [PASSED] 15 VFs
[15:15:28] [PASSED] 16 VFs
[15:15:28] [PASSED] 17 VFs
[15:15:28] [PASSED] 18 VFs
[15:15:28] [PASSED] 19 VFs
[15:15:28] [PASSED] 20 VFs
[15:15:28] [PASSED] 21 VFs
[15:15:28] [PASSED] 22 VFs
[15:15:28] [PASSED] 23 VFs
[15:15:28] [PASSED] 24 VFs
[15:15:28] [PASSED] 25 VFs
[15:15:28] [PASSED] 26 VFs
[15:15:28] [PASSED] 27 VFs
[15:15:28] [PASSED] 28 VFs
[15:15:28] [PASSED] 29 VFs
[15:15:28] [PASSED] 30 VFs
[15:15:28] [PASSED] 31 VFs
[15:15:28] [PASSED] 32 VFs
[15:15:28] [PASSED] 33 VFs
[15:15:28] [PASSED] 34 VFs
[15:15:28] [PASSED] 35 VFs
[15:15:28] [PASSED] 36 VFs
[15:15:28] [PASSED] 37 VFs
[15:15:28] [PASSED] 38 VFs
[15:15:28] [PASSED] 39 VFs
[15:15:28] [PASSED] 40 VFs
[15:15:28] [PASSED] 41 VFs
[15:15:28] [PASSED] 42 VFs
[15:15:28] [PASSED] 43 VFs
[15:15:28] [PASSED] 44 VFs
[15:15:28] [PASSED] 45 VFs
[15:15:28] [PASSED] 46 VFs
[15:15:28] [PASSED] 47 VFs
[15:15:28] [PASSED] 48 VFs
[15:15:28] [PASSED] 49 VFs
[15:15:28] [PASSED] 50 VFs
[15:15:28] [PASSED] 51 VFs
[15:15:28] [PASSED] 52 VFs
[15:15:28] [PASSED] 53 VFs
[15:15:28] [PASSED] 54 VFs
[15:15:28] [PASSED] 55 VFs
[15:15:28] [PASSED] 56 VFs
[15:15:28] [PASSED] 57 VFs
[15:15:28] [PASSED] 58 VFs
[15:15:28] [PASSED] 59 VFs
[15:15:28] [PASSED] 60 VFs
[15:15:28] [PASSED] 61 VFs
[15:15:28] [PASSED] 62 VFs
[15:15:28] [PASSED] 63 VFs
[15:15:28] ==================== [PASSED] fair_vram ====================
[15:15:28] ================== [PASSED] pf_gt_config ===================
[15:15:28] ===================== lmtt (1 subtest) =====================
[15:15:28] ======================== test_ops  =========================
[15:15:28] [PASSED] 2-level
[15:15:28] [PASSED] multi-level
[15:15:28] ==================== [PASSED] test_ops =====================
[15:15:28] ====================== [PASSED] lmtt =======================
[15:15:28] ================= pf_service (11 subtests) =================
[15:15:28] [PASSED] pf_negotiate_any
[15:15:28] [PASSED] pf_negotiate_base_match
[15:15:28] [PASSED] pf_negotiate_base_newer
[15:15:28] [PASSED] pf_negotiate_base_next
[15:15:28] [SKIPPED] pf_negotiate_base_older
[15:15:28] [PASSED] pf_negotiate_base_prev
[15:15:28] [PASSED] pf_negotiate_latest_match
[15:15:28] [PASSED] pf_negotiate_latest_newer
[15:15:28] [PASSED] pf_negotiate_latest_next
[15:15:28] [SKIPPED] pf_negotiate_latest_older
[15:15:28] [SKIPPED] pf_negotiate_latest_prev
[15:15:28] =================== [PASSED] pf_service ====================
[15:15:28] ================== xe_eudebug (1 subtest) ==================
[15:15:28] =============== xe_eudebug_toggle_reg_kunit  ===============
[15:15:28] ========== [SKIPPED] xe_eudebug_toggle_reg_kunit ===========
[15:15:28] =================== [SKIPPED] xe_eudebug ===================
[15:15:28] ================= xe_guc_g2g (2 subtests) ==================
[15:15:28] ============== xe_live_guc_g2g_kunit_default  ==============
[15:15:28] ========= [SKIPPED] xe_live_guc_g2g_kunit_default ==========
[15:15:28] ============== xe_live_guc_g2g_kunit_allmem  ===============
[15:15:28] ========== [SKIPPED] xe_live_guc_g2g_kunit_allmem ==========
[15:15:28] =================== [SKIPPED] xe_guc_g2g ===================
[15:15:28] =================== xe_mocs (2 subtests) ===================
[15:15:28] ================ xe_live_mocs_kernel_kunit  ================
[15:15:28] =========== [SKIPPED] xe_live_mocs_kernel_kunit ============
[15:15:28] ================ xe_live_mocs_reset_kunit  =================
[15:15:28] ============ [SKIPPED] xe_live_mocs_reset_kunit ============
[15:15:28] ==================== [SKIPPED] xe_mocs =====================
[15:15:28] ================= xe_migrate (2 subtests) ==================
[15:15:28] ================= xe_migrate_sanity_kunit  =================
[15:15:28] ============ [SKIPPED] xe_migrate_sanity_kunit =============
[15:15:28] ================== xe_validate_ccs_kunit  ==================
[15:15:28] ============= [SKIPPED] xe_validate_ccs_kunit ==============
[15:15:28] =================== [SKIPPED] xe_migrate ===================
[15:15:28] ================== xe_dma_buf (1 subtest) ==================
[15:15:28] ==================== xe_dma_buf_kunit  =====================
[15:15:28] ================ [SKIPPED] xe_dma_buf_kunit ================
[15:15:28] =================== [SKIPPED] xe_dma_buf ===================
[15:15:28] ================= xe_bo_shrink (1 subtest) =================
[15:15:28] =================== xe_bo_shrink_kunit  ====================
[15:15:28] =============== [SKIPPED] xe_bo_shrink_kunit ===============
[15:15:28] ================== [SKIPPED] xe_bo_shrink ==================
[15:15:28] ==================== xe_bo (2 subtests) ====================
[15:15:28] ================== xe_ccs_migrate_kunit  ===================
[15:15:28] ============== [SKIPPED] xe_ccs_migrate_kunit ==============
[15:15:28] ==================== xe_bo_evict_kunit  ====================
[15:15:28] =============== [SKIPPED] xe_bo_evict_kunit ================
[15:15:28] ===================== [SKIPPED] xe_bo ======================
[15:15:28] ==================== args (13 subtests) ====================
[15:15:28] [PASSED] count_args_test
[15:15:28] [PASSED] call_args_example
[15:15:28] [PASSED] call_args_test
[15:15:28] [PASSED] drop_first_arg_example
[15:15:28] [PASSED] drop_first_arg_test
[15:15:28] [PASSED] first_arg_example
[15:15:28] [PASSED] first_arg_test
[15:15:28] [PASSED] last_arg_example
[15:15:28] [PASSED] last_arg_test
[15:15:28] [PASSED] pick_arg_example
[15:15:28] [PASSED] if_args_example
[15:15:28] [PASSED] if_args_test
[15:15:28] [PASSED] sep_comma_example
[15:15:28] ====================== [PASSED] args =======================
[15:15:28] =================== xe_pci (3 subtests) ====================
[15:15:28] ==================== check_graphics_ip  ====================
[15:15:28] [PASSED] 12.00 Xe_LP
[15:15:28] [PASSED] 12.10 Xe_LP+
[15:15:28] [PASSED] 12.55 Xe_HPG
[15:15:28] [PASSED] 12.60 Xe_HPC
[15:15:28] [PASSED] 12.70 Xe_LPG
[15:15:28] [PASSED] 12.71 Xe_LPG
[15:15:28] [PASSED] 12.74 Xe_LPG+
[15:15:28] [PASSED] 20.01 Xe2_HPG
[15:15:28] [PASSED] 20.02 Xe2_HPG
[15:15:28] [PASSED] 20.04 Xe2_LPG
[15:15:28] [PASSED] 30.00 Xe3_LPG
[15:15:28] [PASSED] 30.01 Xe3_LPG
[15:15:28] [PASSED] 30.03 Xe3_LPG
[15:15:28] [PASSED] 30.04 Xe3_LPG
[15:15:28] [PASSED] 30.05 Xe3_LPG
[15:15:28] [PASSED] 35.10 Xe3p_LPG
[15:15:28] [PASSED] 35.11 Xe3p_XPC
[15:15:28] ================ [PASSED] check_graphics_ip ================
[15:15:28] ===================== check_media_ip  ======================
[15:15:28] [PASSED] 12.00 Xe_M
[15:15:28] [PASSED] 12.55 Xe_HPM
[15:15:28] [PASSED] 13.00 Xe_LPM+
[15:15:28] [PASSED] 13.01 Xe2_HPM
[15:15:28] [PASSED] 20.00 Xe2_LPM
[15:15:28] [PASSED] 30.00 Xe3_LPM
[15:15:28] [PASSED] 30.02 Xe3_LPM
[15:15:28] [PASSED] 35.00 Xe3p_LPM
[15:15:28] [PASSED] 35.03 Xe3p_HPM
[15:15:28] ================= [PASSED] check_media_ip ==================
[15:15:28] =================== check_platform_desc  ===================
[15:15:28] [PASSED] 0x9A60 (TIGERLAKE)
[15:15:28] [PASSED] 0x9A68 (TIGERLAKE)
[15:15:28] [PASSED] 0x9A70 (TIGERLAKE)
[15:15:28] [PASSED] 0x9A40 (TIGERLAKE)
[15:15:28] [PASSED] 0x9A49 (TIGERLAKE)
[15:15:28] [PASSED] 0x9A59 (TIGERLAKE)
[15:15:28] [PASSED] 0x9A78 (TIGERLAKE)
[15:15:28] [PASSED] 0x9AC0 (TIGERLAKE)
[15:15:28] [PASSED] 0x9AC9 (TIGERLAKE)
[15:15:28] [PASSED] 0x9AD9 (TIGERLAKE)
[15:15:28] [PASSED] 0x9AF8 (TIGERLAKE)
[15:15:28] [PASSED] 0x4C80 (ROCKETLAKE)
[15:15:28] [PASSED] 0x4C8A (ROCKETLAKE)
[15:15:28] [PASSED] 0x4C8B (ROCKETLAKE)
[15:15:28] [PASSED] 0x4C8C (ROCKETLAKE)
[15:15:28] [PASSED] 0x4C90 (ROCKETLAKE)
[15:15:28] [PASSED] 0x4C9A (ROCKETLAKE)
[15:15:28] [PASSED] 0x4680 (ALDERLAKE_S)
[15:15:28] [PASSED] 0x4682 (ALDERLAKE_S)
[15:15:28] [PASSED] 0x4688 (ALDERLAKE_S)
[15:15:28] [PASSED] 0x468A (ALDERLAKE_S)
[15:15:28] [PASSED] 0x468B (ALDERLAKE_S)
[15:15:28] [PASSED] 0x4690 (ALDERLAKE_S)
[15:15:28] [PASSED] 0x4692 (ALDERLAKE_S)
[15:15:28] [PASSED] 0x4693 (ALDERLAKE_S)
[15:15:28] [PASSED] 0x46A0 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46A1 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46A2 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46A3 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46A6 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46A8 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46AA (ALDERLAKE_P)
[15:15:28] [PASSED] 0x462A (ALDERLAKE_P)
[15:15:28] [PASSED] 0x4626 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x4628 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46B0 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46B1 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46B2 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46B3 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46C0 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46C1 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46C2 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46C3 (ALDERLAKE_P)
[15:15:28] [PASSED] 0x46D0 (ALDERLAKE_N)
[15:15:28] [PASSED] 0x46D1 (ALDERLAKE_N)
[15:15:28] [PASSED] 0x46D2 (ALDERLAKE_N)
[15:15:28] [PASSED] 0x46D3 (ALDERLAKE_N)
[15:15:28] [PASSED] 0x46D4 (ALDERLAKE_N)
[15:15:28] [PASSED] 0xA721 (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7A1 (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7A9 (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7AC (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7AD (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA720 (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7A0 (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7A8 (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7AA (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA7AB (ALDERLAKE_P)
[15:15:28] [PASSED] 0xA780 (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA781 (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA782 (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA783 (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA788 (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA789 (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA78A (ALDERLAKE_S)
[15:15:28] [PASSED] 0xA78B (ALDERLAKE_S)
[15:15:28] [PASSED] 0x4905 (DG1)
[15:15:28] [PASSED] 0x4906 (DG1)
[15:15:28] [PASSED] 0x4907 (DG1)
[15:15:28] [PASSED] 0x4908 (DG1)
[15:15:28] [PASSED] 0x4909 (DG1)
[15:15:28] [PASSED] 0x56C0 (DG2)
[15:15:28] [PASSED] 0x56C2 (DG2)
[15:15:28] [PASSED] 0x56C1 (DG2)
[15:15:28] [PASSED] 0x7D51 (METEORLAKE)
[15:15:28] [PASSED] 0x7DD1 (METEORLAKE)
[15:15:28] [PASSED] 0x7D41 (METEORLAKE)
[15:15:28] [PASSED] 0x7D67 (METEORLAKE)
[15:15:28] [PASSED] 0xB640 (METEORLAKE)
[15:15:28] [PASSED] 0x56A0 (DG2)
[15:15:28] [PASSED] 0x56A1 (DG2)
[15:15:28] [PASSED] 0x56A2 (DG2)
[15:15:28] [PASSED] 0x56BE (DG2)
[15:15:28] [PASSED] 0x56BF (DG2)
[15:15:28] [PASSED] 0x5690 (DG2)
[15:15:28] [PASSED] 0x5691 (DG2)
[15:15:28] [PASSED] 0x5692 (DG2)
[15:15:28] [PASSED] 0x56A5 (DG2)
[15:15:28] [PASSED] 0x56A6 (DG2)
[15:15:28] [PASSED] 0x56B0 (DG2)
[15:15:28] [PASSED] 0x56B1 (DG2)
[15:15:28] [PASSED] 0x56BA (DG2)
[15:15:28] [PASSED] 0x56BB (DG2)
[15:15:28] [PASSED] 0x56BC (DG2)
[15:15:28] [PASSED] 0x56BD (DG2)
[15:15:28] [PASSED] 0x5693 (DG2)
[15:15:28] [PASSED] 0x5694 (DG2)
[15:15:28] [PASSED] 0x5695 (DG2)
[15:15:28] [PASSED] 0x56A3 (DG2)
[15:15:28] [PASSED] 0x56A4 (DG2)
[15:15:28] [PASSED] 0x56B2 (DG2)
[15:15:28] [PASSED] 0x56B3 (DG2)
[15:15:28] [PASSED] 0x5696 (DG2)
[15:15:28] [PASSED] 0x5697 (DG2)
[15:15:28] [PASSED] 0xB69 (PVC)
[15:15:28] [PASSED] 0xB6E (PVC)
[15:15:28] [PASSED] 0xBD4 (PVC)
[15:15:28] [PASSED] 0xBD5 (PVC)
[15:15:28] [PASSED] 0xBD6 (PVC)
[15:15:28] [PASSED] 0xBD7 (PVC)
[15:15:28] [PASSED] 0xBD8 (PVC)
[15:15:28] [PASSED] 0xBD9 (PVC)
[15:15:28] [PASSED] 0xBDA (PVC)
[15:15:28] [PASSED] 0xBDB (PVC)
[15:15:28] [PASSED] 0xBE0 (PVC)
[15:15:28] [PASSED] 0xBE1 (PVC)
[15:15:28] [PASSED] 0xBE5 (PVC)
[15:15:28] [PASSED] 0x7D40 (METEORLAKE)
[15:15:28] [PASSED] 0x7D45 (METEORLAKE)
[15:15:28] [PASSED] 0x7D55 (METEORLAKE)
[15:15:28] [PASSED] 0x7D60 (METEORLAKE)
[15:15:28] [PASSED] 0x7DD5 (METEORLAKE)
[15:15:28] [PASSED] 0x6420 (LUNARLAKE)
[15:15:28] [PASSED] 0x64A0 (LUNARLAKE)
[15:15:28] [PASSED] 0x64B0 (LUNARLAKE)
[15:15:28] [PASSED] 0xE202 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE209 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE20B (BATTLEMAGE)
[15:15:28] [PASSED] 0xE20C (BATTLEMAGE)
[15:15:28] [PASSED] 0xE20D (BATTLEMAGE)
[15:15:28] [PASSED] 0xE210 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE211 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE212 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE216 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE220 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE221 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE222 (BATTLEMAGE)
[15:15:28] [PASSED] 0xE223 (BATTLEMAGE)
[15:15:28] [PASSED] 0xB080 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB081 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB082 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB083 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB084 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB085 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB086 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB087 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB08F (PANTHERLAKE)
[15:15:28] [PASSED] 0xB090 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB0A0 (PANTHERLAKE)
[15:15:28] [PASSED] 0xB0B0 (PANTHERLAKE)
[15:15:28] [PASSED] 0xFD80 (PANTHERLAKE)
[15:15:28] [PASSED] 0xFD81 (PANTHERLAKE)
[15:15:28] [PASSED] 0xD740 (NOVALAKE_S)
[15:15:28] [PASSED] 0xD741 (NOVALAKE_S)
[15:15:28] [PASSED] 0xD742 (NOVALAKE_S)
[15:15:28] [PASSED] 0xD743 (NOVALAKE_S)
[15:15:28] [PASSED] 0xD744 (NOVALAKE_S)
[15:15:28] [PASSED] 0xD745 (NOVALAKE_S)
[15:15:28] [PASSED] 0x674C (CRESCENTISLAND)
[15:15:28] [PASSED] 0xD750 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD751 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD752 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD753 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD754 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD755 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD756 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD757 (NOVALAKE_P)
[15:15:28] [PASSED] 0xD75F (NOVALAKE_P)
[15:15:28] =============== [PASSED] check_platform_desc ===============
[15:15:28] ===================== [PASSED] xe_pci ======================
[15:15:28] =================== xe_rtp (2 subtests) ====================
[15:15:28] =============== xe_rtp_process_to_sr_tests  ================
[15:15:28] [PASSED] coalesce-same-reg
[15:15:28] [PASSED] no-match-no-add
stty: 'standard input': Inappropriate ioctl for device
[15:15:28] [PASSED] match-or
[15:15:28] [PASSED] match-or-xfail
[15:15:28] [PASSED] no-match-no-add-multiple-rules
[15:15:28] [PASSED] two-regs-two-entries
[15:15:28] [PASSED] clr-one-set-other
[15:15:28] [PASSED] set-field
[15:15:28] [PASSED] conflict-duplicate
[15:15:28] [PASSED] conflict-not-disjoint
[15:15:28] [PASSED] conflict-reg-type
[15:15:28] =========== [PASSED] xe_rtp_process_to_sr_tests ============
[15:15:28] ================== xe_rtp_process_tests  ===================
[15:15:28] [PASSED] active1
[15:15:28] [PASSED] active2
[15:15:28] [PASSED] active-inactive
[15:15:28] [PASSED] inactive-active
[15:15:28] [PASSED] inactive-1st_or_active-inactive
[15:15:28] [PASSED] inactive-2nd_or_active-inactive
[15:15:28] [PASSED] inactive-last_or_active-inactive
[15:15:28] [PASSED] inactive-no_or_active-inactive
[15:15:28] ============== [PASSED] xe_rtp_process_tests ===============
[15:15:28] ===================== [PASSED] xe_rtp ======================
[15:15:28] ==================== xe_wa (1 subtest) =====================
[15:15:28] ======================== xe_wa_gt  =========================
[15:15:28] [PASSED] TIGERLAKE B0
[15:15:28] [PASSED] DG1 A0
[15:15:28] [PASSED] DG1 B0
[15:15:28] [PASSED] ALDERLAKE_S A0
[15:15:28] [PASSED] ALDERLAKE_S B0
[15:15:28] [PASSED] ALDERLAKE_S C0
[15:15:28] [PASSED] ALDERLAKE_S D0
[15:15:28] [PASSED] ALDERLAKE_P A0
[15:15:28] [PASSED] ALDERLAKE_P B0
[15:15:28] [PASSED] ALDERLAKE_P C0
[15:15:28] [PASSED] ALDERLAKE_S RPLS D0
[15:15:28] [PASSED] ALDERLAKE_P RPLU E0
[15:15:28] [PASSED] DG2 G10 C0
[15:15:28] [PASSED] DG2 G11 B1
[15:15:28] [PASSED] DG2 G12 A1
[15:15:28] [PASSED] METEORLAKE 12.70(Xe_LPG) A0 13.00(Xe_LPM+) A0
[15:15:28] [PASSED] METEORLAKE 12.71(Xe_LPG) A0 13.00(Xe_LPM+) A0
[15:15:28] [PASSED] METEORLAKE 12.74(Xe_LPG+) A0 13.00(Xe_LPM+) A0
[15:15:28] [PASSED] LUNARLAKE 20.04(Xe2_LPG) A0 20.00(Xe2_LPM) A0
[15:15:28] [PASSED] LUNARLAKE 20.04(Xe2_LPG) B0 20.00(Xe2_LPM) A0
[15:15:28] [PASSED] BATTLEMAGE 20.01(Xe2_HPG) A0 13.01(Xe2_HPM) A1
[15:15:28] [PASSED] PANTHERLAKE 30.00(Xe3_LPG) A0 30.00(Xe3_LPM) A0
[15:15:28] ==================== [PASSED] xe_wa_gt =====================
[15:15:28] ====================== [PASSED] xe_wa ======================
[15:15:28] ============================================================
[15:15:28] Testing complete. Ran 598 tests: passed: 579, skipped: 19
[15:15:28] Elapsed time: 35.633s total, 4.328s configuring, 30.685s building, 0.614s running

+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/tests/.kunitconfig
[15:15:28] Configuring KUnit Kernel ...
Regenerating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[15:15:30] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
[15:15:54] Starting KUnit Kernel (1/1)...
[15:15:54] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[15:15:54] ============ drm_test_pick_cmdline (2 subtests) ============
[15:15:54] [PASSED] drm_test_pick_cmdline_res_1920_1080_60
[15:15:54] =============== drm_test_pick_cmdline_named  ===============
[15:15:54] [PASSED] NTSC
[15:15:54] [PASSED] NTSC-J
[15:15:54] [PASSED] PAL
[15:15:54] [PASSED] PAL-M
[15:15:54] =========== [PASSED] drm_test_pick_cmdline_named ===========
[15:15:54] ============== [PASSED] drm_test_pick_cmdline ==============
[15:15:54] == drm_test_atomic_get_connector_for_encoder (1 subtest) ===
[15:15:54] [PASSED] drm_test_drm_atomic_get_connector_for_encoder
[15:15:54] ==== [PASSED] drm_test_atomic_get_connector_for_encoder ====
[15:15:54] =========== drm_validate_clone_mode (2 subtests) ===========
[15:15:54] ============== drm_test_check_in_clone_mode  ===============
[15:15:54] [PASSED] in_clone_mode
[15:15:54] [PASSED] not_in_clone_mode
[15:15:54] ========== [PASSED] drm_test_check_in_clone_mode ===========
[15:15:54] =============== drm_test_check_valid_clones  ===============
[15:15:54] [PASSED] not_in_clone_mode
[15:15:54] [PASSED] valid_clone
[15:15:54] [PASSED] invalid_clone
[15:15:54] =========== [PASSED] drm_test_check_valid_clones ===========
[15:15:54] ============= [PASSED] drm_validate_clone_mode =============
[15:15:54] ============= drm_validate_modeset (1 subtest) =============
[15:15:54] [PASSED] drm_test_check_connector_changed_modeset
[15:15:54] ============== [PASSED] drm_validate_modeset ===============
[15:15:54] ====== drm_test_bridge_get_current_state (2 subtests) ======
[15:15:54] [PASSED] drm_test_drm_bridge_get_current_state_atomic
[15:15:54] [PASSED] drm_test_drm_bridge_get_current_state_legacy
[15:15:54] ======== [PASSED] drm_test_bridge_get_current_state ========
[15:15:54] ====== drm_test_bridge_helper_reset_crtc (3 subtests) ======
[15:15:54] [PASSED] drm_test_drm_bridge_helper_reset_crtc_atomic
[15:15:54] [PASSED] drm_test_drm_bridge_helper_reset_crtc_atomic_disabled
[15:15:54] [PASSED] drm_test_drm_bridge_helper_reset_crtc_legacy
[15:15:54] ======== [PASSED] drm_test_bridge_helper_reset_crtc ========
[15:15:54] ============== drm_bridge_alloc (2 subtests) ===============
[15:15:54] [PASSED] drm_test_drm_bridge_alloc_basic
[15:15:54] [PASSED] drm_test_drm_bridge_alloc_get_put
[15:15:54] ================ [PASSED] drm_bridge_alloc =================
[15:15:54] ============= drm_cmdline_parser (40 subtests) =============
[15:15:54] [PASSED] drm_test_cmdline_force_d_only
[15:15:54] [PASSED] drm_test_cmdline_force_D_only_dvi
[15:15:54] [PASSED] drm_test_cmdline_force_D_only_hdmi
[15:15:54] [PASSED] drm_test_cmdline_force_D_only_not_digital
[15:15:54] [PASSED] drm_test_cmdline_force_e_only
[15:15:54] [PASSED] drm_test_cmdline_res
[15:15:54] [PASSED] drm_test_cmdline_res_vesa
[15:15:54] [PASSED] drm_test_cmdline_res_vesa_rblank
[15:15:54] [PASSED] drm_test_cmdline_res_rblank
[15:15:54] [PASSED] drm_test_cmdline_res_bpp
[15:15:54] [PASSED] drm_test_cmdline_res_refresh
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_interlaced
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_margins
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_force_off
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_force_on
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_force_on_analog
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_force_on_digital
[15:15:54] [PASSED] drm_test_cmdline_res_bpp_refresh_interlaced_margins_force_on
[15:15:54] [PASSED] drm_test_cmdline_res_margins_force_on
[15:15:54] [PASSED] drm_test_cmdline_res_vesa_margins
[15:15:54] [PASSED] drm_test_cmdline_name
[15:15:54] [PASSED] drm_test_cmdline_name_bpp
[15:15:54] [PASSED] drm_test_cmdline_name_option
[15:15:54] [PASSED] drm_test_cmdline_name_bpp_option
[15:15:54] [PASSED] drm_test_cmdline_rotate_0
[15:15:54] [PASSED] drm_test_cmdline_rotate_90
[15:15:54] [PASSED] drm_test_cmdline_rotate_180
[15:15:54] [PASSED] drm_test_cmdline_rotate_270
[15:15:54] [PASSED] drm_test_cmdline_hmirror
[15:15:54] [PASSED] drm_test_cmdline_vmirror
[15:15:54] [PASSED] drm_test_cmdline_margin_options
[15:15:54] [PASSED] drm_test_cmdline_multiple_options
[15:15:54] [PASSED] drm_test_cmdline_bpp_extra_and_option
[15:15:54] [PASSED] drm_test_cmdline_extra_and_option
[15:15:54] [PASSED] drm_test_cmdline_freestanding_options
[15:15:54] [PASSED] drm_test_cmdline_freestanding_force_e_and_options
[15:15:54] [PASSED] drm_test_cmdline_panel_orientation
[15:15:54] ================ drm_test_cmdline_invalid  =================
[15:15:54] [PASSED] margin_only
[15:15:54] [PASSED] interlace_only
[15:15:54] [PASSED] res_missing_x
[15:15:54] [PASSED] res_missing_y
[15:15:54] [PASSED] res_bad_y
[15:15:54] [PASSED] res_missing_y_bpp
[15:15:54] [PASSED] res_bad_bpp
[15:15:54] [PASSED] res_bad_refresh
[15:15:54] [PASSED] res_bpp_refresh_force_on_off
[15:15:54] [PASSED] res_invalid_mode
[15:15:54] [PASSED] res_bpp_wrong_place_mode
[15:15:54] [PASSED] name_bpp_refresh
[15:15:54] [PASSED] name_refresh
[15:15:54] [PASSED] name_refresh_wrong_mode
[15:15:54] [PASSED] name_refresh_invalid_mode
[15:15:54] [PASSED] rotate_multiple
[15:15:54] [PASSED] rotate_invalid_val
[15:15:54] [PASSED] rotate_truncated
[15:15:54] [PASSED] invalid_option
[15:15:54] [PASSED] invalid_tv_option
[15:15:54] [PASSED] truncated_tv_option
[15:15:54] ============ [PASSED] drm_test_cmdline_invalid =============
[15:15:54] =============== drm_test_cmdline_tv_options  ===============
[15:15:54] [PASSED] NTSC
[15:15:54] [PASSED] NTSC_443
[15:15:54] [PASSED] NTSC_J
[15:15:54] [PASSED] PAL
[15:15:54] [PASSED] PAL_M
[15:15:54] [PASSED] PAL_N
[15:15:54] [PASSED] SECAM
[15:15:54] [PASSED] MONO_525
[15:15:54] [PASSED] MONO_625
[15:15:54] =========== [PASSED] drm_test_cmdline_tv_options ===========
[15:15:54] =============== [PASSED] drm_cmdline_parser ================
[15:15:54] ========== drmm_connector_hdmi_init (20 subtests) ==========
[15:15:54] [PASSED] drm_test_connector_hdmi_init_valid
[15:15:54] [PASSED] drm_test_connector_hdmi_init_bpc_8
[15:15:54] [PASSED] drm_test_connector_hdmi_init_bpc_10
[15:15:54] [PASSED] drm_test_connector_hdmi_init_bpc_12
[15:15:54] [PASSED] drm_test_connector_hdmi_init_bpc_invalid
[15:15:54] [PASSED] drm_test_connector_hdmi_init_bpc_null
[15:15:54] [PASSED] drm_test_connector_hdmi_init_formats_empty
[15:15:54] [PASSED] drm_test_connector_hdmi_init_formats_no_rgb
[15:15:54] === drm_test_connector_hdmi_init_formats_yuv420_allowed  ===
[15:15:54] [PASSED] supported_formats=0x9 yuv420_allowed=1
[15:15:54] [PASSED] supported_formats=0x9 yuv420_allowed=0
[15:15:54] [PASSED] supported_formats=0x3 yuv420_allowed=1
[15:15:54] [PASSED] supported_formats=0x3 yuv420_allowed=0
[15:15:54] === [PASSED] drm_test_connector_hdmi_init_formats_yuv420_allowed ===
[15:15:54] [PASSED] drm_test_connector_hdmi_init_null_ddc
[15:15:54] [PASSED] drm_test_connector_hdmi_init_null_product
[15:15:54] [PASSED] drm_test_connector_hdmi_init_null_vendor
[15:15:54] [PASSED] drm_test_connector_hdmi_init_product_length_exact
[15:15:54] [PASSED] drm_test_connector_hdmi_init_product_length_too_long
[15:15:54] [PASSED] drm_test_connector_hdmi_init_product_valid
[15:15:54] [PASSED] drm_test_connector_hdmi_init_vendor_length_exact
[15:15:54] [PASSED] drm_test_connector_hdmi_init_vendor_length_too_long
[15:15:54] [PASSED] drm_test_connector_hdmi_init_vendor_valid
[15:15:54] ========= drm_test_connector_hdmi_init_type_valid  =========
[15:15:54] [PASSED] HDMI-A
[15:15:54] [PASSED] HDMI-B
[15:15:54] ===== [PASSED] drm_test_connector_hdmi_init_type_valid =====
[15:15:54] ======== drm_test_connector_hdmi_init_type_invalid  ========
[15:15:54] [PASSED] Unknown
[15:15:54] [PASSED] VGA
[15:15:54] [PASSED] DVI-I
[15:15:54] [PASSED] DVI-D
[15:15:54] [PASSED] DVI-A
[15:15:54] [PASSED] Composite
[15:15:54] [PASSED] SVIDEO
[15:15:54] [PASSED] LVDS
[15:15:54] [PASSED] Component
[15:15:54] [PASSED] DIN
[15:15:54] [PASSED] DP
[15:15:54] [PASSED] TV
[15:15:54] [PASSED] eDP
[15:15:54] [PASSED] Virtual
[15:15:54] [PASSED] DSI
[15:15:54] [PASSED] DPI
[15:15:54] [PASSED] Writeback
[15:15:54] [PASSED] SPI
[15:15:54] [PASSED] USB
[15:15:54] ==== [PASSED] drm_test_connector_hdmi_init_type_invalid ====
[15:15:54] ============ [PASSED] drmm_connector_hdmi_init =============
[15:15:54] ============= drmm_connector_init (3 subtests) =============
[15:15:54] [PASSED] drm_test_drmm_connector_init
[15:15:54] [PASSED] drm_test_drmm_connector_init_null_ddc
[15:15:54] ========= drm_test_drmm_connector_init_type_valid  =========
[15:15:54] [PASSED] Unknown
[15:15:54] [PASSED] VGA
[15:15:54] [PASSED] DVI-I
[15:15:54] [PASSED] DVI-D
[15:15:54] [PASSED] DVI-A
[15:15:54] [PASSED] Composite
[15:15:54] [PASSED] SVIDEO
[15:15:54] [PASSED] LVDS
[15:15:54] [PASSED] Component
[15:15:54] [PASSED] DIN
[15:15:54] [PASSED] DP
[15:15:54] [PASSED] HDMI-A
[15:15:54] [PASSED] HDMI-B
[15:15:54] [PASSED] TV
[15:15:54] [PASSED] eDP
[15:15:54] [PASSED] Virtual
[15:15:54] [PASSED] DSI
[15:15:54] [PASSED] DPI
[15:15:54] [PASSED] Writeback
[15:15:54] [PASSED] SPI
[15:15:54] [PASSED] USB
[15:15:54] ===== [PASSED] drm_test_drmm_connector_init_type_valid =====
[15:15:54] =============== [PASSED] drmm_connector_init ===============
[15:15:54] ========= drm_connector_dynamic_init (6 subtests) ==========
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_init
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_init_null_ddc
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_init_not_added
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_init_properties
[15:15:54] ===== drm_test_drm_connector_dynamic_init_type_valid  ======
[15:15:54] [PASSED] Unknown
[15:15:54] [PASSED] VGA
[15:15:54] [PASSED] DVI-I
[15:15:54] [PASSED] DVI-D
[15:15:54] [PASSED] DVI-A
[15:15:54] [PASSED] Composite
[15:15:54] [PASSED] SVIDEO
[15:15:54] [PASSED] LVDS
[15:15:54] [PASSED] Component
[15:15:54] [PASSED] DIN
[15:15:54] [PASSED] DP
[15:15:54] [PASSED] HDMI-A
[15:15:54] [PASSED] HDMI-B
[15:15:54] [PASSED] TV
[15:15:54] [PASSED] eDP
[15:15:54] [PASSED] Virtual
[15:15:54] [PASSED] DSI
[15:15:54] [PASSED] DPI
[15:15:54] [PASSED] Writeback
[15:15:54] [PASSED] SPI
[15:15:54] [PASSED] USB
[15:15:54] = [PASSED] drm_test_drm_connector_dynamic_init_type_valid ==
[15:15:54] ======== drm_test_drm_connector_dynamic_init_name  =========
[15:15:54] [PASSED] Unknown
[15:15:54] [PASSED] VGA
[15:15:54] [PASSED] DVI-I
[15:15:54] [PASSED] DVI-D
[15:15:54] [PASSED] DVI-A
[15:15:54] [PASSED] Composite
[15:15:54] [PASSED] SVIDEO
[15:15:54] [PASSED] LVDS
[15:15:54] [PASSED] Component
[15:15:54] [PASSED] DIN
[15:15:54] [PASSED] DP
[15:15:54] [PASSED] HDMI-A
[15:15:54] [PASSED] HDMI-B
[15:15:54] [PASSED] TV
[15:15:54] [PASSED] eDP
[15:15:54] [PASSED] Virtual
[15:15:54] [PASSED] DSI
[15:15:54] [PASSED] DPI
[15:15:54] [PASSED] Writeback
[15:15:54] [PASSED] SPI
[15:15:54] [PASSED] USB
[15:15:54] ==== [PASSED] drm_test_drm_connector_dynamic_init_name =====
[15:15:54] =========== [PASSED] drm_connector_dynamic_init ============
[15:15:54] ==== drm_connector_dynamic_register_early (4 subtests) =====
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_early_on_list
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_early_defer
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_early_no_init
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_early_no_mode_object
[15:15:54] ====== [PASSED] drm_connector_dynamic_register_early =======
[15:15:54] ======= drm_connector_dynamic_register (7 subtests) ========
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_on_list
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_no_defer
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_no_init
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_mode_object
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_sysfs
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_sysfs_name
[15:15:54] [PASSED] drm_test_drm_connector_dynamic_register_debugfs
[15:15:54] ========= [PASSED] drm_connector_dynamic_register ==========
[15:15:54] = drm_connector_attach_broadcast_rgb_property (2 subtests) =
[15:15:54] [PASSED] drm_test_drm_connector_attach_broadcast_rgb_property
[15:15:54] [PASSED] drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector
[15:15:54] === [PASSED] drm_connector_attach_broadcast_rgb_property ===
[15:15:54] ========== drm_get_tv_mode_from_name (2 subtests) ==========
[15:15:54] ========== drm_test_get_tv_mode_from_name_valid  ===========
[15:15:54] [PASSED] NTSC
[15:15:54] [PASSED] NTSC-443
[15:15:54] [PASSED] NTSC-J
[15:15:54] [PASSED] PAL
[15:15:54] [PASSED] PAL-M
[15:15:54] [PASSED] PAL-N
[15:15:54] [PASSED] SECAM
[15:15:54] [PASSED] Mono
[15:15:54] ====== [PASSED] drm_test_get_tv_mode_from_name_valid =======
[15:15:54] [PASSED] drm_test_get_tv_mode_from_name_truncated
[15:15:54] ============ [PASSED] drm_get_tv_mode_from_name ============
[15:15:54] = drm_test_connector_hdmi_compute_mode_clock (12 subtests) =
[15:15:54] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb
[15:15:54] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_10bpc
[15:15:54] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_10bpc_vic_1
[15:15:54] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc
[15:15:54] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_12bpc_vic_1
[15:15:54] [PASSED] drm_test_drm_hdmi_compute_mode_clock_rgb_double
[15:15:54] = drm_test_connector_hdmi_compute_mode_clock_yuv420_valid  =
[15:15:54] [PASSED] VIC 96
[15:15:54] [PASSED] VIC 97
[15:15:54] [PASSED] VIC 101
[15:15:54] [PASSED] VIC 102
[15:15:54] [PASSED] VIC 106
[15:15:54] [PASSED] VIC 107
[15:15:54] === [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv420_valid ===
[15:15:54] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv420_10_bpc
[15:15:54] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv420_12_bpc
[15:15:54] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv422_8_bpc
[15:15:54] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv422_10_bpc
[15:15:54] [PASSED] drm_test_connector_hdmi_compute_mode_clock_yuv422_12_bpc
[15:15:54] === [PASSED] drm_test_connector_hdmi_compute_mode_clock ====
[15:15:54] == drm_hdmi_connector_get_broadcast_rgb_name (2 subtests) ==
[15:15:54] === drm_test_drm_hdmi_connector_get_broadcast_rgb_name  ====
[15:15:54] [PASSED] Automatic
[15:15:54] [PASSED] Full
[15:15:54] [PASSED] Limited 16:235
[15:15:54] === [PASSED] drm_test_drm_hdmi_connector_get_broadcast_rgb_name ===
[15:15:54] [PASSED] drm_test_drm_hdmi_connector_get_broadcast_rgb_name_invalid
[15:15:54] ==== [PASSED] drm_hdmi_connector_get_broadcast_rgb_name ====
[15:15:54] == drm_hdmi_connector_get_output_format_name (2 subtests) ==
[15:15:54] === drm_test_drm_hdmi_connector_get_output_format_name  ====
[15:15:54] [PASSED] RGB
[15:15:54] [PASSED] YUV 4:2:0
[15:15:54] [PASSED] YUV 4:2:2
[15:15:54] [PASSED] YUV 4:4:4
[15:15:54] === [PASSED] drm_test_drm_hdmi_connector_get_output_format_name ===
[15:15:54] [PASSED] drm_test_drm_hdmi_connector_get_output_format_name_invalid
[15:15:54] ==== [PASSED] drm_hdmi_connector_get_output_format_name ====
[15:15:54] ============= drm_damage_helper (21 subtests) ==============
[15:15:54] [PASSED] drm_test_damage_iter_no_damage
[15:15:54] [PASSED] drm_test_damage_iter_no_damage_fractional_src
[15:15:54] [PASSED] drm_test_damage_iter_no_damage_src_moved
[15:15:54] [PASSED] drm_test_damage_iter_no_damage_fractional_src_moved
[15:15:54] [PASSED] drm_test_damage_iter_no_damage_not_visible
[15:15:54] [PASSED] drm_test_damage_iter_no_damage_no_crtc
[15:15:54] [PASSED] drm_test_damage_iter_no_damage_no_fb
[15:15:54] [PASSED] drm_test_damage_iter_simple_damage
[15:15:54] [PASSED] drm_test_damage_iter_single_damage
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_intersect_src
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_outside_src
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_fractional_src
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_intersect_fractional_src
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_outside_fractional_src
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_src_moved
[15:15:54] [PASSED] drm_test_damage_iter_single_damage_fractional_src_moved
[15:15:54] [PASSED] drm_test_damage_iter_damage
[15:15:54] [PASSED] drm_test_damage_iter_damage_one_intersect
[15:15:54] [PASSED] drm_test_damage_iter_damage_one_outside
[15:15:54] [PASSED] drm_test_damage_iter_damage_src_moved
[15:15:54] [PASSED] drm_test_damage_iter_damage_not_visible
[15:15:54] ================ [PASSED] drm_damage_helper ================
[15:15:54] ============== drm_dp_mst_helper (3 subtests) ==============
[15:15:54] ============== drm_test_dp_mst_calc_pbn_mode  ==============
[15:15:54] [PASSED] Clock 154000 BPP 30 DSC disabled
[15:15:54] [PASSED] Clock 234000 BPP 30 DSC disabled
[15:15:54] [PASSED] Clock 297000 BPP 24 DSC disabled
[15:15:54] [PASSED] Clock 332880 BPP 24 DSC enabled
[15:15:54] [PASSED] Clock 324540 BPP 24 DSC enabled
[15:15:54] ========== [PASSED] drm_test_dp_mst_calc_pbn_mode ==========
[15:15:54] ============== drm_test_dp_mst_calc_pbn_div  ===============
[15:15:54] [PASSED] Link rate 2000000 lane count 4
[15:15:54] [PASSED] Link rate 2000000 lane count 2
[15:15:54] [PASSED] Link rate 2000000 lane count 1
[15:15:54] [PASSED] Link rate 1350000 lane count 4
[15:15:54] [PASSED] Link rate 1350000 lane count 2
[15:15:54] [PASSED] Link rate 1350000 lane count 1
[15:15:54] [PASSED] Link rate 1000000 lane count 4
[15:15:54] [PASSED] Link rate 1000000 lane count 2
[15:15:54] [PASSED] Link rate 1000000 lane count 1
[15:15:54] [PASSED] Link rate 810000 lane count 4
[15:15:54] [PASSED] Link rate 810000 lane count 2
[15:15:54] [PASSED] Link rate 810000 lane count 1
[15:15:54] [PASSED] Link rate 540000 lane count 4
[15:15:54] [PASSED] Link rate 540000 lane count 2
[15:15:54] [PASSED] Link rate 540000 lane count 1
[15:15:54] [PASSED] Link rate 270000 lane count 4
[15:15:54] [PASSED] Link rate 270000 lane count 2
[15:15:54] [PASSED] Link rate 270000 lane count 1
[15:15:54] [PASSED] Link rate 162000 lane count 4
[15:15:54] [PASSED] Link rate 162000 lane count 2
[15:15:54] [PASSED] Link rate 162000 lane count 1
[15:15:54] ========== [PASSED] drm_test_dp_mst_calc_pbn_div ===========
[15:15:54] ========= drm_test_dp_mst_sideband_msg_req_decode  =========
[15:15:54] [PASSED] DP_ENUM_PATH_RESOURCES with port number
[15:15:54] [PASSED] DP_POWER_UP_PHY with port number
[15:15:54] [PASSED] DP_POWER_DOWN_PHY with port number
[15:15:54] [PASSED] DP_ALLOCATE_PAYLOAD with SDP stream sinks
[15:15:54] [PASSED] DP_ALLOCATE_PAYLOAD with port number
[15:15:54] [PASSED] DP_ALLOCATE_PAYLOAD with VCPI
[15:15:54] [PASSED] DP_ALLOCATE_PAYLOAD with PBN
[15:15:54] [PASSED] DP_QUERY_PAYLOAD with port number
[15:15:54] [PASSED] DP_QUERY_PAYLOAD with VCPI
[15:15:54] [PASSED] DP_REMOTE_DPCD_READ with port number
[15:15:54] [PASSED] DP_REMOTE_DPCD_READ with DPCD address
[15:15:54] [PASSED] DP_REMOTE_DPCD_READ with max number of bytes
[15:15:54] [PASSED] DP_REMOTE_DPCD_WRITE with port number
[15:15:54] [PASSED] DP_REMOTE_DPCD_WRITE with DPCD address
[15:15:54] [PASSED] DP_REMOTE_DPCD_WRITE with data array
[15:15:54] [PASSED] DP_REMOTE_I2C_READ with port number
[15:15:54] [PASSED] DP_REMOTE_I2C_READ with I2C device ID
[15:15:54] [PASSED] DP_REMOTE_I2C_READ with transactions array
[15:15:54] [PASSED] DP_REMOTE_I2C_WRITE with port number
[15:15:54] [PASSED] DP_REMOTE_I2C_WRITE with I2C device ID
[15:15:54] [PASSED] DP_REMOTE_I2C_WRITE with data array
[15:15:54] [PASSED] DP_QUERY_STREAM_ENC_STATUS with stream ID
[15:15:54] [PASSED] DP_QUERY_STREAM_ENC_STATUS with client ID
[15:15:54] [PASSED] DP_QUERY_STREAM_ENC_STATUS with stream event
[15:15:54] [PASSED] DP_QUERY_STREAM_ENC_STATUS with valid stream event
[15:15:54] [PASSED] DP_QUERY_STREAM_ENC_STATUS with stream behavior
[15:15:54] [PASSED] DP_QUERY_STREAM_ENC_STATUS with a valid stream behavior
[15:15:54] ===== [PASSED] drm_test_dp_mst_sideband_msg_req_decode =====
[15:15:54] ================ [PASSED] drm_dp_mst_helper ================
[15:15:54] ================== drm_exec (7 subtests) ===================
[15:15:54] [PASSED] sanitycheck
[15:15:54] [PASSED] test_lock
[15:15:54] [PASSED] test_lock_unlock
[15:15:54] [PASSED] test_duplicates
[15:15:54] [PASSED] test_prepare
[15:15:54] [PASSED] test_prepare_array
[15:15:54] [PASSED] test_multiple_loops
[15:15:54] ==================== [PASSED] drm_exec =====================
[15:15:54] =========== drm_format_helper_test (17 subtests) ===========
[15:15:54] ============== drm_test_fb_xrgb8888_to_gray8  ==============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ========== [PASSED] drm_test_fb_xrgb8888_to_gray8 ==========
[15:15:54] ============= drm_test_fb_xrgb8888_to_rgb332  ==============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ========= [PASSED] drm_test_fb_xrgb8888_to_rgb332 ==========
[15:15:54] ============= drm_test_fb_xrgb8888_to_rgb565  ==============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ========= [PASSED] drm_test_fb_xrgb8888_to_rgb565 ==========
[15:15:54] ============ drm_test_fb_xrgb8888_to_xrgb1555  =============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======== [PASSED] drm_test_fb_xrgb8888_to_xrgb1555 =========
[15:15:54] ============ drm_test_fb_xrgb8888_to_argb1555  =============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======== [PASSED] drm_test_fb_xrgb8888_to_argb1555 =========
[15:15:54] ============ drm_test_fb_xrgb8888_to_rgba5551  =============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======== [PASSED] drm_test_fb_xrgb8888_to_rgba5551 =========
[15:15:54] ============= drm_test_fb_xrgb8888_to_rgb888  ==============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ========= [PASSED] drm_test_fb_xrgb8888_to_rgb888 ==========
[15:15:54] ============= drm_test_fb_xrgb8888_to_bgr888  ==============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ========= [PASSED] drm_test_fb_xrgb8888_to_bgr888 ==========
[15:15:54] ============ drm_test_fb_xrgb8888_to_argb8888  =============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======== [PASSED] drm_test_fb_xrgb8888_to_argb8888 =========
[15:15:54] =========== drm_test_fb_xrgb8888_to_xrgb2101010  ===========
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======= [PASSED] drm_test_fb_xrgb8888_to_xrgb2101010 =======
[15:15:54] =========== drm_test_fb_xrgb8888_to_argb2101010  ===========
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======= [PASSED] drm_test_fb_xrgb8888_to_argb2101010 =======
[15:15:54] ============== drm_test_fb_xrgb8888_to_mono  ===============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ========== [PASSED] drm_test_fb_xrgb8888_to_mono ===========
[15:15:54] ==================== drm_test_fb_swab  =====================
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ================ [PASSED] drm_test_fb_swab =================
[15:15:54] ============ drm_test_fb_xrgb8888_to_xbgr8888  =============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======== [PASSED] drm_test_fb_xrgb8888_to_xbgr8888 =========
[15:15:54] ============ drm_test_fb_xrgb8888_to_abgr8888  =============
[15:15:54] [PASSED] single_pixel_source_buffer
[15:15:54] [PASSED] single_pixel_clip_rectangle
[15:15:54] [PASSED] well_known_colors
[15:15:54] [PASSED] destination_pitch
[15:15:54] ======== [PASSED] drm_test_fb_xrgb8888_to_abgr8888 =========
[15:15:54] ================= drm_test_fb_clip_offset  =================
[15:15:54] [PASSED] pass through
[15:15:54] [PASSED] horizontal offset
[15:15:54] [PASSED] vertical offset
[15:15:54] [PASSED] horizontal and vertical offset
[15:15:54] [PASSED] horizontal offset (custom pitch)
[15:15:54] [PASSED] vertical offset (custom pitch)
[15:15:54] [PASSED] horizontal and vertical offset (custom pitch)
[15:15:54] ============= [PASSED] drm_test_fb_clip_offset =============
[15:15:54] =================== drm_test_fb_memcpy  ====================
[15:15:54] [PASSED] single_pixel_source_buffer: XR24 little-endian (0x34325258)
[15:15:54] [PASSED] single_pixel_source_buffer: XRA8 little-endian (0x38415258)
[15:15:54] [PASSED] single_pixel_source_buffer: YU24 little-endian (0x34325559)
[15:15:54] [PASSED] single_pixel_clip_rectangle: XB24 little-endian (0x34324258)
[15:15:54] [PASSED] single_pixel_clip_rectangle: XRA8 little-endian (0x38415258)
[15:15:54] [PASSED] single_pixel_clip_rectangle: YU24 little-endian (0x34325559)
[15:15:54] [PASSED] well_known_colors: XB24 little-endian (0x34324258)
[15:15:54] [PASSED] well_known_colors: XRA8 little-endian (0x38415258)
[15:15:54] [PASSED] well_known_colors: YU24 little-endian (0x34325559)
[15:15:54] [PASSED] destination_pitch: XB24 little-endian (0x34324258)
[15:15:54] [PASSED] destination_pitch: XRA8 little-endian (0x38415258)
[15:15:54] [PASSED] destination_pitch: YU24 little-endian (0x34325559)
[15:15:54] =============== [PASSED] drm_test_fb_memcpy ================
[15:15:54] ============= [PASSED] drm_format_helper_test ==============
[15:15:54] ================= drm_format (18 subtests) =================
[15:15:54] [PASSED] drm_test_format_block_width_invalid
[15:15:54] [PASSED] drm_test_format_block_width_one_plane
[15:15:54] [PASSED] drm_test_format_block_width_two_plane
[15:15:54] [PASSED] drm_test_format_block_width_three_plane
[15:15:54] [PASSED] drm_test_format_block_width_tiled
[15:15:54] [PASSED] drm_test_format_block_height_invalid
[15:15:54] [PASSED] drm_test_format_block_height_one_plane
[15:15:54] [PASSED] drm_test_format_block_height_two_plane
[15:15:54] [PASSED] drm_test_format_block_height_three_plane
[15:15:54] [PASSED] drm_test_format_block_height_tiled
[15:15:54] [PASSED] drm_test_format_min_pitch_invalid
[15:15:54] [PASSED] drm_test_format_min_pitch_one_plane_8bpp
[15:15:54] [PASSED] drm_test_format_min_pitch_one_plane_16bpp
[15:15:54] [PASSED] drm_test_format_min_pitch_one_plane_24bpp
[15:15:54] [PASSED] drm_test_format_min_pitch_one_plane_32bpp
[15:15:54] [PASSED] drm_test_format_min_pitch_two_plane
[15:15:54] [PASSED] drm_test_format_min_pitch_three_plane_8bpp
[15:15:54] [PASSED] drm_test_format_min_pitch_tiled
[15:15:54] =================== [PASSED] drm_format ====================
[15:15:54] ============== drm_framebuffer (10 subtests) ===============
[15:15:54] ========== drm_test_framebuffer_check_src_coords  ==========
[15:15:54] [PASSED] Success: source fits into fb
[15:15:54] [PASSED] Fail: overflowing fb with x-axis coordinate
[15:15:54] [PASSED] Fail: overflowing fb with y-axis coordinate
[15:15:54] [PASSED] Fail: overflowing fb with source width
[15:15:54] [PASSED] Fail: overflowing fb with source height
[15:15:54] ====== [PASSED] drm_test_framebuffer_check_src_coords ======
[15:15:54] [PASSED] drm_test_framebuffer_cleanup
[15:15:54] =============== drm_test_framebuffer_create  ===============
[15:15:54] [PASSED] ABGR8888 normal sizes
[15:15:54] [PASSED] ABGR8888 max sizes
[15:15:54] [PASSED] ABGR8888 pitch greater than min required
[15:15:54] [PASSED] ABGR8888 pitch less than min required
[15:15:54] [PASSED] ABGR8888 Invalid width
[15:15:54] [PASSED] ABGR8888 Invalid buffer handle
[15:15:54] [PASSED] No pixel format
[15:15:54] [PASSED] ABGR8888 Width 0
[15:15:54] [PASSED] ABGR8888 Height 0
[15:15:54] [PASSED] ABGR8888 Out of bound height * pitch combination
[15:15:54] [PASSED] ABGR8888 Large buffer offset
[15:15:54] [PASSED] ABGR8888 Buffer offset for inexistent plane
[15:15:54] [PASSED] ABGR8888 Invalid flag
[15:15:54] [PASSED] ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers
[15:15:54] [PASSED] ABGR8888 Valid buffer modifier
[15:15:54] [PASSED] ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)
[15:15:54] [PASSED] ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] NV12 Normal sizes
[15:15:54] [PASSED] NV12 Max sizes
[15:15:54] [PASSED] NV12 Invalid pitch
[15:15:54] [PASSED] NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag
[15:15:54] [PASSED] NV12 different  modifier per-plane
[15:15:54] [PASSED] NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE
[15:15:54] [PASSED] NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] NV12 Modifier for inexistent plane
[15:15:54] [PASSED] NV12 Handle for inexistent plane
[15:15:54] [PASSED] NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] YVU420 DRM_MODE_FB_MODIFIERS set without modifier
[15:15:54] [PASSED] YVU420 Normal sizes
[15:15:54] [PASSED] YVU420 Max sizes
[15:15:54] [PASSED] YVU420 Invalid pitch
[15:15:54] [PASSED] YVU420 Different pitches
[15:15:54] [PASSED] YVU420 Different buffer offsets/pitches
[15:15:54] [PASSED] YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS
[15:15:54] [PASSED] YVU420 Valid modifier
[15:15:54] [PASSED] YVU420 Different modifiers per plane
[15:15:54] [PASSED] YVU420 Modifier for inexistent plane
[15:15:54] [PASSED] YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)
[15:15:54] [PASSED] X0L2 Normal sizes
[15:15:54] [PASSED] X0L2 Max sizes
[15:15:54] [PASSED] X0L2 Invalid pitch
[15:15:54] [PASSED] X0L2 Pitch greater than minimum required
[15:15:54] [PASSED] X0L2 Handle for inexistent plane
[15:15:54] [PASSED] X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set
[15:15:54] [PASSED] X0L2 Modifier without DRM_MODE_FB_MODIFIERS set
[15:15:54] [PASSED] X0L2 Valid modifier
[15:15:54] [PASSED] X0L2 Modifier for inexistent plane
[15:15:54] =========== [PASSED] drm_test_framebuffer_create ===========
[15:15:54] [PASSED] drm_test_framebuffer_free
[15:15:54] [PASSED] drm_test_framebuffer_init
[15:15:54] [PASSED] drm_test_framebuffer_init_bad_format
[15:15:54] [PASSED] drm_test_framebuffer_init_dev_mismatch
[15:15:54] [PASSED] drm_test_framebuffer_lookup
[15:15:54] [PASSED] drm_test_framebuffer_lookup_inexistent
[15:15:54] [PASSED] drm_test_framebuffer_modifiers_not_supported
[15:15:54] ================= [PASSED] drm_framebuffer =================
[15:15:54] ================ drm_gem_shmem (8 subtests) ================
[15:15:54] [PASSED] drm_gem_shmem_test_obj_create
[15:15:54] [PASSED] drm_gem_shmem_test_obj_create_private
[15:15:54] [PASSED] drm_gem_shmem_test_pin_pages
[15:15:54] [PASSED] drm_gem_shmem_test_vmap
[15:15:54] [PASSED] drm_gem_shmem_test_get_sg_table
[15:15:54] [PASSED] drm_gem_shmem_test_get_pages_sgt
[15:15:54] [PASSED] drm_gem_shmem_test_madvise
[15:15:54] [PASSED] drm_gem_shmem_test_purge
[15:15:54] ================== [PASSED] drm_gem_shmem ==================
[15:15:54] === drm_atomic_helper_connector_hdmi_check (27 subtests) ===
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_auto_cea_mode
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_auto_cea_mode_vic_1
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_full_cea_mode
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_full_cea_mode_vic_1
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_limited_cea_mode
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_limited_cea_mode_vic_1
[15:15:54] ====== drm_test_check_broadcast_rgb_cea_mode_yuv420  =======
[15:15:54] [PASSED] Automatic
[15:15:54] [PASSED] Full
[15:15:54] [PASSED] Limited 16:235
[15:15:54] == [PASSED] drm_test_check_broadcast_rgb_cea_mode_yuv420 ===
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_crtc_mode_changed
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_crtc_mode_not_changed
[15:15:54] [PASSED] drm_test_check_disable_connector
[15:15:54] [PASSED] drm_test_check_hdmi_funcs_reject_rate
[15:15:54] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_rgb
[15:15:54] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_yuv420
[15:15:54] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv422
[15:15:54] [PASSED] drm_test_check_max_tmds_rate_bpc_fallback_ignore_yuv420
[15:15:54] [PASSED] drm_test_check_driver_unsupported_fallback_yuv420
[15:15:54] [PASSED] drm_test_check_output_bpc_crtc_mode_changed
[15:15:54] [PASSED] drm_test_check_output_bpc_crtc_mode_not_changed
[15:15:54] [PASSED] drm_test_check_output_bpc_dvi
[15:15:54] [PASSED] drm_test_check_output_bpc_format_vic_1
[15:15:54] [PASSED] drm_test_check_output_bpc_format_display_8bpc_only
[15:15:54] [PASSED] drm_test_check_output_bpc_format_display_rgb_only
[15:15:54] [PASSED] drm_test_check_output_bpc_format_driver_8bpc_only
[15:15:54] [PASSED] drm_test_check_output_bpc_format_driver_rgb_only
[15:15:54] [PASSED] drm_test_check_tmds_char_rate_rgb_8bpc
[15:15:54] [PASSED] drm_test_check_tmds_char_rate_rgb_10bpc
[15:15:54] [PASSED] drm_test_check_tmds_char_rate_rgb_12bpc
[15:15:54] ===== [PASSED] drm_atomic_helper_connector_hdmi_check ======
[15:15:54] === drm_atomic_helper_connector_hdmi_reset (6 subtests) ====
[15:15:54] [PASSED] drm_test_check_broadcast_rgb_value
[15:15:54] [PASSED] drm_test_check_bpc_8_value
[15:15:54] [PASSED] drm_test_check_bpc_10_value
[15:15:54] [PASSED] drm_test_check_bpc_12_value
[15:15:54] [PASSED] drm_test_check_format_value
[15:15:54] [PASSED] drm_test_check_tmds_char_value
[15:15:54] ===== [PASSED] drm_atomic_helper_connector_hdmi_reset ======
[15:15:54] = drm_atomic_helper_connector_hdmi_mode_valid (4 subtests) =
[15:15:54] [PASSED] drm_test_check_mode_valid
[15:15:54] [PASSED] drm_test_check_mode_valid_reject
[15:15:54] [PASSED] drm_test_check_mode_valid_reject_rate
[15:15:54] [PASSED] drm_test_check_mode_valid_reject_max_clock
[15:15:54] === [PASSED] drm_atomic_helper_connector_hdmi_mode_valid ===
[15:15:54] = drm_atomic_helper_connector_hdmi_infoframes (5 subtests) =
[15:15:54] [PASSED] drm_test_check_infoframes
[15:15:54] [PASSED] drm_test_check_reject_avi_infoframe
[15:15:54] [PASSED] drm_test_check_reject_hdr_infoframe_bpc_8
[15:15:54] [PASSED] drm_test_check_reject_hdr_infoframe_bpc_10
[15:15:54] [PASSED] drm_test_check_reject_audio_infoframe
[15:15:54] === [PASSED] drm_atomic_helper_connector_hdmi_infoframes ===
[15:15:54] ================= drm_managed (2 subtests) =================
[15:15:54] [PASSED] drm_test_managed_release_action
[15:15:54] [PASSED] drm_test_managed_run_action
[15:15:54] =================== [PASSED] drm_managed ===================
[15:15:54] =================== drm_mm (6 subtests) ====================
[15:15:54] [PASSED] drm_test_mm_init
[15:15:54] [PASSED] drm_test_mm_debug
[15:15:54] [PASSED] drm_test_mm_align32
[15:15:54] [PASSED] drm_test_mm_align64
[15:15:54] [PASSED] drm_test_mm_lowest
[15:15:54] [PASSED] drm_test_mm_highest
[15:15:54] ===================== [PASSED] drm_mm ======================
[15:15:54] ============= drm_modes_analog_tv (5 subtests) =============
[15:15:54] [PASSED] drm_test_modes_analog_tv_mono_576i
[15:15:54] [PASSED] drm_test_modes_analog_tv_ntsc_480i
[15:15:54] [PASSED] drm_test_modes_analog_tv_ntsc_480i_inlined
[15:15:54] [PASSED] drm_test_modes_analog_tv_pal_576i
[15:15:54] [PASSED] drm_test_modes_analog_tv_pal_576i_inlined
[15:15:54] =============== [PASSED] drm_modes_analog_tv ===============
[15:15:54] ============== drm_plane_helper (2 subtests) ===============
[15:15:54] =============== drm_test_check_plane_state  ================
[15:15:54] [PASSED] clipping_simple
[15:15:54] [PASSED] clipping_rotate_reflect
[15:15:54] [PASSED] positioning_simple
[15:15:54] [PASSED] upscaling
[15:15:54] [PASSED] downscaling
[15:15:54] [PASSED] rounding1
[15:15:54] [PASSED] rounding2
[15:15:54] [PASSED] rounding3
[15:15:54] [PASSED] rounding4
[15:15:54] =========== [PASSED] drm_test_check_plane_state ============
[15:15:54] =========== drm_test_check_invalid_plane_state  ============
[15:15:54] [PASSED] positioning_invalid
[15:15:54] [PASSED] upscaling_invalid
[15:15:54] [PASSED] downscaling_invalid
[15:15:54] ======= [PASSED] drm_test_check_invalid_plane_state ========
[15:15:54] ================ [PASSED] drm_plane_helper =================
[15:15:54] ====== drm_connector_helper_tv_get_modes (1 subtest) =======
[15:15:54] ====== drm_test_connector_helper_tv_get_modes_check  =======
[15:15:54] [PASSED] None
[15:15:54] [PASSED] PAL
[15:15:54] [PASSED] NTSC
[15:15:54] [PASSED] Both, NTSC Default
[15:15:54] [PASSED] Both, PAL Default
[15:15:54] [PASSED] Both, NTSC Default, with PAL on command-line
[15:15:54] [PASSED] Both, PAL Default, with NTSC on command-line
[15:15:54] == [PASSED] drm_test_connector_helper_tv_get_modes_check ===
[15:15:54] ======== [PASSED] drm_connector_helper_tv_get_modes ========
[15:15:54] ================== drm_rect (9 subtests) ===================
[15:15:54] [PASSED] drm_test_rect_clip_scaled_div_by_zero
[15:15:54] [PASSED] drm_test_rect_clip_scaled_not_clipped
[15:15:54] [PASSED] drm_test_rect_clip_scaled_clipped
[15:15:54] [PASSED] drm_test_rect_clip_scaled_signed_vs_unsigned
[15:15:54] ================= drm_test_rect_intersect  =================
[15:15:54] [PASSED] top-left x bottom-right: 2x2+1+1 x 2x2+0+0
[15:15:54] [PASSED] top-right x bottom-left: 2x2+0+0 x 2x2+1-1
[15:15:54] [PASSED] bottom-left x top-right: 2x2+1-1 x 2x2+0+0
[15:15:54] [PASSED] bottom-right x top-left: 2x2+0+0 x 2x2+1+1
[15:15:54] [PASSED] right x left: 2x1+0+0 x 3x1+1+0
[15:15:54] [PASSED] left x right: 3x1+1+0 x 2x1+0+0
[15:15:54] [PASSED] up x bottom: 1x2+0+0 x 1x3+0-1
[15:15:54] [PASSED] bottom x up: 1x3+0-1 x 1x2+0+0
[15:15:54] [PASSED] touching corner: 1x1+0+0 x 2x2+1+1
[15:15:54] [PASSED] touching side: 1x1+0+0 x 1x1+1+0
[15:15:54] [PASSED] equal rects: 2x2+0+0 x 2x2+0+0
[15:15:54] [PASSED] inside another: 2x2+0+0 x 1x1+1+1
[15:15:54] [PASSED] far away: 1x1+0+0 x 1x1+3+6
[15:15:54] [PASSED] points intersecting: 0x0+5+10 x 0x0+5+10
[15:15:54] [PASSED] points not intersecting: 0x0+0+0 x 0x0+5+10
[15:15:54] ============= [PASSED] drm_test_rect_intersect =============
[15:15:54] ================ drm_test_rect_calc_hscale  ================
[15:15:54] [PASSED] normal use
[15:15:54] [PASSED] out of max range
[15:15:54] [PASSED] out of min range
[15:15:54] [PASSED] zero dst
[15:15:54] [PASSED] negative src
[15:15:54] [PASSED] negative dst
[15:15:54] ============ [PASSED] drm_test_rect_calc_hscale ============
[15:15:54] ================ drm_test_rect_calc_vscale  ================
[15:15:54] [PASSED] normal use
[15:15:54] [PASSED] out of max range
[15:15:54] [PASSED] out of min range
[15:15:54] [PASSED] zero dst
[15:15:54] [PASSED] negative src
[15:15:54] [PASSED] negative dst
stty: 'standard input': Inappropriate ioctl for device
[15:15:54] ============ [PASSED] drm_test_rect_calc_vscale ============
[15:15:54] ================== drm_test_rect_rotate  ===================
[15:15:54] [PASSED] reflect-x
[15:15:54] [PASSED] reflect-y
[15:15:54] [PASSED] rotate-0
[15:15:54] [PASSED] rotate-90
[15:15:54] [PASSED] rotate-180
[15:15:54] [PASSED] rotate-270
[15:15:54] ============== [PASSED] drm_test_rect_rotate ===============
[15:15:54] ================ drm_test_rect_rotate_inv  =================
[15:15:54] [PASSED] reflect-x
[15:15:54] [PASSED] reflect-y
[15:15:54] [PASSED] rotate-0
[15:15:54] [PASSED] rotate-90
[15:15:54] [PASSED] rotate-180
[15:15:54] [PASSED] rotate-270
[15:15:54] ============ [PASSED] drm_test_rect_rotate_inv =============
[15:15:54] ==================== [PASSED] drm_rect =====================
[15:15:54] ============ drm_sysfb_modeset_test (1 subtest) ============
[15:15:54] ============ drm_test_sysfb_build_fourcc_list  =============
[15:15:54] [PASSED] no native formats
[15:15:54] [PASSED] XRGB8888 as native format
[15:15:54] [PASSED] remove duplicates
[15:15:54] [PASSED] convert alpha formats
[15:15:54] [PASSED] random formats
[15:15:54] ======== [PASSED] drm_test_sysfb_build_fourcc_list =========
[15:15:54] ============= [PASSED] drm_sysfb_modeset_test ==============
[15:15:54] ================== drm_fixp (2 subtests) ===================
[15:15:54] [PASSED] drm_test_int2fixp
[15:15:54] [PASSED] drm_test_sm2fixp
[15:15:54] ==================== [PASSED] drm_fixp =====================
[15:15:54] ============================================================
[15:15:54] Testing complete. Ran 621 tests: passed: 621
[15:15:54] Elapsed time: 25.882s total, 1.624s configuring, 24.092s building, 0.134s running

+ /kernel/tools/testing/kunit/kunit.py run --kunitconfig /kernel/drivers/gpu/drm/ttm/tests/.kunitconfig
[15:15:54] Configuring KUnit Kernel ...
Regenerating .config ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
[15:15:56] Building KUnit Kernel ...
Populating config with:
$ make ARCH=um O=.kunit olddefconfig
Building with:
$ make all compile_commands.json scripts_gdb ARCH=um O=.kunit --jobs=48
[15:16:05] Starting KUnit Kernel (1/1)...
[15:16:05] ============================================================
Running tests with:
$ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt
[15:16:05] ================= ttm_device (5 subtests) ==================
[15:16:05] [PASSED] ttm_device_init_basic
[15:16:05] [PASSED] ttm_device_init_multiple
[15:16:05] [PASSED] ttm_device_fini_basic
[15:16:05] [PASSED] ttm_device_init_no_vma_man
[15:16:05] ================== ttm_device_init_pools  ==================
[15:16:05] [PASSED] No DMA allocations, no DMA32 required
[15:16:05] [PASSED] DMA allocations, DMA32 required
[15:16:05] [PASSED] No DMA allocations, DMA32 required
[15:16:05] [PASSED] DMA allocations, no DMA32 required
[15:16:05] ============== [PASSED] ttm_device_init_pools ==============
[15:16:05] =================== [PASSED] ttm_device ====================
[15:16:05] ================== ttm_pool (8 subtests) ===================
[15:16:05] ================== ttm_pool_alloc_basic  ===================
[15:16:05] [PASSED] One page
[15:16:05] [PASSED] More than one page
[15:16:05] [PASSED] Above the allocation limit
[15:16:05] [PASSED] One page, with coherent DMA mappings enabled
[15:16:05] [PASSED] Above the allocation limit, with coherent DMA mappings enabled
[15:16:05] ============== [PASSED] ttm_pool_alloc_basic ===============
[15:16:05] ============== ttm_pool_alloc_basic_dma_addr  ==============
[15:16:05] [PASSED] One page
[15:16:05] [PASSED] More than one page
[15:16:05] [PASSED] Above the allocation limit
[15:16:05] [PASSED] One page, with coherent DMA mappings enabled
[15:16:05] [PASSED] Above the allocation limit, with coherent DMA mappings enabled
[15:16:05] ========== [PASSED] ttm_pool_alloc_basic_dma_addr ==========
[15:16:05] [PASSED] ttm_pool_alloc_order_caching_match
[15:16:05] [PASSED] ttm_pool_alloc_caching_mismatch
[15:16:05] [PASSED] ttm_pool_alloc_order_mismatch
[15:16:05] [PASSED] ttm_pool_free_dma_alloc
[15:16:05] [PASSED] ttm_pool_free_no_dma_alloc
[15:16:05] [PASSED] ttm_pool_fini_basic
[15:16:05] ==================== [PASSED] ttm_pool =====================
[15:16:05] ================ ttm_resource (8 subtests) =================
[15:16:05] ================= ttm_resource_init_basic  =================
[15:16:05] [PASSED] Init resource in TTM_PL_SYSTEM
[15:16:05] [PASSED] Init resource in TTM_PL_VRAM
[15:16:05] [PASSED] Init resource in a private placement
[15:16:05] [PASSED] Init resource in TTM_PL_SYSTEM, set placement flags
[15:16:05] ============= [PASSED] ttm_resource_init_basic =============
[15:16:05] [PASSED] ttm_resource_init_pinned
[15:16:05] [PASSED] ttm_resource_fini_basic
[15:16:05] [PASSED] ttm_resource_manager_init_basic
[15:16:05] [PASSED] ttm_resource_manager_usage_basic
[15:16:05] [PASSED] ttm_resource_manager_set_used_basic
[15:16:05] [PASSED] ttm_sys_man_alloc_basic
[15:16:05] [PASSED] ttm_sys_man_free_basic
[15:16:05] ================== [PASSED] ttm_resource ===================
[15:16:05] =================== ttm_tt (15 subtests) ===================
[15:16:05] ==================== ttm_tt_init_basic  ====================
[15:16:05] [PASSED] Page-aligned size
[15:16:05] [PASSED] Extra pages requested
[15:16:05] ================ [PASSED] ttm_tt_init_basic ================
[15:16:05] [PASSED] ttm_tt_init_misaligned
[15:16:05] [PASSED] ttm_tt_fini_basic
[15:16:05] [PASSED] ttm_tt_fini_sg
[15:16:05] [PASSED] ttm_tt_fini_shmem
[15:16:05] [PASSED] ttm_tt_create_basic
[15:16:05] [PASSED] ttm_tt_create_invalid_bo_type
[15:16:05] [PASSED] ttm_tt_create_ttm_exists
[15:16:05] [PASSED] ttm_tt_create_failed
[15:16:05] [PASSED] ttm_tt_destroy_basic
[15:16:05] [PASSED] ttm_tt_populate_null_ttm
[15:16:05] [PASSED] ttm_tt_populate_populated_ttm
[15:16:05] [PASSED] ttm_tt_unpopulate_basic
[15:16:05] [PASSED] ttm_tt_unpopulate_empty_ttm
[15:16:05] [PASSED] ttm_tt_swapin_basic
[15:16:05] ===================== [PASSED] ttm_tt ======================
[15:16:05] =================== ttm_bo (14 subtests) ===================
[15:16:05] =========== ttm_bo_reserve_optimistic_no_ticket  ===========
[15:16:05] [PASSED] Cannot be interrupted and sleeps
[15:16:05] [PASSED] Cannot be interrupted, locks straight away
[15:16:05] [PASSED] Can be interrupted, sleeps
[15:16:05] ======= [PASSED] ttm_bo_reserve_optimistic_no_ticket =======
[15:16:05] [PASSED] ttm_bo_reserve_locked_no_sleep
[15:16:05] [PASSED] ttm_bo_reserve_no_wait_ticket
[15:16:06] [PASSED] ttm_bo_reserve_double_resv
[15:16:06] [PASSED] ttm_bo_reserve_interrupted
[15:16:06] [PASSED] ttm_bo_reserve_deadlock
[15:16:06] [PASSED] ttm_bo_unreserve_basic
[15:16:06] [PASSED] ttm_bo_unreserve_pinned
[15:16:06] [PASSED] ttm_bo_unreserve_bulk
[15:16:06] [PASSED] ttm_bo_fini_basic
[15:16:06] [PASSED] ttm_bo_fini_shared_resv
[15:16:06] [PASSED] ttm_bo_pin_basic
[15:16:06] [PASSED] ttm_bo_pin_unpin_resource
[15:16:06] [PASSED] ttm_bo_multiple_pin_one_unpin
[15:16:06] ===================== [PASSED] ttm_bo ======================
[15:16:06] ============== ttm_bo_validate (21 subtests) ===============
[15:16:06] ============== ttm_bo_init_reserved_sys_man  ===============
[15:16:06] [PASSED] Buffer object for userspace
[15:16:06] [PASSED] Kernel buffer object
[15:16:06] [PASSED] Shared buffer object
[15:16:06] ========== [PASSED] ttm_bo_init_reserved_sys_man ===========
[15:16:06] ============== ttm_bo_init_reserved_mock_man  ==============
[15:16:06] [PASSED] Buffer object for userspace
[15:16:06] [PASSED] Kernel buffer object
[15:16:06] [PASSED] Shared buffer object
[15:16:06] ========== [PASSED] ttm_bo_init_reserved_mock_man ==========
[15:16:06] [PASSED] ttm_bo_init_reserved_resv
[15:16:06] ================== ttm_bo_validate_basic  ==================
[15:16:06] [PASSED] Buffer object for userspace
[15:16:06] [PASSED] Kernel buffer object
[15:16:06] [PASSED] Shared buffer object
[15:16:06] ============== [PASSED] ttm_bo_validate_basic ==============
[15:16:06] [PASSED] ttm_bo_validate_invalid_placement
[15:16:06] ============= ttm_bo_validate_same_placement  ==============
[15:16:06] [PASSED] System manager
[15:16:06] [PASSED] VRAM manager
[15:16:06] ========= [PASSED] ttm_bo_validate_same_placement ==========
[15:16:06] [PASSED] ttm_bo_validate_failed_alloc
[15:16:06] [PASSED] ttm_bo_validate_pinned
[15:16:06] [PASSED] ttm_bo_validate_busy_placement
[15:16:06] ================ ttm_bo_validate_multihop  =================
[15:16:06] [PASSED] Buffer object for userspace
[15:16:06] [PASSED] Kernel buffer object
[15:16:06] [PASSED] Shared buffer object
[15:16:06] ============ [PASSED] ttm_bo_validate_multihop =============
[15:16:06] ========== ttm_bo_validate_no_placement_signaled  ==========
[15:16:06] [PASSED] Buffer object in system domain, no page vector
[15:16:06] [PASSED] Buffer object in system domain with an existing page vector
[15:16:06] ====== [PASSED] ttm_bo_validate_no_placement_signaled ======
[15:16:06] ======== ttm_bo_validate_no_placement_not_signaled  ========
[15:16:06] [PASSED] Buffer object for userspace
[15:16:06] [PASSED] Kernel buffer object
[15:16:06] [PASSED] Shared buffer object
[15:16:06] ==== [PASSED] ttm_bo_validate_no_placement_not_signaled ====
[15:16:06] [PASSED] ttm_bo_validate_move_fence_signaled
[15:16:06] ========= ttm_bo_validate_move_fence_not_signaled  =========
[15:16:06] [PASSED] Waits for GPU
[15:16:06] [PASSED] Tries to lock straight away
[15:16:06] ===== [PASSED] ttm_bo_validate_move_fence_not_signaled =====
[15:16:06] [PASSED] ttm_bo_validate_happy_evict
[15:16:06] [PASSED] ttm_bo_validate_all_pinned_evict
[15:16:06] [PASSED] ttm_bo_validate_allowed_only_evict
[15:16:06] [PASSED] ttm_bo_validate_deleted_evict
[15:16:06] [PASSED] ttm_bo_validate_busy_domain_evict
[15:16:06] [PASSED] ttm_bo_validate_evict_gutting
[15:16:06] [PASSED] ttm_bo_validate_recrusive_evict
stty: 'standard input': Inappropriate ioctl for device
[15:16:06] ================= [PASSED] ttm_bo_validate =================
[15:16:06] ============================================================
[15:16:06] Testing complete. Ran 101 tests: passed: 101
[15:16:06] Elapsed time: 11.565s total, 1.750s configuring, 9.549s building, 0.235s running

+ cleanup
++ stat -c %u:%g /kernel
+ chown -R 1003:1003 /kernel



^ permalink raw reply	[flat|nested] 35+ messages in thread

* ✗ CI.checksparse: warning for Intel Xe GPU Debug Support (eudebug) v7
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (23 preceding siblings ...)
  2026-02-23 15:16 ` ✓ CI.KUnit: success " Patchwork
@ 2026-02-23 15:31 ` Patchwork
  2026-02-23 15:51 ` ✓ Xe.CI.BAT: success " Patchwork
  2026-02-24  8:42 ` ✗ Xe.CI.FULL: failure " Patchwork
  26 siblings, 0 replies; 35+ messages in thread
From: Patchwork @ 2026-02-23 15:31 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

== Series Details ==

Series: Intel Xe GPU Debug Support (eudebug) v7
URL   : https://patchwork.freedesktop.org/series/161979/
State : warning

== Summary ==

+ trap cleanup EXIT
+ KERNEL=/kernel
+ MT=/root/linux/maintainer-tools
+ git clone https://gitlab.freedesktop.org/drm/maintainer-tools /root/linux/maintainer-tools
Cloning into '/root/linux/maintainer-tools'...
warning: redirecting to https://gitlab.freedesktop.org/drm/maintainer-tools.git/
+ make -C /root/linux/maintainer-tools
make: Entering directory '/root/linux/maintainer-tools'
cc -O2 -g -Wextra -o remap-log remap-log.c
make: Leaving directory '/root/linux/maintainer-tools'
+ cd /kernel
+ git config --global --add safe.directory /kernel
+ /root/linux/maintainer-tools/dim sparse --fast 45a3045fc0dc46a893cb8bbe304afafd4120c904
Sparse version: 0.6.4 (Ubuntu: 0.6.4-4ubuntu3)
Fast mode used, each commit won't be checked separately.
+/kernel/Makefile:1204: C=1 specified, but sparse is not available or not up to date

+ cleanup
++ stat -c %u:%g /kernel
+ chown -R 1003:1003 /kernel



^ permalink raw reply	[flat|nested] 35+ messages in thread

* ✓ Xe.CI.BAT: success for Intel Xe GPU Debug Support (eudebug) v7
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (24 preceding siblings ...)
  2026-02-23 15:31 ` ✗ CI.checksparse: warning " Patchwork
@ 2026-02-23 15:51 ` Patchwork
  2026-02-24  8:42 ` ✗ Xe.CI.FULL: failure " Patchwork
  26 siblings, 0 replies; 35+ messages in thread
From: Patchwork @ 2026-02-23 15:51 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

[-- Attachment #1: Type: text/plain, Size: 1403 bytes --]

== Series Details ==

Series: Intel Xe GPU Debug Support (eudebug) v7
URL   : https://patchwork.freedesktop.org/series/161979/
State : success

== Summary ==

CI Bug Log - changes from xe-4590-616fe8160929511b0679615cda09751043490b18_BAT -> xe-pw-161979v1_BAT
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  

Participating hosts (14 -> 14)
------------------------------

  No changes in participating hosts

Known issues
------------

  Here are the changes found in xe-pw-161979v1_BAT that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@xe_waitfence@reltime:
    - bat-dg2-oem2:       [PASS][1] -> [FAIL][2] ([Intel XE#6520])
   [1]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/bat-dg2-oem2/igt@xe_waitfence@reltime.html
   [2]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/bat-dg2-oem2/igt@xe_waitfence@reltime.html

  
  [Intel XE#6520]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6520


Build changes
-------------

  * Linux: xe-4590-616fe8160929511b0679615cda09751043490b18 -> xe-pw-161979v1

  IGT_8765: 8765
  xe-4590-616fe8160929511b0679615cda09751043490b18: 616fe8160929511b0679615cda09751043490b18
  xe-pw-161979v1: 161979v1

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/index.html

[-- Attachment #2: Type: text/html, Size: 1968 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling
  2026-02-23 14:03 ` [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling Mika Kuoppala
@ 2026-02-23 18:41   ` Matthew Brost
  2026-02-27 22:11     ` Gwan-gyeong Mun
  2026-02-27 23:11   ` Gustavo Sousa
  1 sibling, 1 reply; 35+ messages in thread
From: Matthew Brost @ 2026-02-23 18:41 UTC (permalink / raw)
  To: Mika Kuoppala
  Cc: intel-xe, simona.vetter, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun

On Mon, Feb 23, 2026 at 04:03:17PM +0200, Mika Kuoppala wrote:
> From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> 
> The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
> access will halt the corresponding EUs. To solve this problem, enable
> EU pagefault handling functionality, which allows to unhalt pagefaulted
> eu threads and to EU debugger to get inform about the eu attentions state
> of EU threads during execution.
> 
> If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
> after handling the pagefault.
> 
> The pagefault handling is a mechanism that allows a stalled EU thread to
> enter SIP mode by installing a temporal null page to the page table entry
> where the pagefault happened.
> 
> A brief description of the page fault handling mechanism flow between KMD
> and the eu thread is as follows
> 
> (1) eu thread accesses unallocated address
> (2) pagefault happens and eu thread stalls
> (3) XE kmd set an force eu thread exception to allow the running eu thread
>     to enter SIP mode (kmd set ForceException / ForceExternalHalt bit of
>     TD_CTL register)
>     Not stalled (none-pagefaulted) eu threads enter SIP mode
> (4) XE kmd installs temporal null page to the pagetable entry of the
>     address where pagefault happened.
> (5) XE kmd replies pagefault successful message to GUC
> (6) stalled eu thread resumes as per pagefault condition has resolved
> (7) resumed eu thread enters SIP mode due to force exception set by (3)
> (8) adapted to consumer/produced pagefaults
> 
> As designed this feature to only work when eudbug is enabled, it should
> have no impact to regular recoverable pagefault code path.
> 
> v2: - pf->q holds the vm ref so drop it (Mika)
>     - streamline uapi (Mika)
>     - cleanup the pagefault through producer if (Mika)
> 
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> ---
>  drivers/gpu/drm/xe/xe_guc_pagefault.c   |  8 +++++++
>  drivers/gpu/drm/xe/xe_pagefault.c       | 31 ++++++++++++++++++++++++-
>  drivers/gpu/drm/xe/xe_pagefault_types.h |  9 +++++++
>  3 files changed, 47 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
> index d48f6ed103bb..6adf3bf73b1c 100644
> --- a/drivers/gpu/drm/xe/xe_guc_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
> @@ -8,6 +8,7 @@
>  #include "xe_guc_ct.h"
>  #include "xe_guc_pagefault.h"
>  #include "xe_pagefault.h"
> +#include "xe_eudebug_pagefault.h"
>  
>  static void guc_ack_fault(struct xe_pagefault *pf, int err)
>  {
> @@ -37,8 +38,15 @@ static void guc_ack_fault(struct xe_pagefault *pf, int err)
>  	xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
>  }
>  
> +static void guc_cleanup_fault(struct xe_pagefault *pf, int err)
> +{
> +	xe_eudebug_pagefault_service(pf);
> +	xe_eudebug_pagefault_destroy(pf, 0);
> +}
> +
>  static const struct xe_pagefault_ops guc_pagefault_ops = {
>  	.ack_fault = guc_ack_fault,
> +	.cleanup_fault = guc_cleanup_fault,
>  };
>  
>  /**
> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
> index 72f589fd2b64..9dcd854e99f9 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
> @@ -10,6 +10,7 @@
>  
>  #include "xe_bo.h"
>  #include "xe_device.h"
> +#include "xe_eudebug_pagefault.h"
>  #include "xe_gt_printk.h"
>  #include "xe_gt_types.h"
>  #include "xe_gt_stats.h"
> @@ -171,6 +172,8 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  	if (IS_ERR(vm))
>  		return PTR_ERR(vm);
>  
> +	xe_eudebug_pagefault_create(vm, pf);
> +
>  	/*
>  	 * TODO: Change to read lock? Using write lock for simplicity.
>  	 */
> @@ -184,9 +187,28 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);

I've mentioned this before - this fundamentally broken if SVM is
enabled as the VMA lookup will never fail given VMA tree is completely
populated in the SVM cases (i.e., when SVM is enabled the first thing
the UMD does is bind the entire CPU address space with a CPU mirror
VMA). What will fail in the SVM case is xe_svm_handle_pagefault will
likely return -ENOENT. UMDs from my understanding will enable SVM by
default so this likely needs to be rethought.

Matt

>  	if (!vma) {
>  		err = -EINVAL;
> -		goto unlock_vm;
> +		vma = xe_eudebug_create_vma(vm, pf);
> +		if (IS_ERR(vma)) {
> +			err = PTR_ERR(vma);
> +			vma = NULL;
> +		}
>  	}
>  
> +	if (vma) {
> +		/*
> +		 * When creating an instance of eudebug_pagefault, there was
> +		 * no vma containing the ppgtt address where the pagefault occurred,
> +		 * but when reacquiring vm->lock, there is.
> +		 * During not aquiring the vm->lock from this context,
> +		 * but vma corresponding to the address where the pagefault occurred
> +		 * in another context has allocated.
> +		 */
> +		err = 0;
> +	}
> +
> +	if (err)
> +		goto unlock_vm;
> +
>  	atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
>  
>  	if (xe_vma_is_cpu_addr_mirror(vma))
> @@ -198,6 +220,10 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  unlock_vm:
>  	if (!err)
>  		vm->usm.last_fault_vma = vma;
> +
> +	if (err)
> +		xe_eudebug_pagefault_destroy(pf, err);
> +
>  	up_write(&vm->lock);
>  	xe_vm_put(vm);
>  
> @@ -268,6 +294,9 @@ static void xe_pagefault_queue_work(struct work_struct *w)
>  
>  		pf.producer.ops->ack_fault(&pf, err);
>  
> +		if (pf.producer.ops->cleanup_fault)
> +			pf.producer.ops->cleanup_fault(&pf, err);
> +
>  		if (time_after(jiffies, threshold)) {
>  			queue_work(gt_to_xe(pf.gt)->usm.pf_wq, w);
>  			break;
> diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
> index 2bee858da597..9d2d29d35a4b 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
> +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
> @@ -43,6 +43,15 @@ struct xe_pagefault_ops {
>  	 * sends the result to the HW/FW interface.
>  	 */
>  	void (*ack_fault)(struct xe_pagefault *pf, int err);
> +
> +	/**
> +	 * @cleanup_fault: Cleanup for producer, if any
> +	 * @pf: Page fault
> +	 * @err: Error state of fault
> +	 *
> +	 * Page fault producer received cleanup request from consumer
> +	 */
> +	void (*cleanup_fault)(struct xe_pagefault *pf, int err);
>  };
>  
>  /**
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface
  2026-02-23 14:03 ` [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface Mika Kuoppala
@ 2026-02-23 19:08   ` Matthew Brost
  2026-02-27 22:10     ` Gwan-gyeong Mun
  0 siblings, 1 reply; 35+ messages in thread
From: Matthew Brost @ 2026-02-23 19:08 UTC (permalink / raw)
  To: Mika Kuoppala
  Cc: intel-xe, simona.vetter, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Jan Maślak

On Mon, Feb 23, 2026 at 04:03:16PM +0200, Mika Kuoppala wrote:
> From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> 

Not a complete review but a few quick comments below.

> The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
> access will halt the corresponding EUs. To solve this problem, introduce
> EU pagefault handling functionality, which allows to unhalt pagefaulted
> eu threads and to EU debugger to get inform about the eu attentions state
> of EU threads during execution.
> 
> If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
> after handling the pagefault. The pagefault eudebug event follows
> the newly added drm_xe_eudebug_event_pagefault type.
> When a pagefault occurs, it prevents to send the
> DRM_XE_EUDEBUG_EVENT_EU_ATTENTION event to the client during pagefault
> handling.
> 
> The page fault event delivery follows the below policy.
> (1) If EU Debugger discovery has completed and pagefaulted eu threads turn
>     on attention bit then pagefault handler delivers pagefault event
>     directly.
> (2) If a pagefault occurs during eu debugger discovery process, pagefault
>     handler queues a pagefault event and sends the queued event when
>     discovery has completed and pagefaulted eu threads turn on attention
>     bit.
> (3) If the pagefaulted eu thread struggles to turn on the attention bit
>     within the specified time, the attention scan worker sends a pagefault
>     event when it detects that the attention bit is turned on.
> 
> If multiple eu threads are running and a pagefault occurs due to accessing
> the same invalid address, send a single pagefault event
> (DRM_XE_EUDEBUG_EVENT_PAGEFAULT type) to the user debugger instead of a
> pagefault event for each of the multiple eu threads.
> If eu threads (other than the one that caused the page fault before) access
> the new invalid addresses, send a new pagefault event.
> 
> As the attention scan worker send the eu attention event whenever the
> attention bit is turned on, user debugger receives attenion event
> immediately after pagefault event.
> In this case, the page-fault event always precedes the attention event.
> 
> When the user debugger receives an attention event after a pagefault event,
> it can detect whether additional breakpoints or interrupts occur in
> addition to the existing pagefault by comparing the eu threads where the
> pagefault occurred with the eu threads where the attention bit is newly
> enabled.
> 
> v2: use only force exception (Joonas, Mika)
> v3: rebased on v4 (Mika)
> v4: streamline uapi, cleanups (Mika)
> v5: struct member documentation (Mika)
> v6: fault to fault_type (Mika)
> 
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Signed-off-by: Jan Maślak <jan.maslak@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> ---
>  drivers/gpu/drm/xe/Makefile               |   2 +-
>  drivers/gpu/drm/xe/xe_eudebug.c           | 100 ++++-
>  drivers/gpu/drm/xe/xe_eudebug.h           |   9 +
>  drivers/gpu/drm/xe/xe_eudebug_hw.c        |  15 +-
>  drivers/gpu/drm/xe/xe_eudebug_pagefault.c | 440 ++++++++++++++++++++++
>  drivers/gpu/drm/xe/xe_eudebug_pagefault.h |  47 +++
>  drivers/gpu/drm/xe/xe_eudebug_types.h     |  69 +++-
>  drivers/gpu/drm/xe/xe_pagefault_types.h   |   4 +
>  include/uapi/drm/xe_drm_eudebug.h         |  12 +
>  9 files changed, 676 insertions(+), 22 deletions(-)
>  create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.c
>  create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.h
> 
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> index 34db797ef8fc..b49fe7ae18e7 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -152,7 +152,7 @@ xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
>  xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
>  
>  # debugging shaders with gdb (eudebug) support
> -xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_gt_debug.o
> +xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_eudebug_pagefault.o xe_gt_debug.o
>  
>  # graphics hardware monitoring (HWMON) support
>  xe-$(CONFIG_HWMON) += xe_hwmon.o
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> index eae93c5f5e86..4b2f0dd9d234 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> @@ -17,12 +17,16 @@
>  #include "xe_eudebug.h"
>  #include "xe_eudebug_hw.h"
>  #include "xe_eudebug_types.h"
> +#include "xe_eudebug_pagefault.h"
>  #include "xe_eudebug_vm.h"
>  #include "xe_exec_queue.h"
> +#include "xe_force_wake.h"
>  #include "xe_gt.h"
>  #include "xe_hw_engine.h"
>  #include "xe_gt.h"
>  #include "xe_gt_debug.h"
> +#include "xe_gt_mcr.h"
> +#include "regs/xe_gt_regs.h"
>  #include "xe_macros.h"
>  #include "xe_pm.h"
>  #include "xe_sriov_pf.h"
> @@ -263,6 +267,7 @@ static void xe_eudebug_free(struct kref *ref)
>  	while (kfifo_get(&d->events.fifo, &event))
>  		kfree(event);
>  
> +	xe_eudebug_pagefault_fini(d);
>  	xe_eudebug_resources_destroy(d);
>  	mutex_destroy(&d->target.lock);
>  	XE_WARN_ON(d->target.xef);
> @@ -461,7 +466,7 @@ static int _xe_eudebug_disconnect(struct xe_eudebug *d,
>  	} \
>  })
>  
> -static struct xe_eudebug *
> +struct xe_eudebug *
>  xe_eudebug_get_nolock(struct xe_file *xef)
>  {
>  	struct xe_eudebug *d;
> @@ -1888,10 +1893,6 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
>  {
>  	int ret;
>  
> -	ret = xe_gt_eu_threads_needing_attention(gt);
> -	if (ret <= 0)
> -		return ret;
> -
>  	ret = xe_send_gt_attention(gt);
>  
>  	/* Discovery in progress, fake it */
> @@ -1901,6 +1902,65 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
>  	return ret;
>  }
>  
> +int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
> +				    struct xe_eudebug_pagefault *pf)
> +{
> +	struct drm_xe_eudebug_event_pagefault *ep;
> +	struct drm_xe_eudebug_event *event;
> +	int h_queue, h_lrc;
> +	u32 size = xe_gt_eu_attention_bitmap_size(pf->q->gt) * 3;
> +	u32 sz = struct_size(ep, bitmask, size);
> +	int ret;
> +
> +	XE_WARN_ON(pf->lrc_idx < 0 || pf->lrc_idx >= pf->q->width);
> +
> +	XE_WARN_ON(!xe_exec_queue_is_debuggable(pf->q));
> +
> +	h_queue = find_handle(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, pf->q);
> +	if (h_queue < 0)
> +		return h_queue;
> +
> +	h_lrc = find_handle(d, XE_EUDEBUG_RES_TYPE_LRC, pf->q->lrc[pf->lrc_idx]);
> +	if (h_lrc < 0)
> +		return h_lrc;
> +
> +	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_PAGEFAULT, 0,
> +					DRM_XE_EUDEBUG_EVENT_STATE_CHANGE, sz);
> +
> +	if (!event)
> +		return -ENOSPC;
> +
> +	ep = cast_event(ep, event);
> +	ep->exec_queue_handle = h_queue;
> +	ep->lrc_handle = h_lrc;
> +	ep->bitmask_size = size;
> +	ep->pagefault_address = pf->fault.addr;
> +
> +	memcpy(ep->bitmask, pf->attentions.before.att, pf->attentions.before.size);
> +	memcpy(ep->bitmask + pf->attentions.before.size,
> +	       pf->attentions.after.att, pf->attentions.after.size);
> +	memcpy(ep->bitmask + pf->attentions.before.size + pf->attentions.after.size,
> +	       pf->attentions.resolved.att, pf->attentions.resolved.size);
> +
> +	event->seqno = atomic_long_inc_return(&d->events.seqno);
> +
> +	ret = xe_eudebug_queue_event(d, event);
> +	if (ret)
> +		xe_eudebug_disconnect(d, ret);
> +
> +	return ret;
> +}
> +
> +static void handle_attention_fail(struct xe_gt *gt, int gt_id, int ret)
> +{
> +	/* TODO: error capture */
> +	drm_info(&gt_to_xe(gt)->drm,
> +		 "gt:%d unable to handle eu attention ret = %d\n",
> +		 gt_id, ret);
> +
> +	xe_gt_reset_async(gt);
> +}
> +
>  static void attention_poll_work(struct work_struct *work)
>  {
>  	struct xe_device *xe = container_of(work, typeof(*xe),
> @@ -1923,15 +1983,15 @@ static void attention_poll_work(struct work_struct *work)
>  			if (gt->info.type != XE_GT_TYPE_MAIN)
>  				continue;
>  
> -			ret = xe_eudebug_handle_gt_attention(gt);
> -			if (ret) {
> -				/* TODO: error capture */
> -				drm_info(&gt_to_xe(gt)->drm,
> -					 "gt:%d unable to handle eu attention ret=%d\n",
> -					 gt_id, ret);
> +			if (!xe_gt_eu_threads_needing_attention(gt))
> +				continue;
> +
> +			ret = xe_eudebug_handle_pagefaults(gt);
> +			if (!ret)
> +				ret = xe_eudebug_handle_gt_attention(gt);
>  
> -				xe_gt_reset_async(gt);
> -			}
> +			if (ret)
> +				handle_attention_fail(gt, gt_id, ret);
>  		}
>  
>  		xe_pm_runtime_put(xe);
> @@ -1940,12 +2000,12 @@ static void attention_poll_work(struct work_struct *work)
>  	schedule_delayed_work(&xe->eudebug.attention_dwork, delay);
>  }
>  
> -static void attention_poll_stop(struct xe_device *xe)
> +void xe_eudebug_attention_poll_stop(struct xe_device *xe)
>  {
>  	cancel_delayed_work_sync(&xe->eudebug.attention_dwork);
>  }
>  
> -static void attention_poll_start(struct xe_device *xe)
> +void xe_eudebug_attention_poll_start(struct xe_device *xe)
>  {
>  	mod_delayed_work(system_wq, &xe->eudebug.attention_dwork, 0);
>  }
> @@ -1988,6 +2048,8 @@ xe_eudebug_connect(struct xe_device *xe,
>  
>  	kref_init(&d->ref);
>  	mutex_init(&d->target.lock);
> +	mutex_init(&d->pf_lock);
> +	INIT_LIST_HEAD(&d->pagefaults);
>  	init_waitqueue_head(&d->events.write_done);
>  	init_waitqueue_head(&d->events.read_done);
>  	init_completion(&d->discovery);
> @@ -2019,7 +2081,7 @@ xe_eudebug_connect(struct xe_device *xe,
>  
>  	kref_get(&d->ref);
>  	queue_work(xe->eudebug.wq, &d->discovery_work);
> -	attention_poll_start(xe);
> +	xe_eudebug_attention_poll_start(xe);
>  
>  	eu_dbg(d, "connected session %lld", d->session);
>  
> @@ -2098,9 +2160,9 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
>  	mutex_unlock(&xe->eudebug.lock);
>  
>  	if (enable) {
> -		attention_poll_start(xe);
> +		xe_eudebug_attention_poll_start(xe);
>  	} else {
> -		attention_poll_stop(xe);
> +		xe_eudebug_attention_poll_stop(xe);
>  
>  		if (IS_SRIOV_PF(xe))
>  			xe_sriov_pf_end_lockdown(xe);
> @@ -2153,7 +2215,7 @@ static void xe_eudebug_fini(struct drm_device *dev, void *__unused)
>  
>  	xe_assert(xe, list_empty(&xe->eudebug.targets));
>  
> -	attention_poll_stop(xe);
> +	xe_eudebug_attention_poll_stop(xe);
>  }
>  
>  void xe_eudebug_init(struct xe_device *xe)
> diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
> index bd9fd7bf454f..34938e87be13 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug.h
> +++ b/drivers/gpu/drm/xe/xe_eudebug.h
> @@ -13,12 +13,14 @@ struct drm_file;
>  struct xe_debug_data;
>  struct xe_device;
>  struct xe_file;
> +struct xe_gt;
>  struct xe_vm;
>  struct xe_vma;
>  struct xe_vma_ops;
>  struct xe_exec_queue;
>  struct xe_user_fence;
>  struct xe_eudebug;
> +struct xe_eudebug_pagefault;
>  
>  #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
>  
> @@ -72,8 +74,15 @@ void xe_eudebug_ufence_init(struct xe_user_fence *ufence);
>  void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
>  bool xe_eudebug_ufence_track(struct xe_user_fence *ufence);
>  
> +struct xe_eudebug *xe_eudebug_get_nolock(struct xe_file *xef);
>  void xe_eudebug_put(struct xe_eudebug *d);
>  
> +int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
> +				    struct xe_eudebug_pagefault *pf);
> +
> +void xe_eudebug_attention_poll_stop(struct xe_device *xe);
> +void xe_eudebug_attention_poll_start(struct xe_device *xe);
> +
>  #else
>  
>  static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
> diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
> index 5365265a67b3..270f7abc82e9 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug_hw.c
> +++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
> @@ -322,6 +322,7 @@ static int do_eu_control(struct xe_eudebug *d,
>  	struct xe_device *xe = d->xe;
>  	u8 *bits = NULL;
>  	unsigned int hw_attn_size, attn_size;
> +	struct dma_fence *pf_fence;
>  	struct xe_exec_queue *q;
>  	struct xe_lrc *lrc;
>  	u64 seqno;
> @@ -376,8 +377,20 @@ static int do_eu_control(struct xe_eudebug *d,
>  		goto out_free;
>  	}
>  
> -	ret = -EINVAL;
>  	mutex_lock(&d->hw.lock);
> +	do {
> +		pf_fence = dma_fence_get(d->pf_fence);
> +		if (pf_fence) {
> +			mutex_unlock(&d->hw.lock);
> +			ret = dma_fence_wait(pf_fence, true);
> +			dma_fence_put(pf_fence);
> +			if (ret)
> +				goto out_free;
> +			mutex_lock(&d->hw.lock);
> +		}
> +	} while (pf_fence);
> +
> +	ret = -EINVAL;
>  
>  	switch (arg->cmd) {
>  	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL:
> diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.c b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
> new file mode 100644
> index 000000000000..edd368a7f6ae
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
> @@ -0,0 +1,440 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023-2025 Intel Corporation
> + */
> +
> +#include "xe_eudebug_pagefault.h"
> +
> +#include <linux/delay.h>
> +
> +#include "xe_exec_queue.h"
> +#include "xe_eudebug.h"
> +#include "xe_eudebug_hw.h"
> +#include "xe_force_wake.h"
> +#include "xe_gt_debug.h"
> +#include "xe_gt_mcr.h"
> +#include "regs/xe_gt_regs.h"
> +#include "xe_vm.h"
> +
> +static struct xe_gt *
> +pf_to_gt(struct xe_eudebug_pagefault *pf)
> +{
> +	return pf->q->gt;
> +}
> +
> +static void destroy_pagefault(struct xe_eudebug_pagefault *pf)
> +{
> +	xe_exec_queue_put(pf->q);
> +	kfree(pf);
> +}
> +
> +static int queue_pagefault(struct xe_eudebug_pagefault *pf)
> +{
> +	struct xe_eudebug *d;
> +
> +	d = xe_eudebug_get_nolock(pf->q->vm->xef);
> +	if (!d)
> +		return -EINVAL;
> +
> +	mutex_lock(&d->pf_lock);
> +	list_add_tail(&pf->link, &d->pagefaults);
> +	mutex_unlock(&d->pf_lock);
> +
> +	xe_eudebug_put(d);
> +
> +	return 0;
> +}
> +
> +static int send_pagefault(struct xe_eudebug_pagefault *pf,
> +			  bool from_attention_scan)
> +{
> +	struct xe_gt *gt = pf_to_gt(pf);
> +	struct xe_eudebug *d;
> +	struct xe_exec_queue *q;
> +	int ret, lrc_idx;
> +
> +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
> +	if (IS_ERR(q))
> +		return PTR_ERR(q);
> +
> +	if (!xe_exec_queue_is_debuggable(q)) {
> +		ret = -EPERM;
> +		goto out_exec_queue_put;
> +	}
> +
> +	d = xe_eudebug_get_nolock(q->vm->xef);
> +	if (!d) {
> +		ret = -ENOTCONN;
> +		goto out_exec_queue_put;
> +	}
> +
> +	if (pf->deferred_resolved) {
> +		xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
> +					 XE_GT_ATTENTION_TIMEOUT_MS);
> +
> +		if (!xe_eu_attentions_xor_count(&pf->attentions.after,
> +						&pf->attentions.resolved) &&
> +		    !from_attention_scan) {
> +			eu_dbg(d, "xe attentions not yet updated\n");
> +			ret = -EBUSY;
> +			goto out_eudebug_put;
> +		}
> +	}
> +
> +	ret = xe_eudebug_send_pagefault_event(d, pf);
> +
> +out_eudebug_put:
> +	xe_eudebug_put(d);
> +out_exec_queue_put:
> +	xe_exec_queue_put(q);
> +
> +	return ret;
> +}
> +
> +static const char *
> +pagefault_get_driver_name(struct dma_fence *dma_fence)
> +{
> +	return "xe";
> +}
> +
> +static const char *
> +pagefault_fence_get_timeline_name(struct dma_fence *dma_fence)
> +{
> +	return "eudebug_pagefault_fence";
> +}
> +
> +static const struct dma_fence_ops pagefault_fence_ops = {
> +	.get_driver_name = pagefault_get_driver_name,
> +	.get_timeline_name = pagefault_fence_get_timeline_name,
> +};
> +
> +struct pagefault_fence {
> +	struct dma_fence base;
> +	spinlock_t lock;
> +};
> +
> +static struct pagefault_fence *pagefault_fence_create(void)
> +{
> +	struct pagefault_fence *fence;
> +
> +	fence = kzalloc_obj(*fence, GFP_KERNEL);
> +	if (fence == NULL)
> +		return NULL;
> +
> +	spin_lock_init(&fence->lock);
> +	dma_fence_init(&fence->base, &pagefault_fence_ops, &fence->lock,
> +		       dma_fence_context_alloc(1), 1);
> +
> +	return fence;
> +}
> +
> +void
> +xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)

This function, as written, is basically a no from me given that
DRM_XE_EUDEBUG is enabled by default. It adds time complexity via
xe_vm_find_vma_by_addr(), which is O(log N) where N is the number of
VMAs.

Page faults are going to be heavily optimized since this is a critical
path. Anything less than O(1) here when no EU connection exists —
combined with DRM_XE_EUDEBUG being on — is likely to receive pushback
from me.

> +{
> +	struct pagefault_fence *pf_fence;
> +	struct xe_eudebug_pagefault *epf;
> +	struct xe_vma *vma;
> +	struct xe_gt *gt = pf->gt;
> +	struct xe_exec_queue *q;
> +	struct dma_fence *fence;
> +	struct xe_eudebug *d;
> +	unsigned int fw_ref;
> +	int lrc_idx;
> +	u32 td_ctl;
> +
> +	pf->consumer.epf = NULL;
> +
> +	down_read(&vm->lock);
> +	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
> +	up_read(&vm->lock);

See my comment in [1] — this doesn't work for SVM. This will need to be
rethought.

[1] https://patchwork.freedesktop.org/patch/706437/?series=161979&rev=1#comment_1299420

> +
> +	if (vma)
> +		return;
> +
> +	d = xe_eudebug_get_nolock(vm->xef);
> +	if (!d)
> +		return;
> +
> +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
> +	if (IS_ERR(q))
> +		goto err_put_eudebug;
> +
> +	if (XE_WARN_ON(q->vm != vm))
> +		goto err_put_exec_queue;
> +
> +	if (!xe_exec_queue_is_debuggable(q))
> +		goto err_put_exec_queue;
> +
> +	fw_ref = xe_force_wake_get(gt_to_fw(gt), q->hwe->domain);
> +	if (!fw_ref)
> +		goto err_put_exec_queue;
> +
> +	/*
> +	 * If there is no debug functionality (TD_CTL_GLOBAL_DEBUG_ENABLE, etc.),
> +	 * don't proceed pagefault routine for eu debugger.
> +	 */
> +	td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
> +	if (!td_ctl)
> +		goto err_put_fw;
> +
> +	epf = kzalloc_obj(*epf, GFP_KERNEL);
> +	if (!epf)
> +		goto err_put_fw;
> +
> +	xe_eudebug_attention_poll_stop(gt_to_xe(gt));
> +
> +	mutex_lock(&d->hw.lock);
> +	fence = dma_fence_get(d->pf_fence);
> +
> +	if (fence) {
> +		/*
> +		 * TODO: If the new incoming pagefaulted address is different
> +		 * from the pagefaulted address it is currently handling on the
> +		 * same ASID, it needs a routine to wait here and then do the
> +		 * following pagefault.
> +		 */
> +		dma_fence_put(fence);
> +		goto err_unlock_hw_lock;
> +	}
> +
> +	pf_fence = pagefault_fence_create();
> +	if (!pf_fence)
> +		goto err_unlock_hw_lock;
> +
> +	d->pf_fence = &pf_fence->base;
> +
> +	INIT_LIST_HEAD(&epf->link);
> +
> +	xe_gt_eu_attentions_read(gt, &epf->attentions.before, 0);
> +
> +	if (td_ctl & TD_CTL_FORCE_EXCEPTION)
> +		eu_warn(d, "force exception already set!");
> +
> +	/* Halt regardless of thread dependencies */
> +	while (!(td_ctl & TD_CTL_FORCE_EXCEPTION)) {
> +		xe_gt_mcr_multicast_write(gt, TD_CTL,
> +					  td_ctl | TD_CTL_FORCE_EXCEPTION);
> +		udelay(200);
> +		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
> +	}
> +
> +	xe_gt_eu_attentions_read(gt, &epf->attentions.after,
> +				 XE_GT_ATTENTION_TIMEOUT_MS);
> +
> +	mutex_unlock(&d->hw.lock);
> +
> +	/*
> +	 * xe_exec_queue_put() will be called from xe_eudebug_pagefault_destroy()
> +	 * or handle_pagefault()
> +	 */
> +	epf->q = q;
> +	epf->lrc_idx = lrc_idx;
> +	epf->fault.addr = pf->consumer.page_addr;
> +	epf->fault.type_level = pf->consumer.fault_type_level;
> +	epf->fault.access_type = pf->consumer.access_type;
> +
> +	pf->consumer.epf = epf;
> +
> +	xe_force_wake_put(gt_to_fw(gt), fw_ref);
> +	xe_eudebug_put(d);
> +
> +	return;
> +
> +err_unlock_hw_lock:
> +	mutex_unlock(&d->hw.lock);
> +	xe_eudebug_attention_poll_start(gt_to_xe(gt));
> +	kfree(epf);
> +err_put_fw:
> +	xe_force_wake_put(gt_to_fw(gt), fw_ref);
> +err_put_exec_queue:
> +	xe_exec_queue_put(q);
> +err_put_eudebug:
> +	xe_eudebug_put(d);
> +}
> +
> +struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
> +{
> +	struct xe_vma *vma = NULL;
> +
> +	if (!pf->consumer.epf)
> +		return NULL;
> +
> +	vma = xe_vm_create_null_vma(vm, pf->consumer.page_addr);
> +	if (IS_ERR(vma))
> +		return vma;
> +
> +	pf->consumer.epf->is_null = true;
> +
> +	return vma;
> +}
> +
> +static void
> +xe_eudebug_pagefault_process(struct xe_eudebug_pagefault *pf)
> +{
> +	struct xe_gt *gt = pf->q->gt;
> +
> +	xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
> +				 XE_GT_ATTENTION_TIMEOUT_MS);
> +
> +	if (!xe_eu_attentions_xor_count(&pf->attentions.after,
> +					&pf->attentions.resolved))
> +		pf->deferred_resolved = true;
> +}
> +
> +static void
> +_xe_eudebug_pagefault_destroy(struct xe_eudebug_pagefault *pf)
> +{
> +	struct xe_gt *gt = pf->q->gt;
> +	struct xe_vm *vm = pf->q->vm;
> +	struct xe_eudebug *d;
> +	unsigned int fw_ref;
> +	u32 td_ctl;
> +	bool queued, try_send;
> +	int ret;
> +
> +	fw_ref = xe_force_wake_get(gt_to_fw(gt), pf->q->hwe->domain);
> +	if (!fw_ref) {
> +		struct xe_device *xe = gt_to_xe(gt);
> +
> +		drm_warn(&xe->drm, "Forcewake fail: Can not recover TD_CTL");
> +	} else {
> +		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
> +		xe_gt_mcr_multicast_write(gt, TD_CTL, td_ctl &
> +					  ~(TD_CTL_FORCE_EXCEPTION));
> +		xe_force_wake_put(gt_to_fw(gt), fw_ref);
> +	}
> +
> +	queued = false;
> +	try_send = pf->is_null;
> +	if (try_send) {
> +		ret = send_pagefault(pf, false);
> +
> +		/*
> +		 * if debugger discovery is not completed or resolved attentions are not
> +		 * updated, then queue pagefault
> +		 */
> +		if (ret == -EBUSY) {
> +			ret = queue_pagefault(pf);
> +			if (!ret)
> +				queued = true;
> +		}
> +	}
> +
> +	d = xe_eudebug_get_nolock(vm->xef);
> +	if (d) {
> +		struct dma_fence *f;
> +
> +		mutex_lock(&d->hw.lock);
> +		f = d->pf_fence;
> +		d->pf_fence = NULL;
> +		mutex_unlock(&d->hw.lock);
> +
> +		if (f) {
> +			if (!queued)
> +				dma_fence_signal(f);
> +
> +			dma_fence_put(f);
> +		}
> +
> +		xe_eudebug_put(d);
> +	}
> +
> +	if (!queued)
> +		destroy_pagefault(pf);
> +
> +	xe_eudebug_attention_poll_start(gt_to_xe(gt));
> +}
> +
> +static int send_queued_pagefaults(struct xe_eudebug *d)
> +{
> +	struct xe_eudebug_pagefault *pf, *pf_temp;
> +	int ret = 0;
> +
> +	mutex_lock(&d->pf_lock);
> +	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
> +		ret = send_pagefault(pf, true);
> +
> +		/* if resolved attentions are not updated */
> +		if (ret == -EBUSY)
> +			break;
> +
> +		list_del(&pf->link);
> +
> +		destroy_pagefault(pf);
> +
> +		if (ret)
> +			break;
> +	}
> +	mutex_unlock(&d->pf_lock);
> +
> +	return ret;
> +}
> +
> +int xe_eudebug_handle_pagefaults(struct xe_gt *gt)
> +{
> +	struct xe_exec_queue *q;
> +	struct xe_eudebug *d;
> +	int ret, lrc_idx;
> +
> +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
> +	if (IS_ERR(q))
> +		return PTR_ERR(q);
> +
> +	if (!xe_exec_queue_is_debuggable(q)) {
> +		ret = -EPERM;
> +		goto out_exec_queue_put;
> +	}
> +
> +	d = xe_eudebug_get_nolock(q->vm->xef);
> +	if (!d) {
> +		ret = -ENOTCONN;
> +		goto out_exec_queue_put;
> +	}
> +
> +	ret = send_queued_pagefaults(d);
> +
> +	xe_eudebug_put(d);
> +
> +out_exec_queue_put:
> +	xe_exec_queue_put(q);
> +
> +	return ret;
> +}
> +
> +void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
> +{
> +	struct xe_eudebug_pagefault *f = pf->consumer.epf;
> +
> +	if (!f)
> +		return;
> +
> +	if (f->is_null)
> +		xe_eudebug_pagefault_process(f);
> +}
> +
> +void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
> +{
> +	struct xe_eudebug_pagefault *f = pf->consumer.epf;
> +
> +	if (!f)
> +		return;
> +
> +	if (err)
> +		f->is_null = false;
> +
> +	_xe_eudebug_pagefault_destroy(f);
> +}
> +
> +void xe_eudebug_pagefault_fini(struct xe_eudebug *d)
> +{
> +	struct xe_eudebug_pagefault *pf, *pf_temp;
> +
> +	/* Since it's the last reference no race here */
> +
> +	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
> +		list_del(&pf->link);
> +		destroy_pagefault(pf);
> +	}
> +
> +	XE_WARN_ON(d->pf_fence);
> +}
> diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.h b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
> new file mode 100644
> index 000000000000..1ba20beac3cf
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2023-2025 Intel Corporation
> + */
> +
> +#ifndef _XE_EUDEBUG_PAGEFAULT_H_
> +#define _XE_EUDEBUG_PAGEFAULT_H_
> +
> +#include <linux/types.h>
> +
> +struct xe_eudebug;
> +struct xe_gt;
> +struct xe_pagefault;
> +struct xe_eudebug_pagefault;
> +struct xe_vm;
> +
> +void xe_eudebug_pagefault_fini(struct xe_eudebug *d);
> +int xe_eudebug_handle_pagefaults(struct xe_gt *gt);
> +
> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> +void xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf);
> +struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf);
> +void xe_eudebug_pagefault_service(struct xe_pagefault *pf);
> +void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err);
> +#else
> +
> +static inline void
> +xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
> +{
> +}
> +
> +static inline struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
> +{
> +	return NULL;
> +}
> +
> +static inline void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
> +{
> +}
> +
> +static inline void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
> +{
> +}
> +
> +#endif
> +
> +#endif /* _XE_EUDEBUG_PAGEFAULT_H_ */
> diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
> index 386b5c78ecff..09bfae8b94ab 100644
> --- a/drivers/gpu/drm/xe/xe_eudebug_types.h
> +++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
> @@ -15,6 +15,8 @@
>  #include <linux/wait.h>
>  #include <linux/xarray.h>
>  
> +#include "xe_gt_debug_types.h"
> +
>  struct xe_device;
>  struct task_struct;
>  struct xe_eudebug;
> @@ -37,7 +39,7 @@ enum xe_eudebug_state {
>  };
>  
>  #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
> -#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EU_ATTENTION
> +#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_PAGEFAULT
>  
>  /**
>   * struct xe_eudebug_handle - eudebug resource handle
> @@ -164,6 +166,71 @@ struct xe_eudebug {
>  
>  	/** @ops: operations for eu_control */
>  	struct xe_eudebug_eu_control_ops *ops;
> +
> +	/** @pf_lock: guards access to pagefaults list*/
> +	struct mutex pf_lock;
> +	/** @pagefaults: xe_eudebug_pagefault list for pagefault event queuing */
> +	struct list_head pagefaults;
> +	/**
> +	 * @pf_fence: fence on operations of eus (eu thread control and attention)
> +	 * when page faults are being handled, protected by @eu_lock.
> +	 */
> +	struct dma_fence *pf_fence;
> +};
> +
> +/**
> + * struct xe_eudebug_pagefault - eudebug structure for queuing pagefault
> + */
> +struct xe_eudebug_pagefault {
> +	/** @link: link into the xe_eudebug.pagefaults */
> +	struct list_head link;
> +	/** @q: exec_queue which raised pagefault */
> +	struct xe_exec_queue *q;
> +	/** @lrc_idx: lrc index of the workload which raised pagefault */
> +	int lrc_idx;
> +
> +	/** @fault: pagefault raw partial data passed from guc */
> +	struct {
> +		/** @addr: ppgtt address where the pagefault occurred */
> +		u64 addr;
> +		u8 type_level;
> +		u8 access_type;
> +	} fault;
> +
> +	/** @attentions: attention states in different phases of fault */
> +	struct {
> +		/** @before: state of attention bits before page fault WA processing*/
> +		struct xe_eu_attentions before;
> +		/**
> +		 * @after: status of attention bits during page fault WA processing.
> +		 * It includes eu threads where attention bits are turned on for
> +		 * reasons other than page fault WA (breakpoint, interrupt, etc.).
> +		 */
> +		struct xe_eu_attentions after;
> +		/**
> +		 * @resolved: state of the attention bits after page fault WA.
> +		 * It includes the eu thread that caused the page fault.
> +		 * To determine the eu thread that caused the page fault,
> +		 * do XOR attentions.after and attentions.resolved.
> +		 */
> +		struct xe_eu_attentions resolved;
> +	} attentions;
> +
> +	/**
> +	 * @deferred_resolved: to update attentions.resolved again when attention
> +	 * bits are ready if the eu thread fails to turn on attention bits within
> +	 * a certain time after page fault WA processing.
> +	 */
> +	bool deferred_resolved;
> +
> +	/**
> +	 * @is_null: marks if this vma is null or not. The lookup for the
> +	 * vma is done in two phases and eudebug pagefault struct needs
> +	 * to be allocated apriori to resolving if we need null vma or not.
> +	 * So we keep the state here so that processing and teardown
> +	 * know which type of fault resulted in creation of this eudebug pf.
> +	 */
> +	bool is_null;
>  };
>  
>  #endif /* _XE_EUDEBUG_TYPES_H_ */
> diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
> index 0e378f41ede6..2bee858da597 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
> +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
> @@ -10,6 +10,7 @@
>  
>  struct xe_gt;
>  struct xe_pagefault;
> +struct xe_eudebug_pagefault;
>  
>  /** enum xe_pagefault_access_type - Xe page fault access type */
>  enum xe_pagefault_access_type {
> @@ -84,6 +85,9 @@ struct xe_pagefault {
>  		u8 engine_class;
>  		/** @consumer.engine_instance: engine instance */
>  		u8 engine_instance;
> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> +		struct xe_eudebug_pagefault *epf;
> +#endif


This will grow the pagefault struct from 64 bytes to 128 bytes.
Everything will still be functionally correct, but I’d really prefer not
to increase the size of this structure. The u64 reserved field will be
used to implement the page-fault cache for fault storms, so that is a
non-starter.

Can we replace producer->private with epf and set a mask bit in the
lower 3 bits to indicate that producer->private has been replaced by
epf, then unwind epf vs. the original private on the producer side
during the ack/cleanup? In that case, we would store the original
producer->private in epf, if that isn’t clear.

Another thing we will have to consider is how the EU debug interface for
page faults will interact with the pagefault cache for fault storms
that’s in the pipe [2] (which I’ll post as soon as CI is fixed). My
initial thought is that it should be fine, given that the head of a
fault storm will populate epf, and subsequent faults that hit the page
being serviced will not have it populated. I’ll CC the EU debug team
when I post this code to ensure we aren’t clobbering each other’s
designs.

[2] https://gitlab.freedesktop.org/mbrost/xe-kernel-driver-svn-perf-6-15-2025/-/commit/93669c7f4e00ec13d0a18e28d34dfcb41803b7c9

Matt

>  		/** consumer.reserved: reserved bits for future expansion */
>  		u64 reserved;
>  	} consumer;
> diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
> index 54394a7e12ab..f7d035532be2 100644
> --- a/include/uapi/drm/xe_drm_eudebug.h
> +++ b/include/uapi/drm/xe_drm_eudebug.h
> @@ -53,6 +53,7 @@ struct drm_xe_eudebug_event {
>  #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
>  #define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	6
>  #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	7
> +#define DRM_XE_EUDEBUG_EVENT_PAGEFAULT		8
>  
>  	/** @flags: Flags */
>  	__u16 flags;
> @@ -358,6 +359,17 @@ struct drm_xe_eudebug_event_eu_attention {
>  	__u8 bitmask[];
>  };
>  
> +struct drm_xe_eudebug_event_pagefault {
> +	struct drm_xe_eudebug_event base;
> +
> +	__u64 exec_queue_handle;
> +	__u64 lrc_handle;
> +	__u32 flags;
> +	__u32 bitmask_size;
> +	__u64 pagefault_address;
> +	__u8 bitmask[];
> +};
> +
>  #if defined(__cplusplus)
>  }
>  #endif
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* ✗ Xe.CI.FULL: failure for Intel Xe GPU Debug Support (eudebug) v7
  2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
                   ` (25 preceding siblings ...)
  2026-02-23 15:51 ` ✓ Xe.CI.BAT: success " Patchwork
@ 2026-02-24  8:42 ` Patchwork
  26 siblings, 0 replies; 35+ messages in thread
From: Patchwork @ 2026-02-24  8:42 UTC (permalink / raw)
  To: Mika Kuoppala; +Cc: intel-xe

[-- Attachment #1: Type: text/plain, Size: 45495 bytes --]

== Series Details ==

Series: Intel Xe GPU Debug Support (eudebug) v7
URL   : https://patchwork.freedesktop.org/series/161979/
State : failure

== Summary ==

CI Bug Log - changes from xe-4590-616fe8160929511b0679615cda09751043490b18_FULL -> xe-pw-161979v1_FULL
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with xe-pw-161979v1_FULL absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in xe-pw-161979v1_FULL, please notify your bug team (I915-ci-infra@lists.freedesktop.org) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  

Participating hosts (2 -> 2)
------------------------------

  No changes in participating hosts

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in xe-pw-161979v1_FULL:

### IGT changes ###

#### Possible regressions ####

  * igt@xe_eudebug_online@pagefault-read-stress@drm_xe_engine_class_render0:
    - shard-lnl:          NOTRUN -> [FAIL][1] +31 other tests fail
   [1]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-3/igt@xe_eudebug_online@pagefault-read-stress@drm_xe_engine_class_render0.html

  * igt@xe_eudebug_online@single-step@drm_xe_engine_class_render0:
    - shard-bmg:          NOTRUN -> [FAIL][2] +29 other tests fail
   [2]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-3/igt@xe_eudebug_online@single-step@drm_xe_engine_class_render0.html

  * igt@xe_evict@evict-beng-threads-small:
    - shard-bmg:          [PASS][3] -> [INCOMPLETE][4] +1 other test incomplete
   [3]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-3/igt@xe_evict@evict-beng-threads-small.html
   [4]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@xe_evict@evict-beng-threads-small.html

  * igt@xe_exec_reset@cm-cat-error:
    - shard-lnl:          [PASS][5] -> [ABORT][6] +3 other tests abort
   [5]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-4/igt@xe_exec_reset@cm-cat-error.html
   [6]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_exec_reset@cm-cat-error.html

  * igt@xe_fault_injection@inject-fault-probe-function-xe_guc_ct_init:
    - shard-bmg:          [PASS][7] -> [ABORT][8] +5 other tests abort
   [7]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-7/igt@xe_fault_injection@inject-fault-probe-function-xe_guc_ct_init.html
   [8]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-1/igt@xe_fault_injection@inject-fault-probe-function-xe_guc_ct_init.html

  
#### Warnings ####

  * igt@xe_eudebug@basic-vm-access-faultable:
    - shard-lnl:          [SKIP][9] ([Intel XE#4837]) -> [FAIL][10] +36 other tests fail
   [9]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-8/igt@xe_eudebug@basic-vm-access-faultable.html
   [10]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-1/igt@xe_eudebug@basic-vm-access-faultable.html

  * igt@xe_eudebug@basic-vm-bind-metadata-discovery:
    - shard-bmg:          [SKIP][11] ([Intel XE#4837]) -> [FAIL][12] +28 other tests fail
   [11]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-9/igt@xe_eudebug@basic-vm-bind-metadata-discovery.html
   [12]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-7/igt@xe_eudebug@basic-vm-bind-metadata-discovery.html

  * igt@xe_eudebug_online@pagefault-read-stress:
    - shard-lnl:          [SKIP][13] ([Intel XE#6665]) -> [FAIL][14]
   [13]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-1/igt@xe_eudebug_online@pagefault-read-stress.html
   [14]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-3/igt@xe_eudebug_online@pagefault-read-stress.html
    - shard-bmg:          [SKIP][15] ([Intel XE#6665] / [Intel XE#6681]) -> [FAIL][16] +1 other test fail
   [15]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-9/igt@xe_eudebug_online@pagefault-read-stress.html
   [16]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_eudebug_online@pagefault-read-stress.html

  * igt@xe_eudebug_online@resume-one:
    - shard-lnl:          [SKIP][17] ([Intel XE#4837] / [Intel XE#6665]) -> [FAIL][18] +18 other tests fail
   [17]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-7/igt@xe_eudebug_online@resume-one.html
   [18]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-8/igt@xe_eudebug_online@resume-one.html

  * igt@xe_eudebug_online@set-breakpoint-sigint-debugger:
    - shard-bmg:          [SKIP][19] ([Intel XE#4837] / [Intel XE#6665]) -> [FAIL][20] +17 other tests fail
   [19]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-1/igt@xe_eudebug_online@set-breakpoint-sigint-debugger.html
   [20]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-4/igt@xe_eudebug_online@set-breakpoint-sigint-debugger.html

  
Known issues
------------

  Here are the changes found in xe-pw-161979v1_FULL that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@kms_big_fb@x-tiled-8bpp-rotate-270:
    - shard-bmg:          NOTRUN -> [SKIP][21] ([Intel XE#2327]) +1 other test skip
   [21]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_big_fb@x-tiled-8bpp-rotate-270.html

  * igt@kms_big_fb@yf-tiled-32bpp-rotate-180:
    - shard-lnl:          NOTRUN -> [SKIP][22] ([Intel XE#1124]) +2 other tests skip
   [22]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_big_fb@yf-tiled-32bpp-rotate-180.html

  * igt@kms_big_fb@yf-tiled-8bpp-rotate-180:
    - shard-bmg:          NOTRUN -> [SKIP][23] ([Intel XE#1124])
   [23]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_big_fb@yf-tiled-8bpp-rotate-180.html

  * igt@kms_bw@linear-tiling-2-displays-2560x1440p:
    - shard-bmg:          NOTRUN -> [SKIP][24] ([Intel XE#367])
   [24]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_bw@linear-tiling-2-displays-2560x1440p.html

  * igt@kms_bw@linear-tiling-3-displays-1920x1080p:
    - shard-lnl:          NOTRUN -> [SKIP][25] ([Intel XE#367])
   [25]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_bw@linear-tiling-3-displays-1920x1080p.html

  * igt@kms_ccs@bad-pixel-format-4-tiled-mtl-rc-ccs-cc:
    - shard-bmg:          NOTRUN -> [SKIP][26] ([Intel XE#2887]) +3 other tests skip
   [26]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_ccs@bad-pixel-format-4-tiled-mtl-rc-ccs-cc.html

  * igt@kms_ccs@ccs-on-another-bo-y-tiled-gen12-rc-ccs:
    - shard-lnl:          NOTRUN -> [SKIP][27] ([Intel XE#2887]) +4 other tests skip
   [27]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_ccs@ccs-on-another-bo-y-tiled-gen12-rc-ccs.html

  * igt@kms_ccs@crc-primary-suspend-4-tiled-bmg-ccs:
    - shard-bmg:          [PASS][28] -> [INCOMPLETE][29] ([Intel XE#7084])
   [28]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-6/igt@kms_ccs@crc-primary-suspend-4-tiled-bmg-ccs.html
   [29]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-8/igt@kms_ccs@crc-primary-suspend-4-tiled-bmg-ccs.html

  * igt@kms_ccs@crc-sprite-planes-basic-4-tiled-lnl-ccs@pipe-d-dp-1:
    - shard-bmg:          NOTRUN -> [SKIP][30] ([Intel XE#2652]) +3 other tests skip
   [30]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@kms_ccs@crc-sprite-planes-basic-4-tiled-lnl-ccs@pipe-d-dp-1.html

  * igt@kms_chamelium_edid@dp-edid-change-during-suspend:
    - shard-lnl:          NOTRUN -> [SKIP][31] ([Intel XE#373]) +2 other tests skip
   [31]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_chamelium_edid@dp-edid-change-during-suspend.html

  * igt@kms_chamelium_edid@dp-edid-resolution-list:
    - shard-bmg:          NOTRUN -> [SKIP][32] ([Intel XE#2252]) +1 other test skip
   [32]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_chamelium_edid@dp-edid-resolution-list.html

  * igt@kms_color@ctm-signed:
    - shard-bmg:          [PASS][33] -> [ABORT][34] ([Intel XE#5545] / [Intel XE#6652]) +1 other test abort
   [33]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-1/igt@kms_color@ctm-signed.html
   [34]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-2/igt@kms_color@ctm-signed.html

  * igt@kms_content_protection@dp-mst-type-0:
    - shard-bmg:          NOTRUN -> [SKIP][35] ([Intel XE#2390] / [Intel XE#6974])
   [35]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_content_protection@dp-mst-type-0.html

  * igt@kms_content_protection@dp-mst-type-1:
    - shard-lnl:          NOTRUN -> [SKIP][36] ([Intel XE#307] / [Intel XE#6974])
   [36]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_content_protection@dp-mst-type-1.html

  * igt@kms_content_protection@suspend-resume@pipe-a-dp-1:
    - shard-bmg:          NOTRUN -> [FAIL][37] ([Intel XE#3304])
   [37]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@kms_content_protection@suspend-resume@pipe-a-dp-1.html

  * igt@kms_content_protection@uevent@pipe-a-dp-2:
    - shard-bmg:          NOTRUN -> [FAIL][38] ([Intel XE#6707])
   [38]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-7/igt@kms_content_protection@uevent@pipe-a-dp-2.html

  * igt@kms_cursor_crc@cursor-random-32x10:
    - shard-lnl:          NOTRUN -> [SKIP][39] ([Intel XE#1424])
   [39]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_cursor_crc@cursor-random-32x10.html

  * igt@kms_cursor_crc@cursor-sliding-512x512:
    - shard-bmg:          NOTRUN -> [SKIP][40] ([Intel XE#2321])
   [40]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_cursor_crc@cursor-sliding-512x512.html

  * igt@kms_dsc@dsc-fractional-bpp-with-bpc:
    - shard-bmg:          NOTRUN -> [SKIP][41] ([Intel XE#2244])
   [41]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_dsc@dsc-fractional-bpp-with-bpc.html

  * igt@kms_fbcon_fbt@psr-suspend:
    - shard-bmg:          NOTRUN -> [SKIP][42] ([Intel XE#776])
   [42]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_fbcon_fbt@psr-suspend.html

  * igt@kms_feature_discovery@psr1:
    - shard-bmg:          NOTRUN -> [SKIP][43] ([Intel XE#2374])
   [43]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_feature_discovery@psr1.html

  * igt@kms_flip@2x-blocking-absolute-wf_vblank-interruptible:
    - shard-lnl:          NOTRUN -> [SKIP][44] ([Intel XE#1421]) +2 other tests skip
   [44]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_flip@2x-blocking-absolute-wf_vblank-interruptible.html

  * igt@kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling:
    - shard-lnl:          NOTRUN -> [SKIP][45] ([Intel XE#7178]) +1 other test skip
   [45]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling.html

  * igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling:
    - shard-bmg:          NOTRUN -> [SKIP][46] ([Intel XE#7178])
   [46]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling.html

  * igt@kms_frontbuffer_tracking@drrs-argb161616f-draw-blt:
    - shard-bmg:          NOTRUN -> [SKIP][47] ([Intel XE#7061])
   [47]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_frontbuffer_tracking@drrs-argb161616f-draw-blt.html

  * igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-onoff:
    - shard-bmg:          NOTRUN -> [SKIP][48] ([Intel XE#4141])
   [48]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-onoff.html

  * igt@kms_frontbuffer_tracking@fbc-abgr161616f-draw-mmap-wc:
    - shard-lnl:          NOTRUN -> [SKIP][49] ([Intel XE#7061]) +1 other test skip
   [49]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_frontbuffer_tracking@fbc-abgr161616f-draw-mmap-wc.html

  * igt@kms_frontbuffer_tracking@fbcdrrs-modesetfrombusy:
    - shard-lnl:          NOTRUN -> [SKIP][50] ([Intel XE#651]) +3 other tests skip
   [50]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_frontbuffer_tracking@fbcdrrs-modesetfrombusy.html

  * igt@kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-mmap-wc:
    - shard-bmg:          NOTRUN -> [SKIP][51] ([Intel XE#2311]) +6 other tests skip
   [51]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-mmap-wc.html

  * igt@kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-indfb-msflip-blt:
    - shard-bmg:          NOTRUN -> [SKIP][52] ([Intel XE#2313]) +4 other tests skip
   [52]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-indfb-msflip-blt.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-blt:
    - shard-lnl:          NOTRUN -> [SKIP][53] ([Intel XE#656]) +6 other tests skip
   [53]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-blt.html

  * igt@kms_hdr@invalid-metadata-sizes:
    - shard-lnl:          NOTRUN -> [SKIP][54] ([Intel XE#1503])
   [54]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_hdr@invalid-metadata-sizes.html

  * igt@kms_plane@pixel-format-x-tiled-modifier@pipe-b-plane-5:
    - shard-bmg:          NOTRUN -> [SKIP][55] ([Intel XE#7130]) +1 other test skip
   [55]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_plane@pixel-format-x-tiled-modifier@pipe-b-plane-5.html

  * igt@kms_plane@pixel-format-y-tiled-gen12-rc-ccs-modifier:
    - shard-bmg:          NOTRUN -> [SKIP][56] ([Intel XE#7283])
   [56]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_plane@pixel-format-y-tiled-gen12-rc-ccs-modifier.html

  * igt@kms_plane@pixel-format-yf-tiled-ccs-modifier-source-clamping:
    - shard-lnl:          NOTRUN -> [SKIP][57] ([Intel XE#7283])
   [57]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_plane@pixel-format-yf-tiled-ccs-modifier-source-clamping.html

  * igt@kms_plane_lowres@tiling-yf:
    - shard-lnl:          NOTRUN -> [SKIP][58] ([Intel XE#599])
   [58]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_plane_lowres@tiling-yf.html

  * igt@kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-5@pipe-b:
    - shard-bmg:          NOTRUN -> [SKIP][59] ([Intel XE#2763] / [Intel XE#6886]) +4 other tests skip
   [59]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-5@pipe-b.html

  * igt@kms_pm_rpm@dpms-mode-unset-non-lpsp:
    - shard-lnl:          NOTRUN -> [SKIP][60] ([Intel XE#1439] / [Intel XE#836])
   [60]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_pm_rpm@dpms-mode-unset-non-lpsp.html

  * igt@kms_pm_rpm@package-g7:
    - shard-lnl:          NOTRUN -> [SKIP][61] ([Intel XE#6813])
   [61]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_pm_rpm@package-g7.html

  * igt@kms_psr2_sf@fbc-pr-cursor-plane-move-continuous-sf:
    - shard-bmg:          NOTRUN -> [SKIP][62] ([Intel XE#1406] / [Intel XE#1489]) +1 other test skip
   [62]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_psr2_sf@fbc-pr-cursor-plane-move-continuous-sf.html

  * igt@kms_psr2_sf@fbc-psr2-cursor-plane-move-continuous-exceed-fully-sf:
    - shard-lnl:          NOTRUN -> [SKIP][63] ([Intel XE#1406] / [Intel XE#2893] / [Intel XE#4608])
   [63]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_psr2_sf@fbc-psr2-cursor-plane-move-continuous-exceed-fully-sf.html

  * igt@kms_psr2_sf@fbc-psr2-cursor-plane-move-continuous-exceed-fully-sf@pipe-b-edp-1:
    - shard-lnl:          NOTRUN -> [SKIP][64] ([Intel XE#1406] / [Intel XE#4608]) +1 other test skip
   [64]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_psr2_sf@fbc-psr2-cursor-plane-move-continuous-exceed-fully-sf@pipe-b-edp-1.html

  * igt@kms_psr2_su@frontbuffer-xrgb8888:
    - shard-lnl:          NOTRUN -> [SKIP][65] ([Intel XE#1128] / [Intel XE#1406])
   [65]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_psr2_su@frontbuffer-xrgb8888.html

  * igt@kms_psr@pr-sprite-render:
    - shard-bmg:          NOTRUN -> [SKIP][66] ([Intel XE#1406] / [Intel XE#2234] / [Intel XE#2850]) +1 other test skip
   [66]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_psr@pr-sprite-render.html

  * igt@kms_rotation_crc@primary-rotation-90:
    - shard-bmg:          NOTRUN -> [SKIP][67] ([Intel XE#3414] / [Intel XE#3904])
   [67]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_rotation_crc@primary-rotation-90.html

  * igt@kms_rotation_crc@primary-y-tiled-reflect-x-0:
    - shard-lnl:          NOTRUN -> [SKIP][68] ([Intel XE#1127])
   [68]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_rotation_crc@primary-y-tiled-reflect-x-0.html

  * igt@kms_sharpness_filter@filter-basic:
    - shard-bmg:          NOTRUN -> [SKIP][69] ([Intel XE#6503])
   [69]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@kms_sharpness_filter@filter-basic.html

  * igt@kms_tiled_display@basic-test-pattern-with-chamelium:
    - shard-lnl:          NOTRUN -> [SKIP][70] ([Intel XE#362])
   [70]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@kms_tiled_display@basic-test-pattern-with-chamelium.html

  * igt@xe_eudebug_online@writes-caching-sram-bb-vram-target-vram@drm_xe_engine_class_render0:
    - shard-lnl:          NOTRUN -> [SKIP][71] ([Intel XE#2825]) +5 other tests skip
   [71]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_eudebug_online@writes-caching-sram-bb-vram-target-vram@drm_xe_engine_class_render0.html

  * igt@xe_evict@evict-large-multi-vm:
    - shard-lnl:          NOTRUN -> [SKIP][72] ([Intel XE#6540] / [Intel XE#688]) +1 other test skip
   [72]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_evict@evict-large-multi-vm.html

  * igt@xe_exec_basic@multigpu-many-execqueues-many-vm-basic:
    - shard-lnl:          NOTRUN -> [SKIP][73] ([Intel XE#1392]) +2 other tests skip
   [73]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_exec_basic@multigpu-many-execqueues-many-vm-basic.html

  * igt@xe_exec_basic@multigpu-once-basic-defer-bind:
    - shard-bmg:          NOTRUN -> [SKIP][74] ([Intel XE#2322])
   [74]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_exec_basic@multigpu-once-basic-defer-bind.html

  * igt@xe_exec_fault_mode@once-multi-queue-userptr-rebind-imm:
    - shard-lnl:          NOTRUN -> [SKIP][75] ([Intel XE#7136]) +3 other tests skip
   [75]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_exec_fault_mode@once-multi-queue-userptr-rebind-imm.html

  * igt@xe_exec_fault_mode@twice-multi-queue-userptr:
    - shard-bmg:          NOTRUN -> [SKIP][76] ([Intel XE#7136]) +2 other tests skip
   [76]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_exec_fault_mode@twice-multi-queue-userptr.html

  * igt@xe_exec_multi_queue@few-execs-userptr:
    - shard-lnl:          NOTRUN -> [SKIP][77] ([Intel XE#6874]) +6 other tests skip
   [77]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_exec_multi_queue@few-execs-userptr.html

  * igt@xe_exec_multi_queue@many-queues-preempt-mode-fault-close-fd:
    - shard-bmg:          NOTRUN -> [SKIP][78] ([Intel XE#6874]) +5 other tests skip
   [78]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_exec_multi_queue@many-queues-preempt-mode-fault-close-fd.html

  * igt@xe_exec_threads@threads-multi-queue-cm-shared-vm-userptr-invalidate:
    - shard-bmg:          NOTRUN -> [SKIP][79] ([Intel XE#7138]) +1 other test skip
   [79]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_exec_threads@threads-multi-queue-cm-shared-vm-userptr-invalidate.html

  * igt@xe_exec_threads@threads-multi-queue-mixed-userptr:
    - shard-lnl:          NOTRUN -> [SKIP][80] ([Intel XE#7138]) +2 other tests skip
   [80]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_exec_threads@threads-multi-queue-mixed-userptr.html

  * igt@xe_fault_injection@exec-queue-create-fail-xe_pxp_exec_queue_add:
    - shard-bmg:          NOTRUN -> [SKIP][81] ([Intel XE#6281])
   [81]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_fault_injection@exec-queue-create-fail-xe_pxp_exec_queue_add.html

  * igt@xe_multigpu_svm@mgpu-xgpu-access-basic:
    - shard-lnl:          NOTRUN -> [SKIP][82] ([Intel XE#6964])
   [82]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_multigpu_svm@mgpu-xgpu-access-basic.html

  * igt@xe_pm@d3cold-i2c:
    - shard-lnl:          NOTRUN -> [SKIP][83] ([Intel XE#5694])
   [83]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_pm@d3cold-i2c.html

  * igt@xe_pm@s3-d3cold-basic-exec:
    - shard-lnl:          NOTRUN -> [SKIP][84] ([Intel XE#2284] / [Intel XE#366])
   [84]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_pm@s3-d3cold-basic-exec.html

  * igt@xe_pmu@engine-activity-accuracy-90:
    - shard-lnl:          [PASS][85] -> [FAIL][86] ([Intel XE#7072]) +1 other test fail
   [85]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-6/igt@xe_pmu@engine-activity-accuracy-90.html
   [86]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-7/igt@xe_pmu@engine-activity-accuracy-90.html

  * igt@xe_pxp@pxp-stale-bo-exec-post-rpm:
    - shard-bmg:          NOTRUN -> [SKIP][87] ([Intel XE#4733])
   [87]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_pxp@pxp-stale-bo-exec-post-rpm.html

  * igt@xe_query@multigpu-query-topology-l3-bank-mask:
    - shard-lnl:          NOTRUN -> [SKIP][88] ([Intel XE#944])
   [88]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_query@multigpu-query-topology-l3-bank-mask.html

  * igt@xe_vm@out-of-memory:
    - shard-lnl:          NOTRUN -> [SKIP][89] ([Intel XE#5745])
   [89]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_vm@out-of-memory.html

  * igt@xe_wedged@wedged-at-any-timeout:
    - shard-lnl:          [PASS][90] -> [ABORT][91] ([Intel XE#3119])
   [90]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-5/igt@xe_wedged@wedged-at-any-timeout.html
   [91]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-2/igt@xe_wedged@wedged-at-any-timeout.html

  
#### Possible fixes ####

  * igt@kms_async_flips@async-flip-with-page-flip-events-linear-atomic@pipe-c-edp-1:
    - shard-lnl:          [FAIL][92] ([Intel XE#6054]) -> [PASS][93] +3 other tests pass
   [92]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-4/igt@kms_async_flips@async-flip-with-page-flip-events-linear-atomic@pipe-c-edp-1.html
   [93]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-6/igt@kms_async_flips@async-flip-with-page-flip-events-linear-atomic@pipe-c-edp-1.html

  * igt@kms_bw@linear-tiling-1-displays-1920x1080p:
    - shard-bmg:          [SKIP][94] ([Intel XE#367]) -> [PASS][95]
   [94]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-6/igt@kms_bw@linear-tiling-1-displays-1920x1080p.html
   [95]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@kms_bw@linear-tiling-1-displays-1920x1080p.html

  * igt@kms_psr_stress_test@invalidate-primary-flip-overlay:
    - shard-lnl:          [SKIP][96] ([Intel XE#1406] / [Intel XE#4692]) -> [PASS][97]
   [96]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-1/igt@kms_psr_stress_test@invalidate-primary-flip-overlay.html
   [97]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-3/igt@kms_psr_stress_test@invalidate-primary-flip-overlay.html

  * igt@xe_eudebug_sriov@deny-sriov:
    - shard-bmg:          [SKIP][98] ([Intel XE#5793] / [Intel XE#7320]) -> [PASS][99]
   [98]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-5/igt@xe_eudebug_sriov@deny-sriov.html
   [99]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_eudebug_sriov@deny-sriov.html

  * igt@xe_exec_sip_eudebug@breakpoint-writesip-nodebug:
    - shard-bmg:          [SKIP][100] ([Intel XE#4837]) -> [PASS][101] +1 other test pass
   [100]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-1/igt@xe_exec_sip_eudebug@breakpoint-writesip-nodebug.html
   [101]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-4/igt@xe_exec_sip_eudebug@breakpoint-writesip-nodebug.html

  * igt@xe_exec_sip_eudebug@wait-writesip-nodebug:
    - shard-lnl:          [SKIP][102] ([Intel XE#4837]) -> [PASS][103] +2 other tests pass
   [102]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-8/igt@xe_exec_sip_eudebug@wait-writesip-nodebug.html
   [103]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-5/igt@xe_exec_sip_eudebug@wait-writesip-nodebug.html

  * igt@xe_exec_system_allocator@many-64k-mmap-remap:
    - shard-bmg:          [DMESG-FAIL][104] -> [PASS][105]
   [104]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@xe_exec_system_allocator@many-64k-mmap-remap.html
   [105]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@xe_exec_system_allocator@many-64k-mmap-remap.html

  * igt@xe_exec_system_allocator@threads-many-stride-new-nomemset:
    - shard-bmg:          [SKIP][106] ([Intel XE#6703]) -> [PASS][107] +3 other tests pass
   [106]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@xe_exec_system_allocator@threads-many-stride-new-nomemset.html
   [107]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@xe_exec_system_allocator@threads-many-stride-new-nomemset.html

  * igt@xe_fault_injection@vm-bind-fail-xe_pt_update_ops_prepare:
    - shard-bmg:          [ABORT][108] -> [PASS][109]
   [108]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-5/igt@xe_fault_injection@vm-bind-fail-xe_pt_update_ops_prepare.html
   [109]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_fault_injection@vm-bind-fail-xe_pt_update_ops_prepare.html

  * igt@xe_live_ktest@xe_eudebug:
    - shard-lnl:          [SKIP][110] ([Intel XE#2833]) -> [PASS][111]
   [110]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-4/igt@xe_live_ktest@xe_eudebug.html
   [111]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-3/igt@xe_live_ktest@xe_eudebug.html
    - shard-bmg:          [SKIP][112] ([Intel XE#2833]) -> [PASS][113]
   [112]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-7/igt@xe_live_ktest@xe_eudebug.html
   [113]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-8/igt@xe_live_ktest@xe_eudebug.html

  * igt@xe_module_load@load:
    - shard-bmg:          ([PASS][114], [PASS][115], [PASS][116], [PASS][117], [PASS][118], [PASS][119], [PASS][120], [PASS][121], [PASS][122], [PASS][123], [PASS][124], [PASS][125], [PASS][126], [SKIP][127], [PASS][128], [PASS][129], [PASS][130], [PASS][131], [PASS][132], [PASS][133], [PASS][134], [PASS][135], [PASS][136], [PASS][137], [PASS][138], [PASS][139]) ([Intel XE#2457]) -> ([PASS][140], [PASS][141], [PASS][142], [PASS][143], [PASS][144], [PASS][145], [PASS][146], [PASS][147], [PASS][148], [PASS][149], [PASS][150], [PASS][151], [PASS][152], [PASS][153], [PASS][154], [PASS][155], [PASS][156], [PASS][157], [PASS][158], [PASS][159], [PASS][160], [PASS][161], [PASS][162], [PASS][163], [PASS][164])
   [114]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-4/igt@xe_module_load@load.html
   [115]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-4/igt@xe_module_load@load.html
   [116]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-9/igt@xe_module_load@load.html
   [117]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-9/igt@xe_module_load@load.html
   [118]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-9/igt@xe_module_load@load.html
   [119]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@xe_module_load@load.html
   [120]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@xe_module_load@load.html
   [121]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@xe_module_load@load.html
   [122]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-3/igt@xe_module_load@load.html
   [123]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-8/igt@xe_module_load@load.html
   [124]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-4/igt@xe_module_load@load.html
   [125]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-3/igt@xe_module_load@load.html
   [126]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-3/igt@xe_module_load@load.html
   [127]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-5/igt@xe_module_load@load.html
   [128]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-7/igt@xe_module_load@load.html
   [129]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-8/igt@xe_module_load@load.html
   [130]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-7/igt@xe_module_load@load.html
   [131]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-7/igt@xe_module_load@load.html
   [132]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-6/igt@xe_module_load@load.html
   [133]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-5/igt@xe_module_load@load.html
   [134]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-5/igt@xe_module_load@load.html
   [135]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-5/igt@xe_module_load@load.html
   [136]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-6/igt@xe_module_load@load.html
   [137]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-6/igt@xe_module_load@load.html
   [138]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-1/igt@xe_module_load@load.html
   [139]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-1/igt@xe_module_load@load.html
   [140]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-2/igt@xe_module_load@load.html
   [141]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@xe_module_load@load.html
   [142]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@xe_module_load@load.html
   [143]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@xe_module_load@load.html
   [144]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-3/igt@xe_module_load@load.html
   [145]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-4/igt@xe_module_load@load.html
   [146]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-1/igt@xe_module_load@load.html
   [147]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_module_load@load.html
   [148]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_module_load@load.html
   [149]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-8/igt@xe_module_load@load.html
   [150]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-4/igt@xe_module_load@load.html
   [151]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-3/igt@xe_module_load@load.html
   [152]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-2/igt@xe_module_load@load.html
   [153]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-1/igt@xe_module_load@load.html
   [154]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@xe_module_load@load.html
   [155]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@xe_module_load@load.html
   [156]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-5/igt@xe_module_load@load.html
   [157]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-7/igt@xe_module_load@load.html
   [158]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-8/igt@xe_module_load@load.html
   [159]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-7/igt@xe_module_load@load.html
   [160]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-7/igt@xe_module_load@load.html
   [161]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-3/igt@xe_module_load@load.html
   [162]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_module_load@load.html
   [163]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-6/igt@xe_module_load@load.html
   [164]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-4/igt@xe_module_load@load.html

  
#### Warnings ####

  * igt@kms_chamelium_hpd@hdmi-hpd-with-enabled-mode:
    - shard-bmg:          [SKIP][165] ([Intel XE#6703]) -> [SKIP][166] ([Intel XE#2252])
   [165]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@kms_chamelium_hpd@hdmi-hpd-with-enabled-mode.html
   [166]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@kms_chamelium_hpd@hdmi-hpd-with-enabled-mode.html

  * igt@kms_feature_discovery@dp-mst:
    - shard-bmg:          [SKIP][167] ([Intel XE#6703]) -> [SKIP][168] ([Intel XE#2375])
   [167]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@kms_feature_discovery@dp-mst.html
   [168]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@kms_feature_discovery@dp-mst.html

  * igt@kms_frontbuffer_tracking@fbc-argb161616f-draw-render:
    - shard-bmg:          [SKIP][169] ([Intel XE#6703]) -> [SKIP][170] ([Intel XE#7061])
   [169]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-2/igt@kms_frontbuffer_tracking@fbc-argb161616f-draw-render.html
   [170]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-9/igt@kms_frontbuffer_tracking@fbc-argb161616f-draw-render.html

  * igt@xe_eudebug@multigpu-basic-client:
    - shard-bmg:          [SKIP][171] ([Intel XE#4837]) -> [SKIP][172] ([Intel XE#3894])
   [171]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-bmg-7/igt@xe_eudebug@multigpu-basic-client.html
   [172]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-bmg-1/igt@xe_eudebug@multigpu-basic-client.html

  * igt@xe_eudebug@multigpu-basic-client-many:
    - shard-lnl:          [SKIP][173] ([Intel XE#4837]) -> [SKIP][174] ([Intel XE#5132]) +1 other test skip
   [173]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-7/igt@xe_eudebug@multigpu-basic-client-many.html
   [174]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-8/igt@xe_eudebug@multigpu-basic-client-many.html

  * igt@xe_eudebug_online@breakpoint-many-sessions-tiles:
    - shard-lnl:          [SKIP][175] ([Intel XE#4837] / [Intel XE#6665]) -> [SKIP][176] ([Intel XE#2846])
   [175]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-8/igt@xe_eudebug_online@breakpoint-many-sessions-tiles.html
   [176]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-1/igt@xe_eudebug_online@breakpoint-many-sessions-tiles.html

  * igt@xe_eudebug_online@writes-caching-sram-bb-vram-target-vram:
    - shard-lnl:          [SKIP][177] ([Intel XE#4837] / [Intel XE#6665]) -> [SKIP][178] ([Intel XE#2825]) +5 other tests skip
   [177]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-4590-616fe8160929511b0679615cda09751043490b18/shard-lnl-4/igt@xe_eudebug_online@writes-caching-sram-bb-vram-target-vram.html
   [178]: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/shard-lnl-4/igt@xe_eudebug_online@writes-caching-sram-bb-vram-target-vram.html

  
  [Intel XE#1124]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1124
  [Intel XE#1127]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1127
  [Intel XE#1128]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1128
  [Intel XE#1392]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1392
  [Intel XE#1406]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1406
  [Intel XE#1421]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1421
  [Intel XE#1424]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1424
  [Intel XE#1439]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1439
  [Intel XE#1489]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1489
  [Intel XE#1503]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1503
  [Intel XE#2234]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2234
  [Intel XE#2244]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2244
  [Intel XE#2252]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2252
  [Intel XE#2284]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2284
  [Intel XE#2311]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2311
  [Intel XE#2313]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2313
  [Intel XE#2321]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2321
  [Intel XE#2322]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2322
  [Intel XE#2327]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2327
  [Intel XE#2374]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2374
  [Intel XE#2375]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2375
  [Intel XE#2390]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2390
  [Intel XE#2457]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2457
  [Intel XE#2652]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2652
  [Intel XE#2763]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2763
  [Intel XE#2825]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2825
  [Intel XE#2833]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2833
  [Intel XE#2846]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2846
  [Intel XE#2850]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2850
  [Intel XE#2887]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2887
  [Intel XE#2893]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2893
  [Intel XE#307]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/307
  [Intel XE#3119]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3119
  [Intel XE#3304]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3304
  [Intel XE#3414]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3414
  [Intel XE#362]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/362
  [Intel XE#366]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/366
  [Intel XE#367]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/367
  [Intel XE#373]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/373
  [Intel XE#3894]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3894
  [Intel XE#3904]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3904
  [Intel XE#4141]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4141
  [Intel XE#4608]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4608
  [Intel XE#4692]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4692
  [Intel XE#4733]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4733
  [Intel XE#4837]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4837
  [Intel XE#5132]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5132
  [Intel XE#5545]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5545
  [Intel XE#5694]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5694
  [Intel XE#5745]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5745
  [Intel XE#5793]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5793
  [Intel XE#599]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/599
  [Intel XE#6054]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6054
  [Intel XE#6281]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6281
  [Intel XE#6503]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6503
  [Intel XE#651]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/651
  [Intel XE#6540]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6540
  [Intel XE#656]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/656
  [Intel XE#6652]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6652
  [Intel XE#6665]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6665
  [Intel XE#6681]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6681
  [Intel XE#6703]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6703
  [Intel XE#6707]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6707
  [Intel XE#6813]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6813
  [Intel XE#6874]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6874
  [Intel XE#688]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/688
  [Intel XE#6886]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6886
  [Intel XE#6964]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6964
  [Intel XE#6974]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6974
  [Intel XE#7061]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7061
  [Intel XE#7072]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7072
  [Intel XE#7084]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7084
  [Intel XE#7130]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7130
  [Intel XE#7136]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7136
  [Intel XE#7138]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7138
  [Intel XE#7178]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7178
  [Intel XE#7283]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7283
  [Intel XE#7320]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7320
  [Intel XE#776]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/776
  [Intel XE#836]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/836
  [Intel XE#944]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/944


Build changes
-------------

  * Linux: xe-4590-616fe8160929511b0679615cda09751043490b18 -> xe-pw-161979v1

  IGT_8765: 8765
  xe-4590-616fe8160929511b0679615cda09751043490b18: 616fe8160929511b0679615cda09751043490b18
  xe-pw-161979v1: 161979v1

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/xe-pw-161979v1/index.html

[-- Attachment #2: Type: text/html, Size: 51326 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface
  2026-02-23 19:08   ` Matthew Brost
@ 2026-02-27 22:10     ` Gwan-gyeong Mun
  2026-02-28  0:36       ` Matthew Brost
  0 siblings, 1 reply; 35+ messages in thread
From: Gwan-gyeong Mun @ 2026-02-27 22:10 UTC (permalink / raw)
  To: Matthew Brost, Mika Kuoppala
  Cc: intel-xe, simona.vetter, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, Jan Maślak



On 2/23/26 11:08 AM, Matthew Brost wrote:
> On Mon, Feb 23, 2026 at 04:03:16PM +0200, Mika Kuoppala wrote:
>> From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>>
> 
> Not a complete review but a few quick comments below.
> 
Thank you for your comments. I have left comments below for each point.

>> The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
>> access will halt the corresponding EUs. To solve this problem, introduce
>> EU pagefault handling functionality, which allows to unhalt pagefaulted
>> eu threads and to EU debugger to get inform about the eu attentions state
>> of EU threads during execution.
>>
>> If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
>> after handling the pagefault. The pagefault eudebug event follows
>> the newly added drm_xe_eudebug_event_pagefault type.
>> When a pagefault occurs, it prevents to send the
>> DRM_XE_EUDEBUG_EVENT_EU_ATTENTION event to the client during pagefault
>> handling.
>>
>> The page fault event delivery follows the below policy.
>> (1) If EU Debugger discovery has completed and pagefaulted eu threads turn
>>      on attention bit then pagefault handler delivers pagefault event
>>      directly.
>> (2) If a pagefault occurs during eu debugger discovery process, pagefault
>>      handler queues a pagefault event and sends the queued event when
>>      discovery has completed and pagefaulted eu threads turn on attention
>>      bit.
>> (3) If the pagefaulted eu thread struggles to turn on the attention bit
>>      within the specified time, the attention scan worker sends a pagefault
>>      event when it detects that the attention bit is turned on.
>>
>> If multiple eu threads are running and a pagefault occurs due to accessing
>> the same invalid address, send a single pagefault event
>> (DRM_XE_EUDEBUG_EVENT_PAGEFAULT type) to the user debugger instead of a
>> pagefault event for each of the multiple eu threads.
>> If eu threads (other than the one that caused the page fault before) access
>> the new invalid addresses, send a new pagefault event.
>>
>> As the attention scan worker send the eu attention event whenever the
>> attention bit is turned on, user debugger receives attenion event
>> immediately after pagefault event.
>> In this case, the page-fault event always precedes the attention event.
>>
>> When the user debugger receives an attention event after a pagefault event,
>> it can detect whether additional breakpoints or interrupts occur in
>> addition to the existing pagefault by comparing the eu threads where the
>> pagefault occurred with the eu threads where the attention bit is newly
>> enabled.
>>
>> v2: use only force exception (Joonas, Mika)
>> v3: rebased on v4 (Mika)
>> v4: streamline uapi, cleanups (Mika)
>> v5: struct member documentation (Mika)
>> v6: fault to fault_type (Mika)
>>
>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>> Signed-off-by: Jan Maślak <jan.maslak@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> ---
>>   drivers/gpu/drm/xe/Makefile               |   2 +-
>>   drivers/gpu/drm/xe/xe_eudebug.c           | 100 ++++-
>>   drivers/gpu/drm/xe/xe_eudebug.h           |   9 +
>>   drivers/gpu/drm/xe/xe_eudebug_hw.c        |  15 +-
>>   drivers/gpu/drm/xe/xe_eudebug_pagefault.c | 440 ++++++++++++++++++++++
>>   drivers/gpu/drm/xe/xe_eudebug_pagefault.h |  47 +++
>>   drivers/gpu/drm/xe/xe_eudebug_types.h     |  69 +++-
>>   drivers/gpu/drm/xe/xe_pagefault_types.h   |   4 +
>>   include/uapi/drm/xe_drm_eudebug.h         |  12 +
>>   9 files changed, 676 insertions(+), 22 deletions(-)
>>   create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.c
>>   create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.h
>>
>> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
>> index 34db797ef8fc..b49fe7ae18e7 100644
>> --- a/drivers/gpu/drm/xe/Makefile
>> +++ b/drivers/gpu/drm/xe/Makefile
>> @@ -152,7 +152,7 @@ xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
>>   xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
>>   
>>   # debugging shaders with gdb (eudebug) support
>> -xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_gt_debug.o
>> +xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_eudebug_pagefault.o xe_gt_debug.o
>>   
>>   # graphics hardware monitoring (HWMON) support
>>   xe-$(CONFIG_HWMON) += xe_hwmon.o
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
>> index eae93c5f5e86..4b2f0dd9d234 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug.c
>> +++ b/drivers/gpu/drm/xe/xe_eudebug.c
>> @@ -17,12 +17,16 @@
>>   #include "xe_eudebug.h"
>>   #include "xe_eudebug_hw.h"
>>   #include "xe_eudebug_types.h"
>> +#include "xe_eudebug_pagefault.h"
>>   #include "xe_eudebug_vm.h"
>>   #include "xe_exec_queue.h"
>> +#include "xe_force_wake.h"
>>   #include "xe_gt.h"
>>   #include "xe_hw_engine.h"
>>   #include "xe_gt.h"
>>   #include "xe_gt_debug.h"
>> +#include "xe_gt_mcr.h"
>> +#include "regs/xe_gt_regs.h"
>>   #include "xe_macros.h"
>>   #include "xe_pm.h"
>>   #include "xe_sriov_pf.h"
>> @@ -263,6 +267,7 @@ static void xe_eudebug_free(struct kref *ref)
>>   	while (kfifo_get(&d->events.fifo, &event))
>>   		kfree(event);
>>   
>> +	xe_eudebug_pagefault_fini(d);
>>   	xe_eudebug_resources_destroy(d);
>>   	mutex_destroy(&d->target.lock);
>>   	XE_WARN_ON(d->target.xef);
>> @@ -461,7 +466,7 @@ static int _xe_eudebug_disconnect(struct xe_eudebug *d,
>>   	} \
>>   })
>>   
>> -static struct xe_eudebug *
>> +struct xe_eudebug *
>>   xe_eudebug_get_nolock(struct xe_file *xef)
>>   {
>>   	struct xe_eudebug *d;
>> @@ -1888,10 +1893,6 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
>>   {
>>   	int ret;
>>   
>> -	ret = xe_gt_eu_threads_needing_attention(gt);
>> -	if (ret <= 0)
>> -		return ret;
>> -
>>   	ret = xe_send_gt_attention(gt);
>>   
>>   	/* Discovery in progress, fake it */
>> @@ -1901,6 +1902,65 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
>>   	return ret;
>>   }
>>   
>> +int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
>> +				    struct xe_eudebug_pagefault *pf)
>> +{
>> +	struct drm_xe_eudebug_event_pagefault *ep;
>> +	struct drm_xe_eudebug_event *event;
>> +	int h_queue, h_lrc;
>> +	u32 size = xe_gt_eu_attention_bitmap_size(pf->q->gt) * 3;
>> +	u32 sz = struct_size(ep, bitmask, size);
>> +	int ret;
>> +
>> +	XE_WARN_ON(pf->lrc_idx < 0 || pf->lrc_idx >= pf->q->width);
>> +
>> +	XE_WARN_ON(!xe_exec_queue_is_debuggable(pf->q));
>> +
>> +	h_queue = find_handle(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, pf->q);
>> +	if (h_queue < 0)
>> +		return h_queue;
>> +
>> +	h_lrc = find_handle(d, XE_EUDEBUG_RES_TYPE_LRC, pf->q->lrc[pf->lrc_idx]);
>> +	if (h_lrc < 0)
>> +		return h_lrc;
>> +
>> +	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_PAGEFAULT, 0,
>> +					DRM_XE_EUDEBUG_EVENT_STATE_CHANGE, sz);
>> +
>> +	if (!event)
>> +		return -ENOSPC;
>> +
>> +	ep = cast_event(ep, event);
>> +	ep->exec_queue_handle = h_queue;
>> +	ep->lrc_handle = h_lrc;
>> +	ep->bitmask_size = size;
>> +	ep->pagefault_address = pf->fault.addr;
>> +
>> +	memcpy(ep->bitmask, pf->attentions.before.att, pf->attentions.before.size);
>> +	memcpy(ep->bitmask + pf->attentions.before.size,
>> +	       pf->attentions.after.att, pf->attentions.after.size);
>> +	memcpy(ep->bitmask + pf->attentions.before.size + pf->attentions.after.size,
>> +	       pf->attentions.resolved.att, pf->attentions.resolved.size);
>> +
>> +	event->seqno = atomic_long_inc_return(&d->events.seqno);
>> +
>> +	ret = xe_eudebug_queue_event(d, event);
>> +	if (ret)
>> +		xe_eudebug_disconnect(d, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static void handle_attention_fail(struct xe_gt *gt, int gt_id, int ret)
>> +{
>> +	/* TODO: error capture */
>> +	drm_info(&gt_to_xe(gt)->drm,
>> +		 "gt:%d unable to handle eu attention ret = %d\n",
>> +		 gt_id, ret);
>> +
>> +	xe_gt_reset_async(gt);
>> +}
>> +
>>   static void attention_poll_work(struct work_struct *work)
>>   {
>>   	struct xe_device *xe = container_of(work, typeof(*xe),
>> @@ -1923,15 +1983,15 @@ static void attention_poll_work(struct work_struct *work)
>>   			if (gt->info.type != XE_GT_TYPE_MAIN)
>>   				continue;
>>   
>> -			ret = xe_eudebug_handle_gt_attention(gt);
>> -			if (ret) {
>> -				/* TODO: error capture */
>> -				drm_info(&gt_to_xe(gt)->drm,
>> -					 "gt:%d unable to handle eu attention ret=%d\n",
>> -					 gt_id, ret);
>> +			if (!xe_gt_eu_threads_needing_attention(gt))
>> +				continue;
>> +
>> +			ret = xe_eudebug_handle_pagefaults(gt);
>> +			if (!ret)
>> +				ret = xe_eudebug_handle_gt_attention(gt);
>>   
>> -				xe_gt_reset_async(gt);
>> -			}
>> +			if (ret)
>> +				handle_attention_fail(gt, gt_id, ret);
>>   		}
>>   
>>   		xe_pm_runtime_put(xe);
>> @@ -1940,12 +2000,12 @@ static void attention_poll_work(struct work_struct *work)
>>   	schedule_delayed_work(&xe->eudebug.attention_dwork, delay);
>>   }
>>   
>> -static void attention_poll_stop(struct xe_device *xe)
>> +void xe_eudebug_attention_poll_stop(struct xe_device *xe)
>>   {
>>   	cancel_delayed_work_sync(&xe->eudebug.attention_dwork);
>>   }
>>   
>> -static void attention_poll_start(struct xe_device *xe)
>> +void xe_eudebug_attention_poll_start(struct xe_device *xe)
>>   {
>>   	mod_delayed_work(system_wq, &xe->eudebug.attention_dwork, 0);
>>   }
>> @@ -1988,6 +2048,8 @@ xe_eudebug_connect(struct xe_device *xe,
>>   
>>   	kref_init(&d->ref);
>>   	mutex_init(&d->target.lock);
>> +	mutex_init(&d->pf_lock);
>> +	INIT_LIST_HEAD(&d->pagefaults);
>>   	init_waitqueue_head(&d->events.write_done);
>>   	init_waitqueue_head(&d->events.read_done);
>>   	init_completion(&d->discovery);
>> @@ -2019,7 +2081,7 @@ xe_eudebug_connect(struct xe_device *xe,
>>   
>>   	kref_get(&d->ref);
>>   	queue_work(xe->eudebug.wq, &d->discovery_work);
>> -	attention_poll_start(xe);
>> +	xe_eudebug_attention_poll_start(xe);
>>   
>>   	eu_dbg(d, "connected session %lld", d->session);
>>   
>> @@ -2098,9 +2160,9 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
>>   	mutex_unlock(&xe->eudebug.lock);
>>   
>>   	if (enable) {
>> -		attention_poll_start(xe);
>> +		xe_eudebug_attention_poll_start(xe);
>>   	} else {
>> -		attention_poll_stop(xe);
>> +		xe_eudebug_attention_poll_stop(xe);
>>   
>>   		if (IS_SRIOV_PF(xe))
>>   			xe_sriov_pf_end_lockdown(xe);
>> @@ -2153,7 +2215,7 @@ static void xe_eudebug_fini(struct drm_device *dev, void *__unused)
>>   
>>   	xe_assert(xe, list_empty(&xe->eudebug.targets));
>>   
>> -	attention_poll_stop(xe);
>> +	xe_eudebug_attention_poll_stop(xe);
>>   }
>>   
>>   void xe_eudebug_init(struct xe_device *xe)
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
>> index bd9fd7bf454f..34938e87be13 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug.h
>> +++ b/drivers/gpu/drm/xe/xe_eudebug.h
>> @@ -13,12 +13,14 @@ struct drm_file;
>>   struct xe_debug_data;
>>   struct xe_device;
>>   struct xe_file;
>> +struct xe_gt;
>>   struct xe_vm;
>>   struct xe_vma;
>>   struct xe_vma_ops;
>>   struct xe_exec_queue;
>>   struct xe_user_fence;
>>   struct xe_eudebug;
>> +struct xe_eudebug_pagefault;
>>   
>>   #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
>>   
>> @@ -72,8 +74,15 @@ void xe_eudebug_ufence_init(struct xe_user_fence *ufence);
>>   void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
>>   bool xe_eudebug_ufence_track(struct xe_user_fence *ufence);
>>   
>> +struct xe_eudebug *xe_eudebug_get_nolock(struct xe_file *xef);
>>   void xe_eudebug_put(struct xe_eudebug *d);
>>   
>> +int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
>> +				    struct xe_eudebug_pagefault *pf);
>> +
>> +void xe_eudebug_attention_poll_stop(struct xe_device *xe);
>> +void xe_eudebug_attention_poll_start(struct xe_device *xe);
>> +
>>   #else
>>   
>>   static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
>> index 5365265a67b3..270f7abc82e9 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug_hw.c
>> +++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
>> @@ -322,6 +322,7 @@ static int do_eu_control(struct xe_eudebug *d,
>>   	struct xe_device *xe = d->xe;
>>   	u8 *bits = NULL;
>>   	unsigned int hw_attn_size, attn_size;
>> +	struct dma_fence *pf_fence;
>>   	struct xe_exec_queue *q;
>>   	struct xe_lrc *lrc;
>>   	u64 seqno;
>> @@ -376,8 +377,20 @@ static int do_eu_control(struct xe_eudebug *d,
>>   		goto out_free;
>>   	}
>>   
>> -	ret = -EINVAL;
>>   	mutex_lock(&d->hw.lock);
>> +	do {
>> +		pf_fence = dma_fence_get(d->pf_fence);
>> +		if (pf_fence) {
>> +			mutex_unlock(&d->hw.lock);
>> +			ret = dma_fence_wait(pf_fence, true);
>> +			dma_fence_put(pf_fence);
>> +			if (ret)
>> +				goto out_free;
>> +			mutex_lock(&d->hw.lock);
>> +		}
>> +	} while (pf_fence);
>> +
>> +	ret = -EINVAL;
>>   
>>   	switch (arg->cmd) {
>>   	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL:
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.c b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
>> new file mode 100644
>> index 000000000000..edd368a7f6ae
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
>> @@ -0,0 +1,440 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2023-2025 Intel Corporation
>> + */
>> +
>> +#include "xe_eudebug_pagefault.h"
>> +
>> +#include <linux/delay.h>
>> +
>> +#include "xe_exec_queue.h"
>> +#include "xe_eudebug.h"
>> +#include "xe_eudebug_hw.h"
>> +#include "xe_force_wake.h"
>> +#include "xe_gt_debug.h"
>> +#include "xe_gt_mcr.h"
>> +#include "regs/xe_gt_regs.h"
>> +#include "xe_vm.h"
>> +
>> +static struct xe_gt *
>> +pf_to_gt(struct xe_eudebug_pagefault *pf)
>> +{
>> +	return pf->q->gt;
>> +}
>> +
>> +static void destroy_pagefault(struct xe_eudebug_pagefault *pf)
>> +{
>> +	xe_exec_queue_put(pf->q);
>> +	kfree(pf);
>> +}
>> +
>> +static int queue_pagefault(struct xe_eudebug_pagefault *pf)
>> +{
>> +	struct xe_eudebug *d;
>> +
>> +	d = xe_eudebug_get_nolock(pf->q->vm->xef);
>> +	if (!d)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&d->pf_lock);
>> +	list_add_tail(&pf->link, &d->pagefaults);
>> +	mutex_unlock(&d->pf_lock);
>> +
>> +	xe_eudebug_put(d);
>> +
>> +	return 0;
>> +}
>> +
>> +static int send_pagefault(struct xe_eudebug_pagefault *pf,
>> +			  bool from_attention_scan)
>> +{
>> +	struct xe_gt *gt = pf_to_gt(pf);
>> +	struct xe_eudebug *d;
>> +	struct xe_exec_queue *q;
>> +	int ret, lrc_idx;
>> +
>> +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
>> +	if (IS_ERR(q))
>> +		return PTR_ERR(q);
>> +
>> +	if (!xe_exec_queue_is_debuggable(q)) {
>> +		ret = -EPERM;
>> +		goto out_exec_queue_put;
>> +	}
>> +
>> +	d = xe_eudebug_get_nolock(q->vm->xef);
>> +	if (!d) {
>> +		ret = -ENOTCONN;
>> +		goto out_exec_queue_put;
>> +	}
>> +
>> +	if (pf->deferred_resolved) {
>> +		xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
>> +					 XE_GT_ATTENTION_TIMEOUT_MS);
>> +
>> +		if (!xe_eu_attentions_xor_count(&pf->attentions.after,
>> +						&pf->attentions.resolved) &&
>> +		    !from_attention_scan) {
>> +			eu_dbg(d, "xe attentions not yet updated\n");
>> +			ret = -EBUSY;
>> +			goto out_eudebug_put;
>> +		}
>> +	}
>> +
>> +	ret = xe_eudebug_send_pagefault_event(d, pf);
>> +
>> +out_eudebug_put:
>> +	xe_eudebug_put(d);
>> +out_exec_queue_put:
>> +	xe_exec_queue_put(q);
>> +
>> +	return ret;
>> +}
>> +
>> +static const char *
>> +pagefault_get_driver_name(struct dma_fence *dma_fence)
>> +{
>> +	return "xe";
>> +}
>> +
>> +static const char *
>> +pagefault_fence_get_timeline_name(struct dma_fence *dma_fence)
>> +{
>> +	return "eudebug_pagefault_fence";
>> +}
>> +
>> +static const struct dma_fence_ops pagefault_fence_ops = {
>> +	.get_driver_name = pagefault_get_driver_name,
>> +	.get_timeline_name = pagefault_fence_get_timeline_name,
>> +};
>> +
>> +struct pagefault_fence {
>> +	struct dma_fence base;
>> +	spinlock_t lock;
>> +};
>> +
>> +static struct pagefault_fence *pagefault_fence_create(void)
>> +{
>> +	struct pagefault_fence *fence;
>> +
>> +	fence = kzalloc_obj(*fence, GFP_KERNEL);
>> +	if (fence == NULL)
>> +		return NULL;
>> +
>> +	spin_lock_init(&fence->lock);
>> +	dma_fence_init(&fence->base, &pagefault_fence_ops, &fence->lock,
>> +		       dma_fence_context_alloc(1), 1);
>> +
>> +	return fence;
>> +}
>> +
>> +void
>> +xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
> 
> This function, as written, is basically a no from me given that
> DRM_XE_EUDEBUG is enabled by default. It adds time complexity via
> xe_vm_find_vma_by_addr(), which is O(log N) where N is the number of
> VMAs.
> 
> Page faults are going to be heavily optimized since this is a critical
> path. Anything less than O(1) here when no EU connection exists —
> combined with DRM_XE_EUDEBUG being on — is likely to receive pushback
> from me.
> 
I'll consider an implementation where eudebug directly uses the vma 
value returned by xe_vm_find_vma_by_addr(), which is called by 
xe_pagefault_service(). this way will avoid the performance degradation 
caused by additional xe_vm_find_vma_by_addr() calls. ( Previously, due 
to lock dependencies, eudebug directly called xe_vm_find_vma_by_addr(). 
I will verify whether this issue still exists. )

>> +{
>> +	struct pagefault_fence *pf_fence;
>> +	struct xe_eudebug_pagefault *epf;
>> +	struct xe_vma *vma;
>> +	struct xe_gt *gt = pf->gt;
>> +	struct xe_exec_queue *q;
>> +	struct dma_fence *fence;
>> +	struct xe_eudebug *d;
>> +	unsigned int fw_ref;
>> +	int lrc_idx;
>> +	u32 td_ctl;
>> +
>> +	pf->consumer.epf = NULL;
>> +
>> +	down_read(&vm->lock);
>> +	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
>> +	up_read(&vm->lock);
> 
> See my comment in [1] — this doesn't work for SVM. This will need to be
> rethought.
> 
> [1] https://patchwork.freedesktop.org/patch/706437/?series=161979&rev=1#comment_1299420
> 
Additional implementation of eudebug pagefault routine for  SVM is 
required. I have replied to the mentioned email thread.

>> +
>> +	if (vma)
>> +		return;
>> +
>> +	d = xe_eudebug_get_nolock(vm->xef);
>> +	if (!d)
>> +		return;
>> +
>> +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
>> +	if (IS_ERR(q))
>> +		goto err_put_eudebug;
>> +
>> +	if (XE_WARN_ON(q->vm != vm))
>> +		goto err_put_exec_queue;
>> +
>> +	if (!xe_exec_queue_is_debuggable(q))
>> +		goto err_put_exec_queue;
>> +
>> +	fw_ref = xe_force_wake_get(gt_to_fw(gt), q->hwe->domain);
>> +	if (!fw_ref)
>> +		goto err_put_exec_queue;
>> +
>> +	/*
>> +	 * If there is no debug functionality (TD_CTL_GLOBAL_DEBUG_ENABLE, etc.),
>> +	 * don't proceed pagefault routine for eu debugger.
>> +	 */
>> +	td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
>> +	if (!td_ctl)
>> +		goto err_put_fw;
>> +
>> +	epf = kzalloc_obj(*epf, GFP_KERNEL);
>> +	if (!epf)
>> +		goto err_put_fw;
>> +
>> +	xe_eudebug_attention_poll_stop(gt_to_xe(gt));
>> +
>> +	mutex_lock(&d->hw.lock);
>> +	fence = dma_fence_get(d->pf_fence);
>> +
>> +	if (fence) {
>> +		/*
>> +		 * TODO: If the new incoming pagefaulted address is different
>> +		 * from the pagefaulted address it is currently handling on the
>> +		 * same ASID, it needs a routine to wait here and then do the
>> +		 * following pagefault.
>> +		 */
>> +		dma_fence_put(fence);
>> +		goto err_unlock_hw_lock;
>> +	}
>> +
>> +	pf_fence = pagefault_fence_create();
>> +	if (!pf_fence)
>> +		goto err_unlock_hw_lock;
>> +
>> +	d->pf_fence = &pf_fence->base;
>> +
>> +	INIT_LIST_HEAD(&epf->link);
>> +
>> +	xe_gt_eu_attentions_read(gt, &epf->attentions.before, 0);
>> +
>> +	if (td_ctl & TD_CTL_FORCE_EXCEPTION)
>> +		eu_warn(d, "force exception already set!");
>> +
>> +	/* Halt regardless of thread dependencies */
>> +	while (!(td_ctl & TD_CTL_FORCE_EXCEPTION)) {
>> +		xe_gt_mcr_multicast_write(gt, TD_CTL,
>> +					  td_ctl | TD_CTL_FORCE_EXCEPTION);
>> +		udelay(200);
>> +		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
>> +	}
>> +
>> +	xe_gt_eu_attentions_read(gt, &epf->attentions.after,
>> +				 XE_GT_ATTENTION_TIMEOUT_MS);
>> +
>> +	mutex_unlock(&d->hw.lock);
>> +
>> +	/*
>> +	 * xe_exec_queue_put() will be called from xe_eudebug_pagefault_destroy()
>> +	 * or handle_pagefault()
>> +	 */
>> +	epf->q = q;
>> +	epf->lrc_idx = lrc_idx;
>> +	epf->fault.addr = pf->consumer.page_addr;
>> +	epf->fault.type_level = pf->consumer.fault_type_level;
>> +	epf->fault.access_type = pf->consumer.access_type;
>> +
>> +	pf->consumer.epf = epf;
>> +
>> +	xe_force_wake_put(gt_to_fw(gt), fw_ref);
>> +	xe_eudebug_put(d);
>> +
>> +	return;
>> +
>> +err_unlock_hw_lock:
>> +	mutex_unlock(&d->hw.lock);
>> +	xe_eudebug_attention_poll_start(gt_to_xe(gt));
>> +	kfree(epf);
>> +err_put_fw:
>> +	xe_force_wake_put(gt_to_fw(gt), fw_ref);
>> +err_put_exec_queue:
>> +	xe_exec_queue_put(q);
>> +err_put_eudebug:
>> +	xe_eudebug_put(d);
>> +}
>> +
>> +struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
>> +{
>> +	struct xe_vma *vma = NULL;
>> +
>> +	if (!pf->consumer.epf)
>> +		return NULL;
>> +
>> +	vma = xe_vm_create_null_vma(vm, pf->consumer.page_addr);
>> +	if (IS_ERR(vma))
>> +		return vma;
>> +
>> +	pf->consumer.epf->is_null = true;
>> +
>> +	return vma;
>> +}
>> +
>> +static void
>> +xe_eudebug_pagefault_process(struct xe_eudebug_pagefault *pf)
>> +{
>> +	struct xe_gt *gt = pf->q->gt;
>> +
>> +	xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
>> +				 XE_GT_ATTENTION_TIMEOUT_MS);
>> +
>> +	if (!xe_eu_attentions_xor_count(&pf->attentions.after,
>> +					&pf->attentions.resolved))
>> +		pf->deferred_resolved = true;
>> +}
>> +
>> +static void
>> +_xe_eudebug_pagefault_destroy(struct xe_eudebug_pagefault *pf)
>> +{
>> +	struct xe_gt *gt = pf->q->gt;
>> +	struct xe_vm *vm = pf->q->vm;
>> +	struct xe_eudebug *d;
>> +	unsigned int fw_ref;
>> +	u32 td_ctl;
>> +	bool queued, try_send;
>> +	int ret;
>> +
>> +	fw_ref = xe_force_wake_get(gt_to_fw(gt), pf->q->hwe->domain);
>> +	if (!fw_ref) {
>> +		struct xe_device *xe = gt_to_xe(gt);
>> +
>> +		drm_warn(&xe->drm, "Forcewake fail: Can not recover TD_CTL");
>> +	} else {
>> +		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
>> +		xe_gt_mcr_multicast_write(gt, TD_CTL, td_ctl &
>> +					  ~(TD_CTL_FORCE_EXCEPTION));
>> +		xe_force_wake_put(gt_to_fw(gt), fw_ref);
>> +	}
>> +
>> +	queued = false;
>> +	try_send = pf->is_null;
>> +	if (try_send) {
>> +		ret = send_pagefault(pf, false);
>> +
>> +		/*
>> +		 * if debugger discovery is not completed or resolved attentions are not
>> +		 * updated, then queue pagefault
>> +		 */
>> +		if (ret == -EBUSY) {
>> +			ret = queue_pagefault(pf);
>> +			if (!ret)
>> +				queued = true;
>> +		}
>> +	}
>> +
>> +	d = xe_eudebug_get_nolock(vm->xef);
>> +	if (d) {
>> +		struct dma_fence *f;
>> +
>> +		mutex_lock(&d->hw.lock);
>> +		f = d->pf_fence;
>> +		d->pf_fence = NULL;
>> +		mutex_unlock(&d->hw.lock);
>> +
>> +		if (f) {
>> +			if (!queued)
>> +				dma_fence_signal(f);
>> +
>> +			dma_fence_put(f);
>> +		}
>> +
>> +		xe_eudebug_put(d);
>> +	}
>> +
>> +	if (!queued)
>> +		destroy_pagefault(pf);
>> +
>> +	xe_eudebug_attention_poll_start(gt_to_xe(gt));
>> +}
>> +
>> +static int send_queued_pagefaults(struct xe_eudebug *d)
>> +{
>> +	struct xe_eudebug_pagefault *pf, *pf_temp;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&d->pf_lock);
>> +	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
>> +		ret = send_pagefault(pf, true);
>> +
>> +		/* if resolved attentions are not updated */
>> +		if (ret == -EBUSY)
>> +			break;
>> +
>> +		list_del(&pf->link);
>> +
>> +		destroy_pagefault(pf);
>> +
>> +		if (ret)
>> +			break;
>> +	}
>> +	mutex_unlock(&d->pf_lock);
>> +
>> +	return ret;
>> +}
>> +
>> +int xe_eudebug_handle_pagefaults(struct xe_gt *gt)
>> +{
>> +	struct xe_exec_queue *q;
>> +	struct xe_eudebug *d;
>> +	int ret, lrc_idx;
>> +
>> +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
>> +	if (IS_ERR(q))
>> +		return PTR_ERR(q);
>> +
>> +	if (!xe_exec_queue_is_debuggable(q)) {
>> +		ret = -EPERM;
>> +		goto out_exec_queue_put;
>> +	}
>> +
>> +	d = xe_eudebug_get_nolock(q->vm->xef);
>> +	if (!d) {
>> +		ret = -ENOTCONN;
>> +		goto out_exec_queue_put;
>> +	}
>> +
>> +	ret = send_queued_pagefaults(d);
>> +
>> +	xe_eudebug_put(d);
>> +
>> +out_exec_queue_put:
>> +	xe_exec_queue_put(q);
>> +
>> +	return ret;
>> +}
>> +
>> +void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
>> +{
>> +	struct xe_eudebug_pagefault *f = pf->consumer.epf;
>> +
>> +	if (!f)
>> +		return;
>> +
>> +	if (f->is_null)
>> +		xe_eudebug_pagefault_process(f);
>> +}
>> +
>> +void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
>> +{
>> +	struct xe_eudebug_pagefault *f = pf->consumer.epf;
>> +
>> +	if (!f)
>> +		return;
>> +
>> +	if (err)
>> +		f->is_null = false;
>> +
>> +	_xe_eudebug_pagefault_destroy(f);
>> +}
>> +
>> +void xe_eudebug_pagefault_fini(struct xe_eudebug *d)
>> +{
>> +	struct xe_eudebug_pagefault *pf, *pf_temp;
>> +
>> +	/* Since it's the last reference no race here */
>> +
>> +	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
>> +		list_del(&pf->link);
>> +		destroy_pagefault(pf);
>> +	}
>> +
>> +	XE_WARN_ON(d->pf_fence);
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.h b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
>> new file mode 100644
>> index 000000000000..1ba20beac3cf
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
>> @@ -0,0 +1,47 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2023-2025 Intel Corporation
>> + */
>> +
>> +#ifndef _XE_EUDEBUG_PAGEFAULT_H_
>> +#define _XE_EUDEBUG_PAGEFAULT_H_
>> +
>> +#include <linux/types.h>
>> +
>> +struct xe_eudebug;
>> +struct xe_gt;
>> +struct xe_pagefault;
>> +struct xe_eudebug_pagefault;
>> +struct xe_vm;
>> +
>> +void xe_eudebug_pagefault_fini(struct xe_eudebug *d);
>> +int xe_eudebug_handle_pagefaults(struct xe_gt *gt);
>> +
>> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
>> +void xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf);
>> +struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf);
>> +void xe_eudebug_pagefault_service(struct xe_pagefault *pf);
>> +void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err);
>> +#else
>> +
>> +static inline void
>> +xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
>> +{
>> +}
>> +
>> +static inline struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
>> +{
>> +	return NULL;
>> +}
>> +
>> +static inline void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
>> +{
>> +}
>> +
>> +static inline void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
>> +{
>> +}
>> +
>> +#endif
>> +
>> +#endif /* _XE_EUDEBUG_PAGEFAULT_H_ */
>> diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
>> index 386b5c78ecff..09bfae8b94ab 100644
>> --- a/drivers/gpu/drm/xe/xe_eudebug_types.h
>> +++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
>> @@ -15,6 +15,8 @@
>>   #include <linux/wait.h>
>>   #include <linux/xarray.h>
>>   
>> +#include "xe_gt_debug_types.h"
>> +
>>   struct xe_device;
>>   struct task_struct;
>>   struct xe_eudebug;
>> @@ -37,7 +39,7 @@ enum xe_eudebug_state {
>>   };
>>   
>>   #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
>> -#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EU_ATTENTION
>> +#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_PAGEFAULT
>>   
>>   /**
>>    * struct xe_eudebug_handle - eudebug resource handle
>> @@ -164,6 +166,71 @@ struct xe_eudebug {
>>   
>>   	/** @ops: operations for eu_control */
>>   	struct xe_eudebug_eu_control_ops *ops;
>> +
>> +	/** @pf_lock: guards access to pagefaults list*/
>> +	struct mutex pf_lock;
>> +	/** @pagefaults: xe_eudebug_pagefault list for pagefault event queuing */
>> +	struct list_head pagefaults;
>> +	/**
>> +	 * @pf_fence: fence on operations of eus (eu thread control and attention)
>> +	 * when page faults are being handled, protected by @eu_lock.
>> +	 */
>> +	struct dma_fence *pf_fence;
>> +};
>> +
>> +/**
>> + * struct xe_eudebug_pagefault - eudebug structure for queuing pagefault
>> + */
>> +struct xe_eudebug_pagefault {
>> +	/** @link: link into the xe_eudebug.pagefaults */
>> +	struct list_head link;
>> +	/** @q: exec_queue which raised pagefault */
>> +	struct xe_exec_queue *q;
>> +	/** @lrc_idx: lrc index of the workload which raised pagefault */
>> +	int lrc_idx;
>> +
>> +	/** @fault: pagefault raw partial data passed from guc */
>> +	struct {
>> +		/** @addr: ppgtt address where the pagefault occurred */
>> +		u64 addr;
>> +		u8 type_level;
>> +		u8 access_type;
>> +	} fault;
>> +
>> +	/** @attentions: attention states in different phases of fault */
>> +	struct {
>> +		/** @before: state of attention bits before page fault WA processing*/
>> +		struct xe_eu_attentions before;
>> +		/**
>> +		 * @after: status of attention bits during page fault WA processing.
>> +		 * It includes eu threads where attention bits are turned on for
>> +		 * reasons other than page fault WA (breakpoint, interrupt, etc.).
>> +		 */
>> +		struct xe_eu_attentions after;
>> +		/**
>> +		 * @resolved: state of the attention bits after page fault WA.
>> +		 * It includes the eu thread that caused the page fault.
>> +		 * To determine the eu thread that caused the page fault,
>> +		 * do XOR attentions.after and attentions.resolved.
>> +		 */
>> +		struct xe_eu_attentions resolved;
>> +	} attentions;
>> +
>> +	/**
>> +	 * @deferred_resolved: to update attentions.resolved again when attention
>> +	 * bits are ready if the eu thread fails to turn on attention bits within
>> +	 * a certain time after page fault WA processing.
>> +	 */
>> +	bool deferred_resolved;
>> +
>> +	/**
>> +	 * @is_null: marks if this vma is null or not. The lookup for the
>> +	 * vma is done in two phases and eudebug pagefault struct needs
>> +	 * to be allocated apriori to resolving if we need null vma or not.
>> +	 * So we keep the state here so that processing and teardown
>> +	 * know which type of fault resulted in creation of this eudebug pf.
>> +	 */
>> +	bool is_null;
>>   };
>>   
>>   #endif /* _XE_EUDEBUG_TYPES_H_ */
>> diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
>> index 0e378f41ede6..2bee858da597 100644
>> --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
>> +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
>> @@ -10,6 +10,7 @@
>>   
>>   struct xe_gt;
>>   struct xe_pagefault;
>> +struct xe_eudebug_pagefault;
>>   
>>   /** enum xe_pagefault_access_type - Xe page fault access type */
>>   enum xe_pagefault_access_type {
>> @@ -84,6 +85,9 @@ struct xe_pagefault {
>>   		u8 engine_class;
>>   		/** @consumer.engine_instance: engine instance */
>>   		u8 engine_instance;
>> +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
>> +		struct xe_eudebug_pagefault *epf;
>> +#endif
> 
> 
> This will grow the pagefault struct from 64 bytes to 128 bytes.
> Everything will still be functionally correct, but I’d really prefer not
> to increase the size of this structure. The u64 reserved field will be
> used to implement the page-fault cache for fault storms, so that is a
> non-starter.
> 
> Can we replace producer->private with epf and set a mask bit in the
> lower 3 bits to indicate that producer->private has been replaced by
> epf, then unwind epf vs. the original private on the producer side
> during the ack/cleanup? In that case, we would store the original
> producer->private in epf, if that isn’t clear.
> 
Thank you for your feedback. It seems I can change the implementation to 
store the epf in producer->private. I will incorporate this change in 
the next version.

> Another thing we will have to consider is how the EU debug interface for
> page faults will interact with the pagefault cache for fault storms
> that’s in the pipe [2] (which I’ll post as soon as CI is fixed). My
> initial thought is that it should be fine, given that the head of a
> fault storm will populate epf, and subsequent faults that hit the page
> being serviced will not have it populated. I’ll CC the EU debug team
> when I post this code to ensure we aren’t clobbering each other’s
> designs.
> 
> [2] https://gitlab.freedesktop.org/mbrost/xe-kernel-driver-svn-perf-6-15-2025/-/commit/93669c7f4e00ec13d0a18e28d34dfcb41803b7c9
> 
Yes, I've checked your patch series. 
https://patchwork.freedesktop.org/series/162167/

The eudebug pagefault handling routine does not appear to conflict 
structurally with the pagefault cache for fault storms. After verifying 
the behavior of applying the eudebug changes on top of your relevant 
patch, I will provide an additional reply.

G.G.

> Matt
> 
>>   		/** consumer.reserved: reserved bits for future expansion */
>>   		u64 reserved;
>>   	} consumer;
>> diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
>> index 54394a7e12ab..f7d035532be2 100644
>> --- a/include/uapi/drm/xe_drm_eudebug.h
>> +++ b/include/uapi/drm/xe_drm_eudebug.h
>> @@ -53,6 +53,7 @@ struct drm_xe_eudebug_event {
>>   #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
>>   #define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	6
>>   #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	7
>> +#define DRM_XE_EUDEBUG_EVENT_PAGEFAULT		8
>>   
>>   	/** @flags: Flags */
>>   	__u16 flags;
>> @@ -358,6 +359,17 @@ struct drm_xe_eudebug_event_eu_attention {
>>   	__u8 bitmask[];
>>   };
>>   
>> +struct drm_xe_eudebug_event_pagefault {
>> +	struct drm_xe_eudebug_event base;
>> +
>> +	__u64 exec_queue_handle;
>> +	__u64 lrc_handle;
>> +	__u32 flags;
>> +	__u32 bitmask_size;
>> +	__u64 pagefault_address;
>> +	__u8 bitmask[];
>> +};
>> +
>>   #if defined(__cplusplus)
>>   }
>>   #endif
>> -- 
>> 2.43.0
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling
  2026-02-23 18:41   ` Matthew Brost
@ 2026-02-27 22:11     ` Gwan-gyeong Mun
  0 siblings, 0 replies; 35+ messages in thread
From: Gwan-gyeong Mun @ 2026-02-27 22:11 UTC (permalink / raw)
  To: Matthew Brost, Mika Kuoppala
  Cc: intel-xe, simona.vetter, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk



On 2/23/26 10:41 AM, Matthew Brost wrote:
> On Mon, Feb 23, 2026 at 04:03:17PM +0200, Mika Kuoppala wrote:
>> From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>>
>> The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
>> access will halt the corresponding EUs. To solve this problem, enable
>> EU pagefault handling functionality, which allows to unhalt pagefaulted
>> eu threads and to EU debugger to get inform about the eu attentions state
>> of EU threads during execution.
>>
>> If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
>> after handling the pagefault.
>>
>> The pagefault handling is a mechanism that allows a stalled EU thread to
>> enter SIP mode by installing a temporal null page to the page table entry
>> where the pagefault happened.
>>
>> A brief description of the page fault handling mechanism flow between KMD
>> and the eu thread is as follows
>>
>> (1) eu thread accesses unallocated address
>> (2) pagefault happens and eu thread stalls
>> (3) XE kmd set an force eu thread exception to allow the running eu thread
>>      to enter SIP mode (kmd set ForceException / ForceExternalHalt bit of
>>      TD_CTL register)
>>      Not stalled (none-pagefaulted) eu threads enter SIP mode
>> (4) XE kmd installs temporal null page to the pagetable entry of the
>>      address where pagefault happened.
>> (5) XE kmd replies pagefault successful message to GUC
>> (6) stalled eu thread resumes as per pagefault condition has resolved
>> (7) resumed eu thread enters SIP mode due to force exception set by (3)
>> (8) adapted to consumer/produced pagefaults
>>
>> As designed this feature to only work when eudbug is enabled, it should
>> have no impact to regular recoverable pagefault code path.
>>
>> v2: - pf->q holds the vm ref so drop it (Mika)
>>      - streamline uapi (Mika)
>>      - cleanup the pagefault through producer if (Mika)
>>
>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> ---
>>   drivers/gpu/drm/xe/xe_guc_pagefault.c   |  8 +++++++
>>   drivers/gpu/drm/xe/xe_pagefault.c       | 31 ++++++++++++++++++++++++-
>>   drivers/gpu/drm/xe/xe_pagefault_types.h |  9 +++++++
>>   3 files changed, 47 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
>> index d48f6ed103bb..6adf3bf73b1c 100644
>> --- a/drivers/gpu/drm/xe/xe_guc_pagefault.c
>> +++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
>> @@ -8,6 +8,7 @@
>>   #include "xe_guc_ct.h"
>>   #include "xe_guc_pagefault.h"
>>   #include "xe_pagefault.h"
>> +#include "xe_eudebug_pagefault.h"
>>   
>>   static void guc_ack_fault(struct xe_pagefault *pf, int err)
>>   {
>> @@ -37,8 +38,15 @@ static void guc_ack_fault(struct xe_pagefault *pf, int err)
>>   	xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
>>   }
>>   
>> +static void guc_cleanup_fault(struct xe_pagefault *pf, int err)
>> +{
>> +	xe_eudebug_pagefault_service(pf);
>> +	xe_eudebug_pagefault_destroy(pf, 0);
>> +}
>> +
>>   static const struct xe_pagefault_ops guc_pagefault_ops = {
>>   	.ack_fault = guc_ack_fault,
>> +	.cleanup_fault = guc_cleanup_fault,
>>   };
>>   
>>   /**
>> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
>> index 72f589fd2b64..9dcd854e99f9 100644
>> --- a/drivers/gpu/drm/xe/xe_pagefault.c
>> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
>> @@ -10,6 +10,7 @@
>>   
>>   #include "xe_bo.h"
>>   #include "xe_device.h"
>> +#include "xe_eudebug_pagefault.h"
>>   #include "xe_gt_printk.h"
>>   #include "xe_gt_types.h"
>>   #include "xe_gt_stats.h"
>> @@ -171,6 +172,8 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>>   	if (IS_ERR(vm))
>>   		return PTR_ERR(vm);
>>   
>> +	xe_eudebug_pagefault_create(vm, pf);
>> +
>>   	/*
>>   	 * TODO: Change to read lock? Using write lock for simplicity.
>>   	 */
>> @@ -184,9 +187,28 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>>   	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
> 
> I've mentioned this before - this fundamentally broken if SVM is
> enabled as the VMA lookup will never fail given VMA tree is completely
> populated in the SVM cases (i.e., when SVM is enabled the first thing
> the UMD does is bind the entire CPU address space with a CPU mirror
> VMA). What will fail in the SVM case is xe_svm_handle_pagefault will
> likely return -ENOENT. UMDs from my understanding will enable SVM by
> default so this likely needs to be rethought.
> 
Thank you for your reply. Yes, additional implementation of eudebug 
pagefault is required for cases where SVM is used.
As per your comment, in the SVM + eudebug pagefault scenario, if 
xe_svm_handle_pagefault() returns -ENOENT (i.e., when memory allocation 
via mmap etc. is not performed in userspace),
eudebug requires a temporary page install at the address where the page 
fault occurred to allow stalled EU threads to enter SIP.

Two methods come to mind for temporary page installation in the page table:
1) Temporary memory allocation by emulating the implementation of 
do_mmap() / __mmap_region() in an eudebug pagefault situation,
    similar to the general memory allocation scenario in an SVM scenario

    - Pros: Follows the do_mmap() flow for memory allocation; only 
requires one additional call to xe_svm_handle_pagefault() after 
temporary page installation (simple code implementation).
    - Cons: (1) If the installed temporary VMA is not removed, a page 
fault occurring at the same address on the CPU triggers migration to 
system memory,
                preventing the CPU debugger from causing a segmentation 
fault.
            (2) The GPU debugger may fail to handle page faults for 
low-address regions inaccessible to userspace (mmap_min_addr issue)

2) In the SVM scenario, perform a temporary page install only on the GPU 
page table where the page fault occurred during the EUDEBUG page fault 
situation
    : Updating the page table directly without using the 
xe_svm_handle_pagefault() function flow


Could I get your input on these two approaches? Or do you have 
additional thoughts?

G.G.

> Matt
> 
>>   	if (!vma) {
>>   		err = -EINVAL;
>> -		goto unlock_vm;
>> +		vma = xe_eudebug_create_vma(vm, pf);
>> +		if (IS_ERR(vma)) {
>> +			err = PTR_ERR(vma);
>> +			vma = NULL;
>> +		}
>>   	}
>>   
>> +	if (vma) {
>> +		/*
>> +		 * When creating an instance of eudebug_pagefault, there was
>> +		 * no vma containing the ppgtt address where the pagefault occurred,
>> +		 * but when reacquiring vm->lock, there is.
>> +		 * During not aquiring the vm->lock from this context,
>> +		 * but vma corresponding to the address where the pagefault occurred
>> +		 * in another context has allocated.
>> +		 */
>> +		err = 0;
>> +	}
>> +
>> +	if (err)
>> +		goto unlock_vm;
>> +
>>   	atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
>>   
>>   	if (xe_vma_is_cpu_addr_mirror(vma))
>> @@ -198,6 +220,10 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>>   unlock_vm:
>>   	if (!err)
>>   		vm->usm.last_fault_vma = vma;
>> +
>> +	if (err)
>> +		xe_eudebug_pagefault_destroy(pf, err);
>> +
>>   	up_write(&vm->lock);
>>   	xe_vm_put(vm);
>>   
>> @@ -268,6 +294,9 @@ static void xe_pagefault_queue_work(struct work_struct *w)
>>   
>>   		pf.producer.ops->ack_fault(&pf, err);
>>   
>> +		if (pf.producer.ops->cleanup_fault)
>> +			pf.producer.ops->cleanup_fault(&pf, err);
>> +
>>   		if (time_after(jiffies, threshold)) {
>>   			queue_work(gt_to_xe(pf.gt)->usm.pf_wq, w);
>>   			break;
>> diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
>> index 2bee858da597..9d2d29d35a4b 100644
>> --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
>> +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
>> @@ -43,6 +43,15 @@ struct xe_pagefault_ops {
>>   	 * sends the result to the HW/FW interface.
>>   	 */
>>   	void (*ack_fault)(struct xe_pagefault *pf, int err);
>> +
>> +	/**
>> +	 * @cleanup_fault: Cleanup for producer, if any
>> +	 * @pf: Page fault
>> +	 * @err: Error state of fault
>> +	 *
>> +	 * Page fault producer received cleanup request from consumer
>> +	 */
>> +	void (*cleanup_fault)(struct xe_pagefault *pf, int err);
>>   };
>>   
>>   /**
>> -- 
>> 2.43.0
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling
  2026-02-23 14:03 ` [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling Mika Kuoppala
  2026-02-23 18:41   ` Matthew Brost
@ 2026-02-27 23:11   ` Gustavo Sousa
  2026-02-28  6:49     ` Gwan-gyeong Mun
  1 sibling, 1 reply; 35+ messages in thread
From: Gustavo Sousa @ 2026-02-27 23:11 UTC (permalink / raw)
  To: Mika Kuoppala, intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk, gwan-gyeong.mun,
	Mika Kuoppala

Mika Kuoppala <mika.kuoppala@linux.intel.com> writes:

> From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>
> The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
> access will halt the corresponding EUs. To solve this problem, enable
> EU pagefault handling functionality, which allows to unhalt pagefaulted
> eu threads and to EU debugger to get inform about the eu attentions state
> of EU threads during execution.
>
> If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
> after handling the pagefault.
>
> The pagefault handling is a mechanism that allows a stalled EU thread to
> enter SIP mode by installing a temporal null page to the page table entry
> where the pagefault happened.
>
> A brief description of the page fault handling mechanism flow between KMD
> and the eu thread is as follows
>
> (1) eu thread accesses unallocated address
> (2) pagefault happens and eu thread stalls
> (3) XE kmd set an force eu thread exception to allow the running eu thread
>     to enter SIP mode (kmd set ForceException / ForceExternalHalt bit of
>     TD_CTL register)
>     Not stalled (none-pagefaulted) eu threads enter SIP mode
> (4) XE kmd installs temporal null page to the pagetable entry of the
>     address where pagefault happened.
> (5) XE kmd replies pagefault successful message to GUC
> (6) stalled eu thread resumes as per pagefault condition has resolved
> (7) resumed eu thread enters SIP mode due to force exception set by (3)
> (8) adapted to consumer/produced pagefaults
>
> As designed this feature to only work when eudbug is enabled, it should
> have no impact to regular recoverable pagefault code path.
>
> v2: - pf->q holds the vm ref so drop it (Mika)
>     - streamline uapi (Mika)
>     - cleanup the pagefault through producer if (Mika)
>
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> ---
>  drivers/gpu/drm/xe/xe_guc_pagefault.c   |  8 +++++++
>  drivers/gpu/drm/xe/xe_pagefault.c       | 31 ++++++++++++++++++++++++-
>  drivers/gpu/drm/xe/xe_pagefault_types.h |  9 +++++++
>  3 files changed, 47 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
> index d48f6ed103bb..6adf3bf73b1c 100644
> --- a/drivers/gpu/drm/xe/xe_guc_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
> @@ -8,6 +8,7 @@
>  #include "xe_guc_ct.h"
>  #include "xe_guc_pagefault.h"
>  #include "xe_pagefault.h"
> +#include "xe_eudebug_pagefault.h"
>  
>  static void guc_ack_fault(struct xe_pagefault *pf, int err)
>  {
> @@ -37,8 +38,15 @@ static void guc_ack_fault(struct xe_pagefault *pf, int err)
>  	xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
>  }
>  
> +static void guc_cleanup_fault(struct xe_pagefault *pf, int err)
> +{
> +	xe_eudebug_pagefault_service(pf);
> +	xe_eudebug_pagefault_destroy(pf, 0);
> +}
> +
>  static const struct xe_pagefault_ops guc_pagefault_ops = {
>  	.ack_fault = guc_ack_fault,
> +	.cleanup_fault = guc_cleanup_fault,
>  };
>  
>  /**
> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
> index 72f589fd2b64..9dcd854e99f9 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault.c
> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
> @@ -10,6 +10,7 @@
>  
>  #include "xe_bo.h"
>  #include "xe_device.h"
> +#include "xe_eudebug_pagefault.h"
>  #include "xe_gt_printk.h"
>  #include "xe_gt_types.h"
>  #include "xe_gt_stats.h"
> @@ -171,6 +172,8 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  	if (IS_ERR(vm))
>  		return PTR_ERR(vm);
>  
> +	xe_eudebug_pagefault_create(vm, pf);

This might not get called because of the early returns above. And
looking at xe_guc_pagefault_handler(), we don't seem to zero initialize
the "pf" variable there.  Couldn't this cause a risk of
xe_eudebug_pagefault_destroy() being called with pf->consumer.epf
containing garbage?

--
Gustavo Sousa

> +
>  	/*
>  	 * TODO: Change to read lock? Using write lock for simplicity.
>  	 */
> @@ -184,9 +187,28 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
>  	if (!vma) {
>  		err = -EINVAL;
> -		goto unlock_vm;
> +		vma = xe_eudebug_create_vma(vm, pf);
> +		if (IS_ERR(vma)) {
> +			err = PTR_ERR(vma);
> +			vma = NULL;
> +		}
>  	}
>  
> +	if (vma) {
> +		/*
> +		 * When creating an instance of eudebug_pagefault, there was
> +		 * no vma containing the ppgtt address where the pagefault occurred,
> +		 * but when reacquiring vm->lock, there is.
> +		 * During not aquiring the vm->lock from this context,
> +		 * but vma corresponding to the address where the pagefault occurred
> +		 * in another context has allocated.
> +		 */
> +		err = 0;
> +	}
> +
> +	if (err)
> +		goto unlock_vm;
> +
>  	atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
>  
>  	if (xe_vma_is_cpu_addr_mirror(vma))
> @@ -198,6 +220,10 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>  unlock_vm:
>  	if (!err)
>  		vm->usm.last_fault_vma = vma;
> +
> +	if (err)
> +		xe_eudebug_pagefault_destroy(pf, err);
> +
>  	up_write(&vm->lock);
>  	xe_vm_put(vm);
>  
> @@ -268,6 +294,9 @@ static void xe_pagefault_queue_work(struct work_struct *w)
>  
>  		pf.producer.ops->ack_fault(&pf, err);
>  
> +		if (pf.producer.ops->cleanup_fault)
> +			pf.producer.ops->cleanup_fault(&pf, err);
> +
>  		if (time_after(jiffies, threshold)) {
>  			queue_work(gt_to_xe(pf.gt)->usm.pf_wq, w);
>  			break;
> diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
> index 2bee858da597..9d2d29d35a4b 100644
> --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
> +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
> @@ -43,6 +43,15 @@ struct xe_pagefault_ops {
>  	 * sends the result to the HW/FW interface.
>  	 */
>  	void (*ack_fault)(struct xe_pagefault *pf, int err);
> +
> +	/**
> +	 * @cleanup_fault: Cleanup for producer, if any
> +	 * @pf: Page fault
> +	 * @err: Error state of fault
> +	 *
> +	 * Page fault producer received cleanup request from consumer
> +	 */
> +	void (*cleanup_fault)(struct xe_pagefault *pf, int err);
>  };
>  
>  /**
> -- 
> 2.43.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface
  2026-02-27 22:10     ` Gwan-gyeong Mun
@ 2026-02-28  0:36       ` Matthew Brost
  0 siblings, 0 replies; 35+ messages in thread
From: Matthew Brost @ 2026-02-28  0:36 UTC (permalink / raw)
  To: Gwan-gyeong Mun
  Cc: Mika Kuoppala, intel-xe, simona.vetter, christian.koenig,
	thomas.hellstrom, joonas.lahtinen, christoph.manszewski,
	rodrigo.vivi, andrzej.hajda, matthew.auld, maciej.patelczyk,
	Jan Maślak

On Fri, Feb 27, 2026 at 02:10:26PM -0800, Gwan-gyeong Mun wrote:
> 
> 
> On 2/23/26 11:08 AM, Matthew Brost wrote:
> > On Mon, Feb 23, 2026 at 04:03:16PM +0200, Mika Kuoppala wrote:
> > > From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> > > 
> > 
> > Not a complete review but a few quick comments below.
> > 
> Thank you for your comments. I have left comments below for each point.
> 
> > > The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
> > > access will halt the corresponding EUs. To solve this problem, introduce
> > > EU pagefault handling functionality, which allows to unhalt pagefaulted
> > > eu threads and to EU debugger to get inform about the eu attentions state
> > > of EU threads during execution.
> > > 
> > > If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
> > > after handling the pagefault. The pagefault eudebug event follows
> > > the newly added drm_xe_eudebug_event_pagefault type.
> > > When a pagefault occurs, it prevents to send the
> > > DRM_XE_EUDEBUG_EVENT_EU_ATTENTION event to the client during pagefault
> > > handling.
> > > 
> > > The page fault event delivery follows the below policy.
> > > (1) If EU Debugger discovery has completed and pagefaulted eu threads turn
> > >      on attention bit then pagefault handler delivers pagefault event
> > >      directly.
> > > (2) If a pagefault occurs during eu debugger discovery process, pagefault
> > >      handler queues a pagefault event and sends the queued event when
> > >      discovery has completed and pagefaulted eu threads turn on attention
> > >      bit.
> > > (3) If the pagefaulted eu thread struggles to turn on the attention bit
> > >      within the specified time, the attention scan worker sends a pagefault
> > >      event when it detects that the attention bit is turned on.
> > > 
> > > If multiple eu threads are running and a pagefault occurs due to accessing
> > > the same invalid address, send a single pagefault event
> > > (DRM_XE_EUDEBUG_EVENT_PAGEFAULT type) to the user debugger instead of a
> > > pagefault event for each of the multiple eu threads.
> > > If eu threads (other than the one that caused the page fault before) access
> > > the new invalid addresses, send a new pagefault event.
> > > 
> > > As the attention scan worker send the eu attention event whenever the
> > > attention bit is turned on, user debugger receives attenion event
> > > immediately after pagefault event.
> > > In this case, the page-fault event always precedes the attention event.
> > > 
> > > When the user debugger receives an attention event after a pagefault event,
> > > it can detect whether additional breakpoints or interrupts occur in
> > > addition to the existing pagefault by comparing the eu threads where the
> > > pagefault occurred with the eu threads where the attention bit is newly
> > > enabled.
> > > 
> > > v2: use only force exception (Joonas, Mika)
> > > v3: rebased on v4 (Mika)
> > > v4: streamline uapi, cleanups (Mika)
> > > v5: struct member documentation (Mika)
> > > v6: fault to fault_type (Mika)
> > > 
> > > Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> > > Signed-off-by: Jan Maślak <jan.maslak@intel.com>
> > > Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
> > > ---
> > >   drivers/gpu/drm/xe/Makefile               |   2 +-
> > >   drivers/gpu/drm/xe/xe_eudebug.c           | 100 ++++-
> > >   drivers/gpu/drm/xe/xe_eudebug.h           |   9 +
> > >   drivers/gpu/drm/xe/xe_eudebug_hw.c        |  15 +-
> > >   drivers/gpu/drm/xe/xe_eudebug_pagefault.c | 440 ++++++++++++++++++++++
> > >   drivers/gpu/drm/xe/xe_eudebug_pagefault.h |  47 +++
> > >   drivers/gpu/drm/xe/xe_eudebug_types.h     |  69 +++-
> > >   drivers/gpu/drm/xe/xe_pagefault_types.h   |   4 +
> > >   include/uapi/drm/xe_drm_eudebug.h         |  12 +
> > >   9 files changed, 676 insertions(+), 22 deletions(-)
> > >   create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.c
> > >   create mode 100644 drivers/gpu/drm/xe/xe_eudebug_pagefault.h
> > > 
> > > diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
> > > index 34db797ef8fc..b49fe7ae18e7 100644
> > > --- a/drivers/gpu/drm/xe/Makefile
> > > +++ b/drivers/gpu/drm/xe/Makefile
> > > @@ -152,7 +152,7 @@ xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
> > >   xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o
> > >   # debugging shaders with gdb (eudebug) support
> > > -xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_gt_debug.o
> > > +xe-$(CONFIG_DRM_XE_EUDEBUG) += xe_eudebug.o xe_eudebug_vm.o xe_eudebug_hw.o xe_eudebug_pagefault.o xe_gt_debug.o
> > >   # graphics hardware monitoring (HWMON) support
> > >   xe-$(CONFIG_HWMON) += xe_hwmon.o
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug.c b/drivers/gpu/drm/xe/xe_eudebug.c
> > > index eae93c5f5e86..4b2f0dd9d234 100644
> > > --- a/drivers/gpu/drm/xe/xe_eudebug.c
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug.c
> > > @@ -17,12 +17,16 @@
> > >   #include "xe_eudebug.h"
> > >   #include "xe_eudebug_hw.h"
> > >   #include "xe_eudebug_types.h"
> > > +#include "xe_eudebug_pagefault.h"
> > >   #include "xe_eudebug_vm.h"
> > >   #include "xe_exec_queue.h"
> > > +#include "xe_force_wake.h"
> > >   #include "xe_gt.h"
> > >   #include "xe_hw_engine.h"
> > >   #include "xe_gt.h"
> > >   #include "xe_gt_debug.h"
> > > +#include "xe_gt_mcr.h"
> > > +#include "regs/xe_gt_regs.h"
> > >   #include "xe_macros.h"
> > >   #include "xe_pm.h"
> > >   #include "xe_sriov_pf.h"
> > > @@ -263,6 +267,7 @@ static void xe_eudebug_free(struct kref *ref)
> > >   	while (kfifo_get(&d->events.fifo, &event))
> > >   		kfree(event);
> > > +	xe_eudebug_pagefault_fini(d);
> > >   	xe_eudebug_resources_destroy(d);
> > >   	mutex_destroy(&d->target.lock);
> > >   	XE_WARN_ON(d->target.xef);
> > > @@ -461,7 +466,7 @@ static int _xe_eudebug_disconnect(struct xe_eudebug *d,
> > >   	} \
> > >   })
> > > -static struct xe_eudebug *
> > > +struct xe_eudebug *
> > >   xe_eudebug_get_nolock(struct xe_file *xef)
> > >   {
> > >   	struct xe_eudebug *d;
> > > @@ -1888,10 +1893,6 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
> > >   {
> > >   	int ret;
> > > -	ret = xe_gt_eu_threads_needing_attention(gt);
> > > -	if (ret <= 0)
> > > -		return ret;
> > > -
> > >   	ret = xe_send_gt_attention(gt);
> > >   	/* Discovery in progress, fake it */
> > > @@ -1901,6 +1902,65 @@ static int xe_eudebug_handle_gt_attention(struct xe_gt *gt)
> > >   	return ret;
> > >   }
> > > +int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
> > > +				    struct xe_eudebug_pagefault *pf)
> > > +{
> > > +	struct drm_xe_eudebug_event_pagefault *ep;
> > > +	struct drm_xe_eudebug_event *event;
> > > +	int h_queue, h_lrc;
> > > +	u32 size = xe_gt_eu_attention_bitmap_size(pf->q->gt) * 3;
> > > +	u32 sz = struct_size(ep, bitmask, size);
> > > +	int ret;
> > > +
> > > +	XE_WARN_ON(pf->lrc_idx < 0 || pf->lrc_idx >= pf->q->width);
> > > +
> > > +	XE_WARN_ON(!xe_exec_queue_is_debuggable(pf->q));
> > > +
> > > +	h_queue = find_handle(d, XE_EUDEBUG_RES_TYPE_EXEC_QUEUE, pf->q);
> > > +	if (h_queue < 0)
> > > +		return h_queue;
> > > +
> > > +	h_lrc = find_handle(d, XE_EUDEBUG_RES_TYPE_LRC, pf->q->lrc[pf->lrc_idx]);
> > > +	if (h_lrc < 0)
> > > +		return h_lrc;
> > > +
> > > +	event = xe_eudebug_create_event(d, DRM_XE_EUDEBUG_EVENT_PAGEFAULT, 0,
> > > +					DRM_XE_EUDEBUG_EVENT_STATE_CHANGE, sz);
> > > +
> > > +	if (!event)
> > > +		return -ENOSPC;
> > > +
> > > +	ep = cast_event(ep, event);
> > > +	ep->exec_queue_handle = h_queue;
> > > +	ep->lrc_handle = h_lrc;
> > > +	ep->bitmask_size = size;
> > > +	ep->pagefault_address = pf->fault.addr;
> > > +
> > > +	memcpy(ep->bitmask, pf->attentions.before.att, pf->attentions.before.size);
> > > +	memcpy(ep->bitmask + pf->attentions.before.size,
> > > +	       pf->attentions.after.att, pf->attentions.after.size);
> > > +	memcpy(ep->bitmask + pf->attentions.before.size + pf->attentions.after.size,
> > > +	       pf->attentions.resolved.att, pf->attentions.resolved.size);
> > > +
> > > +	event->seqno = atomic_long_inc_return(&d->events.seqno);
> > > +
> > > +	ret = xe_eudebug_queue_event(d, event);
> > > +	if (ret)
> > > +		xe_eudebug_disconnect(d, ret);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static void handle_attention_fail(struct xe_gt *gt, int gt_id, int ret)
> > > +{
> > > +	/* TODO: error capture */
> > > +	drm_info(&gt_to_xe(gt)->drm,
> > > +		 "gt:%d unable to handle eu attention ret = %d\n",
> > > +		 gt_id, ret);
> > > +
> > > +	xe_gt_reset_async(gt);
> > > +}
> > > +
> > >   static void attention_poll_work(struct work_struct *work)
> > >   {
> > >   	struct xe_device *xe = container_of(work, typeof(*xe),
> > > @@ -1923,15 +1983,15 @@ static void attention_poll_work(struct work_struct *work)
> > >   			if (gt->info.type != XE_GT_TYPE_MAIN)
> > >   				continue;
> > > -			ret = xe_eudebug_handle_gt_attention(gt);
> > > -			if (ret) {
> > > -				/* TODO: error capture */
> > > -				drm_info(&gt_to_xe(gt)->drm,
> > > -					 "gt:%d unable to handle eu attention ret=%d\n",
> > > -					 gt_id, ret);
> > > +			if (!xe_gt_eu_threads_needing_attention(gt))
> > > +				continue;
> > > +
> > > +			ret = xe_eudebug_handle_pagefaults(gt);
> > > +			if (!ret)
> > > +				ret = xe_eudebug_handle_gt_attention(gt);
> > > -				xe_gt_reset_async(gt);
> > > -			}
> > > +			if (ret)
> > > +				handle_attention_fail(gt, gt_id, ret);
> > >   		}
> > >   		xe_pm_runtime_put(xe);
> > > @@ -1940,12 +2000,12 @@ static void attention_poll_work(struct work_struct *work)
> > >   	schedule_delayed_work(&xe->eudebug.attention_dwork, delay);
> > >   }
> > > -static void attention_poll_stop(struct xe_device *xe)
> > > +void xe_eudebug_attention_poll_stop(struct xe_device *xe)
> > >   {
> > >   	cancel_delayed_work_sync(&xe->eudebug.attention_dwork);
> > >   }
> > > -static void attention_poll_start(struct xe_device *xe)
> > > +void xe_eudebug_attention_poll_start(struct xe_device *xe)
> > >   {
> > >   	mod_delayed_work(system_wq, &xe->eudebug.attention_dwork, 0);
> > >   }
> > > @@ -1988,6 +2048,8 @@ xe_eudebug_connect(struct xe_device *xe,
> > >   	kref_init(&d->ref);
> > >   	mutex_init(&d->target.lock);
> > > +	mutex_init(&d->pf_lock);
> > > +	INIT_LIST_HEAD(&d->pagefaults);
> > >   	init_waitqueue_head(&d->events.write_done);
> > >   	init_waitqueue_head(&d->events.read_done);
> > >   	init_completion(&d->discovery);
> > > @@ -2019,7 +2081,7 @@ xe_eudebug_connect(struct xe_device *xe,
> > >   	kref_get(&d->ref);
> > >   	queue_work(xe->eudebug.wq, &d->discovery_work);
> > > -	attention_poll_start(xe);
> > > +	xe_eudebug_attention_poll_start(xe);
> > >   	eu_dbg(d, "connected session %lld", d->session);
> > > @@ -2098,9 +2160,9 @@ int xe_eudebug_enable(struct xe_device *xe, bool enable)
> > >   	mutex_unlock(&xe->eudebug.lock);
> > >   	if (enable) {
> > > -		attention_poll_start(xe);
> > > +		xe_eudebug_attention_poll_start(xe);
> > >   	} else {
> > > -		attention_poll_stop(xe);
> > > +		xe_eudebug_attention_poll_stop(xe);
> > >   		if (IS_SRIOV_PF(xe))
> > >   			xe_sriov_pf_end_lockdown(xe);
> > > @@ -2153,7 +2215,7 @@ static void xe_eudebug_fini(struct drm_device *dev, void *__unused)
> > >   	xe_assert(xe, list_empty(&xe->eudebug.targets));
> > > -	attention_poll_stop(xe);
> > > +	xe_eudebug_attention_poll_stop(xe);
> > >   }
> > >   void xe_eudebug_init(struct xe_device *xe)
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug.h b/drivers/gpu/drm/xe/xe_eudebug.h
> > > index bd9fd7bf454f..34938e87be13 100644
> > > --- a/drivers/gpu/drm/xe/xe_eudebug.h
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug.h
> > > @@ -13,12 +13,14 @@ struct drm_file;
> > >   struct xe_debug_data;
> > >   struct xe_device;
> > >   struct xe_file;
> > > +struct xe_gt;
> > >   struct xe_vm;
> > >   struct xe_vma;
> > >   struct xe_vma_ops;
> > >   struct xe_exec_queue;
> > >   struct xe_user_fence;
> > >   struct xe_eudebug;
> > > +struct xe_eudebug_pagefault;
> > >   #if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> > > @@ -72,8 +74,15 @@ void xe_eudebug_ufence_init(struct xe_user_fence *ufence);
> > >   void xe_eudebug_ufence_fini(struct xe_user_fence *ufence);
> > >   bool xe_eudebug_ufence_track(struct xe_user_fence *ufence);
> > > +struct xe_eudebug *xe_eudebug_get_nolock(struct xe_file *xef);
> > >   void xe_eudebug_put(struct xe_eudebug *d);
> > > +int xe_eudebug_send_pagefault_event(struct xe_eudebug *d,
> > > +				    struct xe_eudebug_pagefault *pf);
> > > +
> > > +void xe_eudebug_attention_poll_stop(struct xe_device *xe);
> > > +void xe_eudebug_attention_poll_start(struct xe_device *xe);
> > > +
> > >   #else
> > >   static inline int xe_eudebug_connect_ioctl(struct drm_device *dev,
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug_hw.c b/drivers/gpu/drm/xe/xe_eudebug_hw.c
> > > index 5365265a67b3..270f7abc82e9 100644
> > > --- a/drivers/gpu/drm/xe/xe_eudebug_hw.c
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug_hw.c
> > > @@ -322,6 +322,7 @@ static int do_eu_control(struct xe_eudebug *d,
> > >   	struct xe_device *xe = d->xe;
> > >   	u8 *bits = NULL;
> > >   	unsigned int hw_attn_size, attn_size;
> > > +	struct dma_fence *pf_fence;
> > >   	struct xe_exec_queue *q;
> > >   	struct xe_lrc *lrc;
> > >   	u64 seqno;
> > > @@ -376,8 +377,20 @@ static int do_eu_control(struct xe_eudebug *d,
> > >   		goto out_free;
> > >   	}
> > > -	ret = -EINVAL;
> > >   	mutex_lock(&d->hw.lock);
> > > +	do {
> > > +		pf_fence = dma_fence_get(d->pf_fence);
> > > +		if (pf_fence) {
> > > +			mutex_unlock(&d->hw.lock);
> > > +			ret = dma_fence_wait(pf_fence, true);
> > > +			dma_fence_put(pf_fence);
> > > +			if (ret)
> > > +				goto out_free;
> > > +			mutex_lock(&d->hw.lock);
> > > +		}
> > > +	} while (pf_fence);
> > > +
> > > +	ret = -EINVAL;
> > >   	switch (arg->cmd) {
> > >   	case DRM_XE_EUDEBUG_EU_CONTROL_CMD_INTERRUPT_ALL:
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.c b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
> > > new file mode 100644
> > > index 000000000000..edd368a7f6ae
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.c
> > > @@ -0,0 +1,440 @@
> > > +// SPDX-License-Identifier: MIT
> > > +/*
> > > + * Copyright © 2023-2025 Intel Corporation
> > > + */
> > > +
> > > +#include "xe_eudebug_pagefault.h"
> > > +
> > > +#include <linux/delay.h>
> > > +
> > > +#include "xe_exec_queue.h"
> > > +#include "xe_eudebug.h"
> > > +#include "xe_eudebug_hw.h"
> > > +#include "xe_force_wake.h"
> > > +#include "xe_gt_debug.h"
> > > +#include "xe_gt_mcr.h"
> > > +#include "regs/xe_gt_regs.h"
> > > +#include "xe_vm.h"
> > > +
> > > +static struct xe_gt *
> > > +pf_to_gt(struct xe_eudebug_pagefault *pf)
> > > +{
> > > +	return pf->q->gt;
> > > +}
> > > +
> > > +static void destroy_pagefault(struct xe_eudebug_pagefault *pf)
> > > +{
> > > +	xe_exec_queue_put(pf->q);
> > > +	kfree(pf);
> > > +}
> > > +
> > > +static int queue_pagefault(struct xe_eudebug_pagefault *pf)
> > > +{
> > > +	struct xe_eudebug *d;
> > > +
> > > +	d = xe_eudebug_get_nolock(pf->q->vm->xef);
> > > +	if (!d)
> > > +		return -EINVAL;
> > > +
> > > +	mutex_lock(&d->pf_lock);
> > > +	list_add_tail(&pf->link, &d->pagefaults);
> > > +	mutex_unlock(&d->pf_lock);
> > > +
> > > +	xe_eudebug_put(d);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int send_pagefault(struct xe_eudebug_pagefault *pf,
> > > +			  bool from_attention_scan)
> > > +{
> > > +	struct xe_gt *gt = pf_to_gt(pf);
> > > +	struct xe_eudebug *d;
> > > +	struct xe_exec_queue *q;
> > > +	int ret, lrc_idx;
> > > +
> > > +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
> > > +	if (IS_ERR(q))
> > > +		return PTR_ERR(q);
> > > +
> > > +	if (!xe_exec_queue_is_debuggable(q)) {
> > > +		ret = -EPERM;
> > > +		goto out_exec_queue_put;
> > > +	}
> > > +
> > > +	d = xe_eudebug_get_nolock(q->vm->xef);
> > > +	if (!d) {
> > > +		ret = -ENOTCONN;
> > > +		goto out_exec_queue_put;
> > > +	}
> > > +
> > > +	if (pf->deferred_resolved) {
> > > +		xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
> > > +					 XE_GT_ATTENTION_TIMEOUT_MS);
> > > +
> > > +		if (!xe_eu_attentions_xor_count(&pf->attentions.after,
> > > +						&pf->attentions.resolved) &&
> > > +		    !from_attention_scan) {
> > > +			eu_dbg(d, "xe attentions not yet updated\n");
> > > +			ret = -EBUSY;
> > > +			goto out_eudebug_put;
> > > +		}
> > > +	}
> > > +
> > > +	ret = xe_eudebug_send_pagefault_event(d, pf);
> > > +
> > > +out_eudebug_put:
> > > +	xe_eudebug_put(d);
> > > +out_exec_queue_put:
> > > +	xe_exec_queue_put(q);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const char *
> > > +pagefault_get_driver_name(struct dma_fence *dma_fence)
> > > +{
> > > +	return "xe";
> > > +}
> > > +
> > > +static const char *
> > > +pagefault_fence_get_timeline_name(struct dma_fence *dma_fence)
> > > +{
> > > +	return "eudebug_pagefault_fence";
> > > +}
> > > +
> > > +static const struct dma_fence_ops pagefault_fence_ops = {
> > > +	.get_driver_name = pagefault_get_driver_name,
> > > +	.get_timeline_name = pagefault_fence_get_timeline_name,
> > > +};
> > > +
> > > +struct pagefault_fence {
> > > +	struct dma_fence base;
> > > +	spinlock_t lock;
> > > +};
> > > +
> > > +static struct pagefault_fence *pagefault_fence_create(void)
> > > +{
> > > +	struct pagefault_fence *fence;
> > > +
> > > +	fence = kzalloc_obj(*fence, GFP_KERNEL);
> > > +	if (fence == NULL)
> > > +		return NULL;
> > > +
> > > +	spin_lock_init(&fence->lock);
> > > +	dma_fence_init(&fence->base, &pagefault_fence_ops, &fence->lock,
> > > +		       dma_fence_context_alloc(1), 1);
> > > +
> > > +	return fence;
> > > +}
> > > +
> > > +void
> > > +xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
> > 
> > This function, as written, is basically a no from me given that
> > DRM_XE_EUDEBUG is enabled by default. It adds time complexity via
> > xe_vm_find_vma_by_addr(), which is O(log N) where N is the number of
> > VMAs.
> > 
> > Page faults are going to be heavily optimized since this is a critical
> > path. Anything less than O(1) here when no EU connection exists —
> > combined with DRM_XE_EUDEBUG being on — is likely to receive pushback
> > from me.
> > 
> I'll consider an implementation where eudebug directly uses the vma value
> returned by xe_vm_find_vma_by_addr(), which is called by
> xe_pagefault_service(). this way will avoid the performance degradation
> caused by additional xe_vm_find_vma_by_addr() calls. ( Previously, due to
> lock dependencies, eudebug directly called xe_vm_find_vma_by_addr(). I will
> verify whether this issue still exists. )
> 

Yes, this would work for me. 

> > > +{
> > > +	struct pagefault_fence *pf_fence;
> > > +	struct xe_eudebug_pagefault *epf;
> > > +	struct xe_vma *vma;
> > > +	struct xe_gt *gt = pf->gt;
> > > +	struct xe_exec_queue *q;
> > > +	struct dma_fence *fence;
> > > +	struct xe_eudebug *d;
> > > +	unsigned int fw_ref;
> > > +	int lrc_idx;
> > > +	u32 td_ctl;
> > > +
> > > +	pf->consumer.epf = NULL;
> > > +
> > > +	down_read(&vm->lock);
> > > +	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
> > > +	up_read(&vm->lock);
> > 
> > See my comment in [1] — this doesn't work for SVM. This will need to be
> > rethought.
> > 
> > [1] https://patchwork.freedesktop.org/patch/706437/?series=161979&rev=1#comment_1299420
> > 
> Additional implementation of eudebug pagefault routine for  SVM is required.
> I have replied to the mentioned email thread.
> 

Reading this one and thinking this through.

Matt

> > > +
> > > +	if (vma)
> > > +		return;
> > > +
> > > +	d = xe_eudebug_get_nolock(vm->xef);
> > > +	if (!d)
> > > +		return;
> > > +
> > > +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
> > > +	if (IS_ERR(q))
> > > +		goto err_put_eudebug;
> > > +
> > > +	if (XE_WARN_ON(q->vm != vm))
> > > +		goto err_put_exec_queue;
> > > +
> > > +	if (!xe_exec_queue_is_debuggable(q))
> > > +		goto err_put_exec_queue;
> > > +
> > > +	fw_ref = xe_force_wake_get(gt_to_fw(gt), q->hwe->domain);
> > > +	if (!fw_ref)
> > > +		goto err_put_exec_queue;
> > > +
> > > +	/*
> > > +	 * If there is no debug functionality (TD_CTL_GLOBAL_DEBUG_ENABLE, etc.),
> > > +	 * don't proceed pagefault routine for eu debugger.
> > > +	 */
> > > +	td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
> > > +	if (!td_ctl)
> > > +		goto err_put_fw;
> > > +
> > > +	epf = kzalloc_obj(*epf, GFP_KERNEL);
> > > +	if (!epf)
> > > +		goto err_put_fw;
> > > +
> > > +	xe_eudebug_attention_poll_stop(gt_to_xe(gt));
> > > +
> > > +	mutex_lock(&d->hw.lock);
> > > +	fence = dma_fence_get(d->pf_fence);
> > > +
> > > +	if (fence) {
> > > +		/*
> > > +		 * TODO: If the new incoming pagefaulted address is different
> > > +		 * from the pagefaulted address it is currently handling on the
> > > +		 * same ASID, it needs a routine to wait here and then do the
> > > +		 * following pagefault.
> > > +		 */
> > > +		dma_fence_put(fence);
> > > +		goto err_unlock_hw_lock;
> > > +	}
> > > +
> > > +	pf_fence = pagefault_fence_create();
> > > +	if (!pf_fence)
> > > +		goto err_unlock_hw_lock;
> > > +
> > > +	d->pf_fence = &pf_fence->base;
> > > +
> > > +	INIT_LIST_HEAD(&epf->link);
> > > +
> > > +	xe_gt_eu_attentions_read(gt, &epf->attentions.before, 0);
> > > +
> > > +	if (td_ctl & TD_CTL_FORCE_EXCEPTION)
> > > +		eu_warn(d, "force exception already set!");
> > > +
> > > +	/* Halt regardless of thread dependencies */
> > > +	while (!(td_ctl & TD_CTL_FORCE_EXCEPTION)) {
> > > +		xe_gt_mcr_multicast_write(gt, TD_CTL,
> > > +					  td_ctl | TD_CTL_FORCE_EXCEPTION);
> > > +		udelay(200);
> > > +		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
> > > +	}
> > > +
> > > +	xe_gt_eu_attentions_read(gt, &epf->attentions.after,
> > > +				 XE_GT_ATTENTION_TIMEOUT_MS);
> > > +
> > > +	mutex_unlock(&d->hw.lock);
> > > +
> > > +	/*
> > > +	 * xe_exec_queue_put() will be called from xe_eudebug_pagefault_destroy()
> > > +	 * or handle_pagefault()
> > > +	 */
> > > +	epf->q = q;
> > > +	epf->lrc_idx = lrc_idx;
> > > +	epf->fault.addr = pf->consumer.page_addr;
> > > +	epf->fault.type_level = pf->consumer.fault_type_level;
> > > +	epf->fault.access_type = pf->consumer.access_type;
> > > +
> > > +	pf->consumer.epf = epf;
> > > +
> > > +	xe_force_wake_put(gt_to_fw(gt), fw_ref);
> > > +	xe_eudebug_put(d);
> > > +
> > > +	return;
> > > +
> > > +err_unlock_hw_lock:
> > > +	mutex_unlock(&d->hw.lock);
> > > +	xe_eudebug_attention_poll_start(gt_to_xe(gt));
> > > +	kfree(epf);
> > > +err_put_fw:
> > > +	xe_force_wake_put(gt_to_fw(gt), fw_ref);
> > > +err_put_exec_queue:
> > > +	xe_exec_queue_put(q);
> > > +err_put_eudebug:
> > > +	xe_eudebug_put(d);
> > > +}
> > > +
> > > +struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
> > > +{
> > > +	struct xe_vma *vma = NULL;
> > > +
> > > +	if (!pf->consumer.epf)
> > > +		return NULL;
> > > +
> > > +	vma = xe_vm_create_null_vma(vm, pf->consumer.page_addr);
> > > +	if (IS_ERR(vma))
> > > +		return vma;
> > > +
> > > +	pf->consumer.epf->is_null = true;
> > > +
> > > +	return vma;
> > > +}
> > > +
> > > +static void
> > > +xe_eudebug_pagefault_process(struct xe_eudebug_pagefault *pf)
> > > +{
> > > +	struct xe_gt *gt = pf->q->gt;
> > > +
> > > +	xe_gt_eu_attentions_read(gt, &pf->attentions.resolved,
> > > +				 XE_GT_ATTENTION_TIMEOUT_MS);
> > > +
> > > +	if (!xe_eu_attentions_xor_count(&pf->attentions.after,
> > > +					&pf->attentions.resolved))
> > > +		pf->deferred_resolved = true;
> > > +}
> > > +
> > > +static void
> > > +_xe_eudebug_pagefault_destroy(struct xe_eudebug_pagefault *pf)
> > > +{
> > > +	struct xe_gt *gt = pf->q->gt;
> > > +	struct xe_vm *vm = pf->q->vm;
> > > +	struct xe_eudebug *d;
> > > +	unsigned int fw_ref;
> > > +	u32 td_ctl;
> > > +	bool queued, try_send;
> > > +	int ret;
> > > +
> > > +	fw_ref = xe_force_wake_get(gt_to_fw(gt), pf->q->hwe->domain);
> > > +	if (!fw_ref) {
> > > +		struct xe_device *xe = gt_to_xe(gt);
> > > +
> > > +		drm_warn(&xe->drm, "Forcewake fail: Can not recover TD_CTL");
> > > +	} else {
> > > +		td_ctl = xe_gt_mcr_unicast_read_any(gt, TD_CTL);
> > > +		xe_gt_mcr_multicast_write(gt, TD_CTL, td_ctl &
> > > +					  ~(TD_CTL_FORCE_EXCEPTION));
> > > +		xe_force_wake_put(gt_to_fw(gt), fw_ref);
> > > +	}
> > > +
> > > +	queued = false;
> > > +	try_send = pf->is_null;
> > > +	if (try_send) {
> > > +		ret = send_pagefault(pf, false);
> > > +
> > > +		/*
> > > +		 * if debugger discovery is not completed or resolved attentions are not
> > > +		 * updated, then queue pagefault
> > > +		 */
> > > +		if (ret == -EBUSY) {
> > > +			ret = queue_pagefault(pf);
> > > +			if (!ret)
> > > +				queued = true;
> > > +		}
> > > +	}
> > > +
> > > +	d = xe_eudebug_get_nolock(vm->xef);
> > > +	if (d) {
> > > +		struct dma_fence *f;
> > > +
> > > +		mutex_lock(&d->hw.lock);
> > > +		f = d->pf_fence;
> > > +		d->pf_fence = NULL;
> > > +		mutex_unlock(&d->hw.lock);
> > > +
> > > +		if (f) {
> > > +			if (!queued)
> > > +				dma_fence_signal(f);
> > > +
> > > +			dma_fence_put(f);
> > > +		}
> > > +
> > > +		xe_eudebug_put(d);
> > > +	}
> > > +
> > > +	if (!queued)
> > > +		destroy_pagefault(pf);
> > > +
> > > +	xe_eudebug_attention_poll_start(gt_to_xe(gt));
> > > +}
> > > +
> > > +static int send_queued_pagefaults(struct xe_eudebug *d)
> > > +{
> > > +	struct xe_eudebug_pagefault *pf, *pf_temp;
> > > +	int ret = 0;
> > > +
> > > +	mutex_lock(&d->pf_lock);
> > > +	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
> > > +		ret = send_pagefault(pf, true);
> > > +
> > > +		/* if resolved attentions are not updated */
> > > +		if (ret == -EBUSY)
> > > +			break;
> > > +
> > > +		list_del(&pf->link);
> > > +
> > > +		destroy_pagefault(pf);
> > > +
> > > +		if (ret)
> > > +			break;
> > > +	}
> > > +	mutex_unlock(&d->pf_lock);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +int xe_eudebug_handle_pagefaults(struct xe_gt *gt)
> > > +{
> > > +	struct xe_exec_queue *q;
> > > +	struct xe_eudebug *d;
> > > +	int ret, lrc_idx;
> > > +
> > > +	q = xe_gt_runalone_active_queue_get(gt, &lrc_idx);
> > > +	if (IS_ERR(q))
> > > +		return PTR_ERR(q);
> > > +
> > > +	if (!xe_exec_queue_is_debuggable(q)) {
> > > +		ret = -EPERM;
> > > +		goto out_exec_queue_put;
> > > +	}
> > > +
> > > +	d = xe_eudebug_get_nolock(q->vm->xef);
> > > +	if (!d) {
> > > +		ret = -ENOTCONN;
> > > +		goto out_exec_queue_put;
> > > +	}
> > > +
> > > +	ret = send_queued_pagefaults(d);
> > > +
> > > +	xe_eudebug_put(d);
> > > +
> > > +out_exec_queue_put:
> > > +	xe_exec_queue_put(q);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
> > > +{
> > > +	struct xe_eudebug_pagefault *f = pf->consumer.epf;
> > > +
> > > +	if (!f)
> > > +		return;
> > > +
> > > +	if (f->is_null)
> > > +		xe_eudebug_pagefault_process(f);
> > > +}
> > > +
> > > +void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
> > > +{
> > > +	struct xe_eudebug_pagefault *f = pf->consumer.epf;
> > > +
> > > +	if (!f)
> > > +		return;
> > > +
> > > +	if (err)
> > > +		f->is_null = false;
> > > +
> > > +	_xe_eudebug_pagefault_destroy(f);
> > > +}
> > > +
> > > +void xe_eudebug_pagefault_fini(struct xe_eudebug *d)
> > > +{
> > > +	struct xe_eudebug_pagefault *pf, *pf_temp;
> > > +
> > > +	/* Since it's the last reference no race here */
> > > +
> > > +	list_for_each_entry_safe(pf, pf_temp, &d->pagefaults, link) {
> > > +		list_del(&pf->link);
> > > +		destroy_pagefault(pf);
> > > +	}
> > > +
> > > +	XE_WARN_ON(d->pf_fence);
> > > +}
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug_pagefault.h b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
> > > new file mode 100644
> > > index 000000000000..1ba20beac3cf
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug_pagefault.h
> > > @@ -0,0 +1,47 @@
> > > +/* SPDX-License-Identifier: MIT */
> > > +/*
> > > + * Copyright © 2023-2025 Intel Corporation
> > > + */
> > > +
> > > +#ifndef _XE_EUDEBUG_PAGEFAULT_H_
> > > +#define _XE_EUDEBUG_PAGEFAULT_H_
> > > +
> > > +#include <linux/types.h>
> > > +
> > > +struct xe_eudebug;
> > > +struct xe_gt;
> > > +struct xe_pagefault;
> > > +struct xe_eudebug_pagefault;
> > > +struct xe_vm;
> > > +
> > > +void xe_eudebug_pagefault_fini(struct xe_eudebug *d);
> > > +int xe_eudebug_handle_pagefaults(struct xe_gt *gt);
> > > +
> > > +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> > > +void xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf);
> > > +struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf);
> > > +void xe_eudebug_pagefault_service(struct xe_pagefault *pf);
> > > +void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err);
> > > +#else
> > > +
> > > +static inline void
> > > +xe_eudebug_pagefault_create(struct xe_vm *vm, struct xe_pagefault *pf)
> > > +{
> > > +}
> > > +
> > > +static inline struct xe_vma *xe_eudebug_create_vma(struct xe_vm *vm, struct xe_pagefault *pf)
> > > +{
> > > +	return NULL;
> > > +}
> > > +
> > > +static inline void xe_eudebug_pagefault_service(struct xe_pagefault *pf)
> > > +{
> > > +}
> > > +
> > > +static inline void xe_eudebug_pagefault_destroy(struct xe_pagefault *pf, int err)
> > > +{
> > > +}
> > > +
> > > +#endif
> > > +
> > > +#endif /* _XE_EUDEBUG_PAGEFAULT_H_ */
> > > diff --git a/drivers/gpu/drm/xe/xe_eudebug_types.h b/drivers/gpu/drm/xe/xe_eudebug_types.h
> > > index 386b5c78ecff..09bfae8b94ab 100644
> > > --- a/drivers/gpu/drm/xe/xe_eudebug_types.h
> > > +++ b/drivers/gpu/drm/xe/xe_eudebug_types.h
> > > @@ -15,6 +15,8 @@
> > >   #include <linux/wait.h>
> > >   #include <linux/xarray.h>
> > > +#include "xe_gt_debug_types.h"
> > > +
> > >   struct xe_device;
> > >   struct task_struct;
> > >   struct xe_eudebug;
> > > @@ -37,7 +39,7 @@ enum xe_eudebug_state {
> > >   };
> > >   #define CONFIG_DRM_XE_DEBUGGER_EVENT_QUEUE_SIZE 64
> > > -#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_EU_ATTENTION
> > > +#define XE_EUDEBUG_MAX_EVENT_TYPE DRM_XE_EUDEBUG_EVENT_PAGEFAULT
> > >   /**
> > >    * struct xe_eudebug_handle - eudebug resource handle
> > > @@ -164,6 +166,71 @@ struct xe_eudebug {
> > >   	/** @ops: operations for eu_control */
> > >   	struct xe_eudebug_eu_control_ops *ops;
> > > +
> > > +	/** @pf_lock: guards access to pagefaults list*/
> > > +	struct mutex pf_lock;
> > > +	/** @pagefaults: xe_eudebug_pagefault list for pagefault event queuing */
> > > +	struct list_head pagefaults;
> > > +	/**
> > > +	 * @pf_fence: fence on operations of eus (eu thread control and attention)
> > > +	 * when page faults are being handled, protected by @eu_lock.
> > > +	 */
> > > +	struct dma_fence *pf_fence;
> > > +};
> > > +
> > > +/**
> > > + * struct xe_eudebug_pagefault - eudebug structure for queuing pagefault
> > > + */
> > > +struct xe_eudebug_pagefault {
> > > +	/** @link: link into the xe_eudebug.pagefaults */
> > > +	struct list_head link;
> > > +	/** @q: exec_queue which raised pagefault */
> > > +	struct xe_exec_queue *q;
> > > +	/** @lrc_idx: lrc index of the workload which raised pagefault */
> > > +	int lrc_idx;
> > > +
> > > +	/** @fault: pagefault raw partial data passed from guc */
> > > +	struct {
> > > +		/** @addr: ppgtt address where the pagefault occurred */
> > > +		u64 addr;
> > > +		u8 type_level;
> > > +		u8 access_type;
> > > +	} fault;
> > > +
> > > +	/** @attentions: attention states in different phases of fault */
> > > +	struct {
> > > +		/** @before: state of attention bits before page fault WA processing*/
> > > +		struct xe_eu_attentions before;
> > > +		/**
> > > +		 * @after: status of attention bits during page fault WA processing.
> > > +		 * It includes eu threads where attention bits are turned on for
> > > +		 * reasons other than page fault WA (breakpoint, interrupt, etc.).
> > > +		 */
> > > +		struct xe_eu_attentions after;
> > > +		/**
> > > +		 * @resolved: state of the attention bits after page fault WA.
> > > +		 * It includes the eu thread that caused the page fault.
> > > +		 * To determine the eu thread that caused the page fault,
> > > +		 * do XOR attentions.after and attentions.resolved.
> > > +		 */
> > > +		struct xe_eu_attentions resolved;
> > > +	} attentions;
> > > +
> > > +	/**
> > > +	 * @deferred_resolved: to update attentions.resolved again when attention
> > > +	 * bits are ready if the eu thread fails to turn on attention bits within
> > > +	 * a certain time after page fault WA processing.
> > > +	 */
> > > +	bool deferred_resolved;
> > > +
> > > +	/**
> > > +	 * @is_null: marks if this vma is null or not. The lookup for the
> > > +	 * vma is done in two phases and eudebug pagefault struct needs
> > > +	 * to be allocated apriori to resolving if we need null vma or not.
> > > +	 * So we keep the state here so that processing and teardown
> > > +	 * know which type of fault resulted in creation of this eudebug pf.
> > > +	 */
> > > +	bool is_null;
> > >   };
> > >   #endif /* _XE_EUDEBUG_TYPES_H_ */
> > > diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
> > > index 0e378f41ede6..2bee858da597 100644
> > > --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
> > > +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
> > > @@ -10,6 +10,7 @@
> > >   struct xe_gt;
> > >   struct xe_pagefault;
> > > +struct xe_eudebug_pagefault;
> > >   /** enum xe_pagefault_access_type - Xe page fault access type */
> > >   enum xe_pagefault_access_type {
> > > @@ -84,6 +85,9 @@ struct xe_pagefault {
> > >   		u8 engine_class;
> > >   		/** @consumer.engine_instance: engine instance */
> > >   		u8 engine_instance;
> > > +#if IS_ENABLED(CONFIG_DRM_XE_EUDEBUG)
> > > +		struct xe_eudebug_pagefault *epf;
> > > +#endif
> > 
> > 
> > This will grow the pagefault struct from 64 bytes to 128 bytes.
> > Everything will still be functionally correct, but I’d really prefer not
> > to increase the size of this structure. The u64 reserved field will be
> > used to implement the page-fault cache for fault storms, so that is a
> > non-starter.
> > 
> > Can we replace producer->private with epf and set a mask bit in the
> > lower 3 bits to indicate that producer->private has been replaced by
> > epf, then unwind epf vs. the original private on the producer side
> > during the ack/cleanup? In that case, we would store the original
> > producer->private in epf, if that isn’t clear.
> > 
> Thank you for your feedback. It seems I can change the implementation to
> store the epf in producer->private. I will incorporate this change in the
> next version.
> 
> > Another thing we will have to consider is how the EU debug interface for
> > page faults will interact with the pagefault cache for fault storms
> > that’s in the pipe [2] (which I’ll post as soon as CI is fixed). My
> > initial thought is that it should be fine, given that the head of a
> > fault storm will populate epf, and subsequent faults that hit the page
> > being serviced will not have it populated. I’ll CC the EU debug team
> > when I post this code to ensure we aren’t clobbering each other’s
> > designs.
> > 
> > [2] https://gitlab.freedesktop.org/mbrost/xe-kernel-driver-svn-perf-6-15-2025/-/commit/93669c7f4e00ec13d0a18e28d34dfcb41803b7c9
> > 
> Yes, I've checked your patch series.
> https://patchwork.freedesktop.org/series/162167/
> 
> The eudebug pagefault handling routine does not appear to conflict
> structurally with the pagefault cache for fault storms. After verifying the
> behavior of applying the eudebug changes on top of your relevant patch, I
> will provide an additional reply.
> 
> G.G.
> 
> > Matt
> > 
> > >   		/** consumer.reserved: reserved bits for future expansion */
> > >   		u64 reserved;
> > >   	} consumer;
> > > diff --git a/include/uapi/drm/xe_drm_eudebug.h b/include/uapi/drm/xe_drm_eudebug.h
> > > index 54394a7e12ab..f7d035532be2 100644
> > > --- a/include/uapi/drm/xe_drm_eudebug.h
> > > +++ b/include/uapi/drm/xe_drm_eudebug.h
> > > @@ -53,6 +53,7 @@ struct drm_xe_eudebug_event {
> > >   #define DRM_XE_EUDEBUG_EVENT_VM_BIND_OP_DEBUG_DATA	5
> > >   #define DRM_XE_EUDEBUG_EVENT_VM_BIND_UFENCE	6
> > >   #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION	7
> > > +#define DRM_XE_EUDEBUG_EVENT_PAGEFAULT		8
> > >   	/** @flags: Flags */
> > >   	__u16 flags;
> > > @@ -358,6 +359,17 @@ struct drm_xe_eudebug_event_eu_attention {
> > >   	__u8 bitmask[];
> > >   };
> > > +struct drm_xe_eudebug_event_pagefault {
> > > +	struct drm_xe_eudebug_event base;
> > > +
> > > +	__u64 exec_queue_handle;
> > > +	__u64 lrc_handle;
> > > +	__u32 flags;
> > > +	__u32 bitmask_size;
> > > +	__u64 pagefault_address;
> > > +	__u8 bitmask[];
> > > +};
> > > +
> > >   #if defined(__cplusplus)
> > >   }
> > >   #endif
> > > -- 
> > > 2.43.0
> > > 
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling
  2026-02-27 23:11   ` Gustavo Sousa
@ 2026-02-28  6:49     ` Gwan-gyeong Mun
  0 siblings, 0 replies; 35+ messages in thread
From: Gwan-gyeong Mun @ 2026-02-28  6:49 UTC (permalink / raw)
  To: Gustavo Sousa, Mika Kuoppala, intel-xe
  Cc: simona.vetter, matthew.brost, christian.koenig, thomas.hellstrom,
	joonas.lahtinen, christoph.manszewski, rodrigo.vivi,
	andrzej.hajda, matthew.auld, maciej.patelczyk



On 2/27/26 3:11 PM, Gustavo Sousa wrote:
> Mika Kuoppala <mika.kuoppala@linux.intel.com> writes:
> 
>> From: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>>
>> The XE2 (and PVC) HW has a limitation that the pagefault due to invalid
>> access will halt the corresponding EUs. To solve this problem, enable
>> EU pagefault handling functionality, which allows to unhalt pagefaulted
>> eu threads and to EU debugger to get inform about the eu attentions state
>> of EU threads during execution.
>>
>> If a pagefault occurs, send the DRM_XE_EUDEBUG_EVENT_PAGEFAULT event
>> after handling the pagefault.
>>
>> The pagefault handling is a mechanism that allows a stalled EU thread to
>> enter SIP mode by installing a temporal null page to the page table entry
>> where the pagefault happened.
>>
>> A brief description of the page fault handling mechanism flow between KMD
>> and the eu thread is as follows
>>
>> (1) eu thread accesses unallocated address
>> (2) pagefault happens and eu thread stalls
>> (3) XE kmd set an force eu thread exception to allow the running eu thread
>>      to enter SIP mode (kmd set ForceException / ForceExternalHalt bit of
>>      TD_CTL register)
>>      Not stalled (none-pagefaulted) eu threads enter SIP mode
>> (4) XE kmd installs temporal null page to the pagetable entry of the
>>      address where pagefault happened.
>> (5) XE kmd replies pagefault successful message to GUC
>> (6) stalled eu thread resumes as per pagefault condition has resolved
>> (7) resumed eu thread enters SIP mode due to force exception set by (3)
>> (8) adapted to consumer/produced pagefaults
>>
>> As designed this feature to only work when eudbug is enabled, it should
>> have no impact to regular recoverable pagefault code path.
>>
>> v2: - pf->q holds the vm ref so drop it (Mika)
>>      - streamline uapi (Mika)
>>      - cleanup the pagefault through producer if (Mika)
>>
>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>> Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
>> ---
>>   drivers/gpu/drm/xe/xe_guc_pagefault.c   |  8 +++++++
>>   drivers/gpu/drm/xe/xe_pagefault.c       | 31 ++++++++++++++++++++++++-
>>   drivers/gpu/drm/xe/xe_pagefault_types.h |  9 +++++++
>>   3 files changed, 47 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
>> index d48f6ed103bb..6adf3bf73b1c 100644
>> --- a/drivers/gpu/drm/xe/xe_guc_pagefault.c
>> +++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
>> @@ -8,6 +8,7 @@
>>   #include "xe_guc_ct.h"
>>   #include "xe_guc_pagefault.h"
>>   #include "xe_pagefault.h"
>> +#include "xe_eudebug_pagefault.h"
>>   
>>   static void guc_ack_fault(struct xe_pagefault *pf, int err)
>>   {
>> @@ -37,8 +38,15 @@ static void guc_ack_fault(struct xe_pagefault *pf, int err)
>>   	xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
>>   }
>>   
>> +static void guc_cleanup_fault(struct xe_pagefault *pf, int err)
>> +{
>> +	xe_eudebug_pagefault_service(pf);
>> +	xe_eudebug_pagefault_destroy(pf, 0);
>> +}
>> +
>>   static const struct xe_pagefault_ops guc_pagefault_ops = {
>>   	.ack_fault = guc_ack_fault,
>> +	.cleanup_fault = guc_cleanup_fault,
>>   };
>>   
>>   /**
>> diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
>> index 72f589fd2b64..9dcd854e99f9 100644
>> --- a/drivers/gpu/drm/xe/xe_pagefault.c
>> +++ b/drivers/gpu/drm/xe/xe_pagefault.c
>> @@ -10,6 +10,7 @@
>>   
>>   #include "xe_bo.h"
>>   #include "xe_device.h"
>> +#include "xe_eudebug_pagefault.h"
>>   #include "xe_gt_printk.h"
>>   #include "xe_gt_types.h"
>>   #include "xe_gt_stats.h"
>> @@ -171,6 +172,8 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>>   	if (IS_ERR(vm))
>>   		return PTR_ERR(vm);
>>   
>> +	xe_eudebug_pagefault_create(vm, pf);
> 
> This might not get called because of the early returns above. And
> looking at xe_guc_pagefault_handler(), we don't seem to zero initialize
> the "pf" variable there.  Couldn't this cause a risk of
> xe_eudebug_pagefault_destroy() being called with pf->consumer.epf
> containing garbage?
> 
Yes, you're right. Thank you for the good point.
I'll also apply this fix to the version that stores epf in 
pf->producer.private.

G.G.

> --
> Gustavo Sousa
> 
>> +
>>   	/*
>>   	 * TODO: Change to read lock? Using write lock for simplicity.
>>   	 */
>> @@ -184,9 +187,28 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>>   	vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
>>   	if (!vma) {
>>   		err = -EINVAL;
>> -		goto unlock_vm;
>> +		vma = xe_eudebug_create_vma(vm, pf);
>> +		if (IS_ERR(vma)) {
>> +			err = PTR_ERR(vma);
>> +			vma = NULL;
>> +		}
>>   	}
>>   
>> +	if (vma) {
>> +		/*
>> +		 * When creating an instance of eudebug_pagefault, there was
>> +		 * no vma containing the ppgtt address where the pagefault occurred,
>> +		 * but when reacquiring vm->lock, there is.
>> +		 * During not aquiring the vm->lock from this context,
>> +		 * but vma corresponding to the address where the pagefault occurred
>> +		 * in another context has allocated.
>> +		 */
>> +		err = 0;
>> +	}
>> +
>> +	if (err)
>> +		goto unlock_vm;
>> +
>>   	atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
>>   
>>   	if (xe_vma_is_cpu_addr_mirror(vma))
>> @@ -198,6 +220,10 @@ static int xe_pagefault_service(struct xe_pagefault *pf)
>>   unlock_vm:
>>   	if (!err)
>>   		vm->usm.last_fault_vma = vma;
>> +
>> +	if (err)
>> +		xe_eudebug_pagefault_destroy(pf, err);
>> +
>>   	up_write(&vm->lock);
>>   	xe_vm_put(vm);
>>   
>> @@ -268,6 +294,9 @@ static void xe_pagefault_queue_work(struct work_struct *w)
>>   
>>   		pf.producer.ops->ack_fault(&pf, err);
>>   
>> +		if (pf.producer.ops->cleanup_fault)
>> +			pf.producer.ops->cleanup_fault(&pf, err);
>> +
>>   		if (time_after(jiffies, threshold)) {
>>   			queue_work(gt_to_xe(pf.gt)->usm.pf_wq, w);
>>   			break;
>> diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
>> index 2bee858da597..9d2d29d35a4b 100644
>> --- a/drivers/gpu/drm/xe/xe_pagefault_types.h
>> +++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
>> @@ -43,6 +43,15 @@ struct xe_pagefault_ops {
>>   	 * sends the result to the HW/FW interface.
>>   	 */
>>   	void (*ack_fault)(struct xe_pagefault *pf, int err);
>> +
>> +	/**
>> +	 * @cleanup_fault: Cleanup for producer, if any
>> +	 * @pf: Page fault
>> +	 * @err: Error state of fault
>> +	 *
>> +	 * Page fault producer received cleanup request from consumer
>> +	 */
>> +	void (*cleanup_fault)(struct xe_pagefault *pf, int err);
>>   };
>>   
>>   /**
>> -- 
>> 2.43.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2026-02-28  6:49 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-23 14:02 [PATCH 00/22] Intel Xe GPU Debug Support (eudebug) v7 Mika Kuoppala
2026-02-23 14:02 ` [PATCH 01/22] drm/xe/eudebug: Introduce eudebug interface Mika Kuoppala
2026-02-23 14:02 ` [PATCH 02/22] drm/xe/eudebug: Add documentation Mika Kuoppala
2026-02-23 14:02 ` [PATCH 03/22] drm/xe/eudebug: Add connection establishment documentation Mika Kuoppala
2026-02-23 14:02 ` [PATCH 04/22] drm/xe/eudebug: Introduce discovery for resources Mika Kuoppala
2026-02-23 14:03 ` [PATCH 05/22] drm/xe/eudebug: Introduce exec_queue events Mika Kuoppala
2026-02-23 14:03 ` [PATCH 06/22] drm/xe: Add EUDEBUG_ENABLE exec queue property Mika Kuoppala
2026-02-23 14:03 ` [PATCH 07/22] drm/xe/eudebug: Mark guc contexts as debuggable Mika Kuoppala
2026-02-23 14:03 ` [PATCH 08/22] drm/xe: Introduce ADD_DEBUG_DATA and REMOVE_DEBUG_DATA vm bind ops Mika Kuoppala
2026-02-23 14:03 ` [PATCH 09/22] drm/xe/eudebug: Introduce vm bind and vm bind debug data events Mika Kuoppala
2026-02-23 14:03 ` [PATCH 10/22] drm/xe/eudebug: Add UFENCE events with acks Mika Kuoppala
2026-02-23 14:03 ` [PATCH 11/22] drm/xe/eudebug: vm open/pread/pwrite Mika Kuoppala
2026-02-23 14:03 ` [PATCH 12/22] drm/xe/eudebug: userptr vm pread/pwrite Mika Kuoppala
2026-02-23 14:03 ` [PATCH 13/22] drm/xe/eudebug: hw enablement for eudebug Mika Kuoppala
2026-02-23 14:03 ` [PATCH 14/22] drm/xe/eudebug: Introduce EU control interface Mika Kuoppala
2026-02-23 14:03 ` [PATCH 15/22] drm/xe/eudebug: Introduce per device attention scan worker Mika Kuoppala
2026-02-23 14:03 ` [PATCH 16/22] drm/xe/eudebug_test: Introduce xe_eudebug wa kunit test Mika Kuoppala
2026-02-23 14:03 ` [PATCH 17/22] drm/xe: Implement SR-IOV and eudebug exclusivity Mika Kuoppala
2026-02-23 14:03 ` [PATCH 18/22] drm/xe: Add xe_client_debugfs and introduce debug_data file Mika Kuoppala
2026-02-23 14:03 ` [PATCH 19/22] drm/xe/eudebug: Add read/count/compare helper for eu attention Mika Kuoppala
2026-02-23 14:03 ` [PATCH 20/22] drm/xe/vm: Support for adding null page VMA to VM on request Mika Kuoppala
2026-02-23 14:03 ` [PATCH 21/22] drm/xe/eudebug: Introduce EU pagefault handling interface Mika Kuoppala
2026-02-23 19:08   ` Matthew Brost
2026-02-27 22:10     ` Gwan-gyeong Mun
2026-02-28  0:36       ` Matthew Brost
2026-02-23 14:03 ` [PATCH 22/22] drm/xe/eudebug: Enable EU pagefault handling Mika Kuoppala
2026-02-23 18:41   ` Matthew Brost
2026-02-27 22:11     ` Gwan-gyeong Mun
2026-02-27 23:11   ` Gustavo Sousa
2026-02-28  6:49     ` Gwan-gyeong Mun
2026-02-23 15:14 ` ✗ CI.checkpatch: warning for Intel Xe GPU Debug Support (eudebug) v7 Patchwork
2026-02-23 15:16 ` ✓ CI.KUnit: success " Patchwork
2026-02-23 15:31 ` ✗ CI.checksparse: warning " Patchwork
2026-02-23 15:51 ` ✓ Xe.CI.BAT: success " Patchwork
2026-02-24  8:42 ` ✗ Xe.CI.FULL: failure " Patchwork

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox